Vor kurzem habe ich angefangen, verschiedene Dinge mit Spring Web Flux zu machen. Mit Spring WebFlux
@GetMapping
public Flux<Hoge> all() {
return Flux.create(sink -> {
// ...
})
}
Ich schreibe eine Beschreibung wie folgt in den Controller, aber wie erhalte ich diese im Browser? Ich habe ein wenig recherchiert und es versucht, also werde ich es schreiben.
Dieser Artikel wird sehr hilfreich sein. BLOG.IK.AM - First Spring WebFlux (Teil 1 - Try Spring WebFlux)
Es scheint, dass es im Format "Inhaltstyp: Text / Ereignis-Stream" ("Vom Server gesendetes Ereignis") oder im Format "Inhaltstyp: Anwendung / Stream + JSON" zurückgegeben wird.
Jeder scheint den Körper im folgenden Format zurückzugeben.
Content-Type: text/event-stream
data:{"key":"value" ... }
data:{"key":"value" ... }
data:{"key":"value" ... }
Content-Type: application/stream+json
{"key":"value" ... }
{"key":"value" ... }
{"key":"value" ... }
Wenn "Vom Server gesendetes Ereignis" gegoogelt wird, wird dies als Verwenden von vom Server gesendeten Ereignissen | MDN EventSource erläutert. Mir wurde schnell klar, dass ich es verwenden würde, aber was ist mit application / stream + json
?
application / stream + json
über Fetch APIMit der Fetch API können Sie anscheinend wie folgt darauf zugreifen.
const url = "..."; //URL anzufordern
const callback = json => { /* ...Logik zum Verarbeiten jeder Zeile JSON*/ };
const decoder = new TextDecoder();
const abortController = new AbortController();
const { signal } = abortController;
//Starten Sie den Abruf
fetch(url, {
signal,
headers: {
Accept: "application/stream+json"
}
}).then(response => {
let buffer = "";
/**
*Verarbeiten Sie den aus dem Stream gelesenen Teilstring
* @param {string}chunk String lesen
* @returns {void}
*/
function consumeChunk(chunk) {
buffer += chunk;
//Aufteilen nach Zeilenvorschubcode und Abrufen jeder JSON-Zeile
// https://en.wikipedia.org/wiki/JSON_streaming#Line-delimited_JSON
// http://jsonlines.org/
const re = /(.*?)(\r\n|\r|\n)/g;
let result;
let lastIndex = 0;
while ((result = re.exec(buffer))) {
const data = result[1];
if (data.trim()) {
callback(JSON.parse(data)); //Übergeben Sie JSON an den Rückruf, um es zu verarbeiten.
}
({ lastIndex } = re);
}
//Wenn der Zeilenvorschubcode nicht vorhanden ist, wird der Rest der Zeichenfolge mit dem nächsten Lesevorgang zur Verarbeitung kombiniert.
buffer = buffer.slice(lastIndex);
}
//Erstellen Sie einen Stream-Reader
const reader = response.body.getReader();
//Prozesskörper lesen
function readNext() {
return reader.read().then(({ done, value }) => {
if (!done) {
// --Lesevorgang--
consumeChunk(decoder.decode(value));
return readNext();
}
// --Verarbeitung beenden--
if (buffer.trim()) {
//Wenn am Ende noch eine Zeichenfolge vorhanden ist, wird diese verarbeitet.
//Wenn nach den letzten Zeilendaten kein Zeilenvorschubcode vorhanden ist, gelangen Sie hierher.
//Es scheint nicht in Spring WebFlux zu passieren, aber http://jsonlines.org/Beim Betrachten hatte ich den Eindruck, dass der letzte Zeilenvorschubcode möglicherweise nicht verfügbar ist, daher werde ich ihn für alle Fälle implementieren.
// `consumeChunk`Erkennt die Leitung nur, wenn Sie den Zeilenvorschubcode übergeben. Übergeben Sie daher den Zeilenvorschubcode.
consumeChunk("\n");
}
return response;
});
}
return readNext();
});
}
Ich werde unten erklären, was ich tue.
Dies hat nichts mit dem Abrufen von Flux zu tun, ist jedoch erforderlich, wenn Sie das Abrufen in der Mitte abbrechen möchten.
const abortController = new AbortController();
const { signal } = abortController;
//Starten Sie den Abruf
fetch(url, {
signal,
headers: {
Accept: "application/stream+json"
}
})
Wenn Sie "Signal" an die Option "Abrufen" übergeben.
abortController.abort()
Dann können Sie fetch
in der Mitte stoppen.
Zu diesem Zeitpunkt scheint es noch besser zu sein, wenn die Java Controller-Seite den Prozess auch unterbricht, wenn er abgebrochen wird.
@GetMapping
public Flux<Hoge> all() {
return Flux.create(sink -> {
// ...
if (sink.isCancelled()) {
//Da es abgebrochen wurde, unterbrechen wir den Vorgang.
}
// ...
})
}
Sie können auch "onCancel" verwenden.
Siehe ReadableStream.getReader () | MDN.
Bei Verwendung von ReadableStream kann die Antwort von Stream wie folgt empfangen werden.
fetch(...).then(response => {
// ...
//Erstellen Sie einen Stream-Reader
const reader = response.body.getReader();
function readNext() {
return reader.read().then(({ done, value }) => {
if (!done) {
/*Wert handhaben*/
// ...
return readNext();
}
return; /*Ende*/
});
}
return readNext();
})
value
Der von ReadableStream erhaltene Wert ist Uint8Array. Es scheint in docs / Web / JavaScript / Reference / Global_Objects / Uint8Array zu kommen. Verwenden Sie daher TextDecoder, um es zu dekodieren.
const decoder = new TextDecoder();
// ...
const sValue = decoder.decode(value) //Dekodiere Uint8Array und konvertiere es in einen String
Bis zu diesem Punkt haben Sie den Punkt erreicht, an dem Sie den Inhalt von Stream als Zeichenfolge abrufen können. Hier werden wir den JSON tatsächlich analysieren.
let buffer = "";
/**
*Verarbeiten Sie den aus dem Stream gelesenen Teilstring
* @param {string}chunk String lesen
* @returns {void}
*/
function consumeChunk(chunk) {
buffer += chunk;
//Aufteilen nach Zeilenvorschubcode und Abrufen jeder JSON-Zeile
// https://en.wikipedia.org/wiki/JSON_streaming#Line-delimited_JSON
// http://jsonlines.org/
const re = /(.*?)(\r\n|\r|\n)/g;
let result;
let lastIndex = 0;
while ((result = re.exec(buffer))) {
const data = result[1];
if (data.trim()) {
callback(JSON.parse(data)); //Übergeben Sie JSON an den Rückruf, um es zu verarbeiten.
}
({ lastIndex } = re);
}
//Wenn der Zeilenvorschubcode nicht vorhanden ist, wird der Rest der Zeichenfolge mit dem nächsten Lesevorgang zur Verarbeitung kombiniert.
buffer = buffer.slice(lastIndex);
}
Ich mache etwas, aber ich werde es später erklären.
application / stream + json
gibt die folgende Antwort wie oben beschrieben zurück.
{"key":"value" ... }
{"key":"value" ... }
{"key":"value" ... }
Diese Antwort ist ein JSON, der durch einen Zeilenvorschubcode getrennt ist. Daher ist jede Zeile JSON "JSON.parse", getrennt durch einen Zeilenvorschubcode.
Die mit "read ()" von ReadableStream erhaltene Zeichenfolge ist kein vollständiger JSON. Es ist nur ein Teil der gesamten Antwort.
Wenn der Block keinen Zeilenvorschubcode enthält, wird er daher zur Verarbeitung mit dem nächsten Block kombiniert.
Das ist alles für die Erklärung.
Die Methode, die EventSource verwendet, kann möglicherweise keine Header-Informationen übergeben und erfordert je nach einzubettender Anwendung möglicherweise Einfallsreichtum oder Änderungen. (Zum Beispiel bei der Verarbeitung auf der Serverseite unter der Annahme, dass das Authentifizierungstoken im Header platziert ist.)
Diese Methode zur Verwendung von Fetch API entspricht jedoch fast dem normalen HTTP-Zugriff, sodass ich den Eindruck hatte, dass sie problemlos verwendet werden kann (wenn der Browser Fetch API unterstützt).
Recommended Posts