Récemment, j'ai commencé à créer diverses choses en utilisant Spring Web Flux. Avec Spring WebFlux,
@GetMapping
public Flux<Hoge> all() {
return Flux.create(sink -> {
// ...
})
}
J'écris une description dans le contrôleur comme celle-ci, mais comment puis-je la recevoir dans le navigateur? J'ai fait un peu de recherche et l'ai essayé, donc je vais l'écrire.
Cet article sera très utile. BLOG.IK.AM --First Spring WebFlux (Partie 1 - Essayez Spring WebFlux)
Il semble qu'il sera retourné au format Content-Type: text / event-stream
( Server-Sent Event
) ou au format Content-Type: application / stream + json
.
Chacun semble renvoyer le corps dans le format suivant.
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" ... }
Lorsque Evénement envoyé par le serveur 'est recherché sur Google, il est expliqué comme [Utilisation des événements envoyés par le serveur | MDN](https://developer.mozilla.org/ja/docs/Server-sent_events/Using_server-sent_events) [Source de l'événement] Je me suis vite rendu compte que j'allais l'utiliser, mais qu'en est-il de ʻapplication / stream + json
?
En utilisant Fetch API, il semble que vous puissiez y accéder comme suit.
const url = "..."; //URL à demander
const callback = json => { /* ...Logique pour traiter chaque ligne JSON*/ };
const decoder = new TextDecoder();
const abortController = new AbortController();
const { signal } = abortController;
//commencer à chercher
fetch(url, {
signal,
headers: {
Accept: "application/stream+json"
}
}).then(response => {
let buffer = "";
/**
*Traiter la sous-chaîne lue dans le flux
* @param {string}chunk Lire la chaîne
* @returns {void}
*/
function consumeChunk(chunk) {
buffer += chunk;
//Diviser par code de saut de ligne et récupérer chaque ligne JSON
// 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)); //Passez JSON au rappel à traiter.
}
({ lastIndex } = re);
}
//Si le code de saut de ligne n'existe pas, le reste de la chaîne sera combiné avec la prochaine lecture pour traitement.
buffer = buffer.slice(lastIndex);
}
//Créer un lecteur de flux
const reader = response.body.getReader();
//Corps du processus de lecture
function readNext() {
return reader.read().then(({ done, value }) => {
if (!done) {
// --Processus de lecture--
consumeChunk(decoder.decode(value));
return readNext();
}
// --Terminer le traitement--
if (buffer.trim()) {
//S'il reste une chaîne de caractères à la fin, elle sera traitée.
//S'il n'y a pas de code de saut de ligne après les dernières données de ligne, vous atteindrez ici.
//Cela ne semble pas se produire dans Spring WebFlux, mais http://jsonlines.org/En regardant, j'ai eu l'impression que le dernier code de saut de ligne n'est peut-être pas disponible, donc je vais l'implémenter au cas où.
// `consumeChunk`Ne reconnaît pas la ligne à moins que vous ne transmettiez le code de saut de ligne, alors transmettez le code de saut de ligne.
consumeChunk("\n");
}
return response;
});
}
return readNext();
});
}
J'expliquerai ce que je fais ci-dessous.
Cela n'a rien à voir avec la récupération de Flux, mais c'est nécessaire si vous voulez annuler fetch
au milieu.
const abortController = new AbortController();
const { signal } = abortController;
//commencer à chercher
fetch(url, {
signal,
headers: {
Accept: "application/stream+json"
}
})
Si vous passez signal
à l'option fetch
.
abortController.abort()
Ensuite, vous pouvez arrêter fetch
au milieu.
À ce stade, il semble encore mieux si le côté contrôleur Java interrompt également le processus s'il est annulé.
@GetMapping
public Flux<Hoge> all() {
return Flux.create(sink -> {
// ...
if (sink.isCancelled()) {
//Puisqu'il a été annulé, interrompons le processus.
}
// ...
})
}
Vous pouvez également utiliser ʻon Cancel`.
réponse
dans StreamReportez-vous à ReadableStream.getReader () | MDN.
En utilisant ReadableStream, il semble que la réponse puisse être reçue par Stream comme suit.
fetch(...).then(response => {
// ...
//Créer un lecteur de flux
const reader = response.body.getReader();
function readNext() {
return reader.read().then(({ done, value }) => {
if (!done) {
/*Valeur de poignée*/
// ...
return readNext();
}
return; /*Fin*/
});
}
return readNext();
})
valeur
La valeur
obtenue de ReadableStream est [Uint8Array](https://developer.mozilla.org/en/ Il semble venir dans docs / Web / JavaScript / Reference / Global_Objects / Uint8Array), alors décodez-le en utilisant TextDecoder.
const decoder = new TextDecoder();
// ...
const sValue = decoder.decode(value) //Décodez Uint8Array et convertissez-le en chaîne
chunk
Jusqu'à présent, vous avez atteint le point où vous pouvez obtenir le contenu de Stream sous forme de chaîne de caractères. Ici, nous allons analyser le JSON.
let buffer = "";
/**
*Traiter la sous-chaîne lue dans le flux
* @param {string}chunk Lire la chaîne
* @returns {void}
*/
function consumeChunk(chunk) {
buffer += chunk;
//Diviser par code de saut de ligne et récupérer chaque ligne JSON
// 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)); //Passez JSON au rappel à traiter.
}
({ lastIndex } = re);
}
//Si le code de saut de ligne n'existe pas, le reste de la chaîne sera combiné avec la prochaine lecture pour traitement.
buffer = buffer.slice(lastIndex);
}
Je fais quelque chose, mais je l'expliquerai plus tard.
ʻApplication / stream + json` renvoie la réponse suivante comme décrit ci-dessus.
{"key":"value" ... }
{"key":"value" ... }
{"key":"value" ... }
Cette réponse est un JSON séparé par un code de saut de ligne.
Ainsi, chaque ligne JSON est JSON.parse
séparée par un code de saut de ligne.
La chaîne obtenue avec read ()
de ReadableStream n'est pas un JSON complet. Ce n'est qu'une partie de la réponse globale.
Par conséquent, si le bloc ne contient pas de code de saut de ligne, il est combiné avec le bloc suivant pour traitement.
C'est tout pour l'explication.
La méthode utilisant EventSource peut ne pas être en mesure de transmettre les informations d'en-tête et peut nécessiter une certaine ingéniosité ou des modifications en fonction de l'application à incorporer. (Par exemple, lors du traitement côté serveur en supposant que le jeton d'authentification est placé dans l'en-tête.)
Cependant, cette méthode d'utilisation de Fetch API est presque la même que l'accès HTTP normal, j'ai donc eu l'impression qu'elle pouvait être utilisée facilement (si le navigateur supportait Fetch API).
Recommended Posts