'Returning JSON data as a stream per chunk to Angular2 or jQuery over HTTP2 (HTTPS)
In one of my API's I mostly return a result (let's say paged 50 results) as one whole in an json
array like so:
[{},{},{},{},{},...]
I was wondering if there are better ways of doing this over HTTP2 (as it has many new partial streaming features) with Go's HTTP server (using HTTPS in Gin for this project).
Maybe I could chunk every {}
result and send them as segments on a stream? How would the AJAX call in Angular or jQuery know that there's a new chunk delivered (newline or some character marker?)? And what call in the library could actually handle such a multi-promise (does that even exist? :P)? Could I benefit from the HTTP2 stream-features in some way to prevent multiple-connections from opening?
I'm kind of aiming to have the results nicely plop into the list as they come in.
UPDATE
Maybe it's easier to use Keep-Alive
header in some way to let's keep the connection open for for a certain amount of seconds to be able to stream over and fire many smaller requests/responses?
Solution 1:[1]
As I see, application/x-ndjson
could help you.
Here're an example that uses standard net/http
package. You could port to GIN also.
package main
import (
"encoding/json"
"fmt"
"net/http"
"time"
)
// let's generate some fake data
func sampleStream() <-chan []byte {
out := make(chan []byte, 10)
go func() {
for i := 0; i < 10; i++ {
d := map[string]interface{}{"hello": i}
buf, _ := json.Marshal(d)
out <- buf
time.Sleep(200 * time.Millisecond)
}
close(out)
}()
return out
}
func streamJSON(w http.ResponseWriter, r *http.Request) {
flusher, ok := w.(http.Flusher)
if !ok {
w.WriteHeader(500)
w.Write([]byte("Server unsupport flush"))
return
}
// proper handle cors !!!
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Content-Type", "application/x-ndjson")
w.Header().Set("Connection", "Keep-Alive")
w.Header().Set("X-Content-Type-Options", "nosniff")
for buf := range sampleStream() {
w.Write(buf)
w.Write([]byte("\n"))
flusher.Flush()
}
}
func main() {
http.HandleFunc("/", streamJSON)
s := &http.Server{
Addr: ":8080",
}
fmt.Println("Listen :8080")
if err := s.ListenAndServe(); err != nil {
panic(err)
}
}
And simple JS:
const abortController = new AbortController();
async function main() {
const res = await fetch("http://localhost:8080", {
signal: abortController.signal,
});
const reader = res.body.getReader();
const textDecoder = new TextDecoder();
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
const text = textDecoder.decode(value);
const json = JSON.parse(text);
console.log(json);
}
} finally {
reader.releaseLock();
}
}
Solution 2:[2]
Lookup Server Sent Events. It's essentially chunked response for which the browser already has built-in support.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | hienduyph |
Solution 2 |