@@ -267,20 +267,24 @@ else if (Rendering.class.isAssignableFrom(clazz)) {
267
267
Mono .just (Collections .singletonList ((View ) view )));
268
268
}
269
269
else if (FragmentsRendering .class .isAssignableFrom (clazz )) {
270
+ ServerHttpResponse response = exchange .getResponse ();
270
271
FragmentsRendering render = (FragmentsRendering ) returnValue ;
271
272
HttpStatusCode status = render .status ();
272
273
if (status != null ) {
273
- exchange . getResponse () .setStatusCode (status );
274
+ response .setStatusCode (status );
274
275
}
275
- exchange . getResponse () .getHeaders ().putAll (render .headers ());
276
+ response .getHeaders ().putAll (render .headers ());
276
277
bindingContext .updateModel (exchange );
277
278
278
279
StreamHandler streamHandler = getStreamHandler (exchange );
280
+ if (streamHandler != null ) {
281
+ streamHandler .updateResponse (exchange );
282
+ }
279
283
280
284
Flux <Flux <DataBuffer >> renderFlux = render .fragments ().concatMap (fragment ->
281
285
renderFragment (fragment , streamHandler , locale , bindingContext , exchange ));
282
286
283
- return exchange . getResponse () .writeAndFlushWith (renderFlux );
287
+ return response .writeAndFlushWith (renderFlux );
284
288
}
285
289
else if (Model .class .isAssignableFrom (clazz )) {
286
290
model .addAllAttributes (((Model ) returnValue ).asMap ());
@@ -299,7 +303,7 @@ else if (View.class.isAssignableFrom(clazz)) {
299
303
viewsMono = resolveViews (getDefaultViewName (exchange ), locale );
300
304
}
301
305
bindingContext .updateModel (exchange );
302
- return viewsMono .flatMap (views -> render (views , model .asMap (), bindingContext , exchange ));
306
+ return viewsMono .flatMap (views -> render (views , model .asMap (), null , bindingContext , exchange ));
303
307
});
304
308
}
305
309
@@ -346,10 +350,16 @@ private Mono<Flux<DataBuffer>> renderFragment(
346
350
Mono .just (List .of (fragment .view ())) :
347
351
resolveViews (fragment .viewName () != null ? fragment .viewName () : getDefaultViewName (exchange ), locale ));
348
352
349
- return selectedViews .flatMap (views -> render (views , fragment .model (), bindingContext , mutatedExchange ))
350
- .then (Mono .fromSupplier (() -> (streamHandler != null ?
351
- streamHandler .format (response .getBodyFlux (), fragment , exchange ) :
352
- response .getBodyFlux ())));
353
+ Map <String , Object > model = fragment .model ();
354
+
355
+ if (streamHandler != null ) {
356
+ return selectedViews .flatMap (views -> render (views , model , MediaType .TEXT_HTML , bindingContext , mutatedExchange ))
357
+ .then (Mono .fromSupplier (() -> streamHandler .format (response .getBodyFlux (), fragment , exchange )));
358
+ }
359
+ else {
360
+ return selectedViews .flatMap (views -> render (views , model , null , bindingContext , mutatedExchange ))
361
+ .then (Mono .fromSupplier (response ::getBodyFlux ));
362
+ }
353
363
}
354
364
355
365
@ Nullable
@@ -369,7 +379,8 @@ private String getNameForReturnValue(MethodParameter returnType) {
369
379
.orElseGet (() -> Conventions .getVariableNameForParameter (returnType ));
370
380
}
371
381
372
- private Mono <? extends Void > render (List <View > views , Map <String , Object > model ,
382
+ private Mono <? extends Void > render (
383
+ List <View > views , Map <String , Object > model , @ Nullable MediaType bestMediaType ,
373
384
BindingContext bindingContext , ServerWebExchange exchange ) {
374
385
375
386
for (View view : views ) {
@@ -378,19 +389,20 @@ private Mono<? extends Void> render(List<View> views, Map<String, Object> model,
378
389
}
379
390
}
380
391
List <MediaType > mediaTypes = getMediaTypes (views );
381
- MediaType bestMediaType ;
382
- try {
383
- bestMediaType = selectMediaType (exchange , () -> mediaTypes );
384
- }
385
- catch (NotAcceptableStatusException ex ) {
386
- HttpStatusCode statusCode = exchange .getResponse ().getStatusCode ();
387
- if (statusCode != null && statusCode .isError ()) {
388
- if (logger .isDebugEnabled ()) {
389
- logger .debug ("Ignoring error response content (if any). " + ex .getReason ());
392
+ if (bestMediaType == null ) {
393
+ try {
394
+ bestMediaType = selectMediaType (exchange , () -> mediaTypes );
395
+ }
396
+ catch (NotAcceptableStatusException ex ) {
397
+ HttpStatusCode statusCode = exchange .getResponse ().getStatusCode ();
398
+ if (statusCode != null && statusCode .isError ()) {
399
+ if (logger .isDebugEnabled ()) {
400
+ logger .debug ("Ignoring error response content (if any). " + ex .getReason ());
401
+ }
402
+ return Mono .empty ();
390
403
}
391
- return Mono . empty () ;
404
+ throw ex ;
392
405
}
393
- throw ex ;
394
406
}
395
407
if (bestMediaType != null ) {
396
408
for (View view : views ) {
@@ -427,15 +439,23 @@ private static class BodySavingResponse extends ServerHttpResponseDecorator {
427
439
@ Nullable
428
440
private Flux <DataBuffer > bodyFlux ;
429
441
430
- private final HttpHeaders headers ;
442
+ @ Nullable
443
+ private HttpHeaders headers ;
431
444
432
445
BodySavingResponse (ServerHttpResponse delegate ) {
433
446
super (delegate );
434
- this .headers = new HttpHeaders (delegate .getHeaders ()); // Ignore header changes
435
447
}
436
448
437
449
@ Override
438
450
public HttpHeaders getHeaders () {
451
+ if (!super .getHeaders ().containsKey (HttpHeaders .CONTENT_TYPE )) {
452
+ return super .getHeaders ();
453
+ }
454
+ // Content-type is set, ignore further updates
455
+ if (this .headers == null ) {
456
+ this .headers = new HttpHeaders ();
457
+ this .headers .putAll (super .getHeaders ());
458
+ }
439
459
return this .headers ;
440
460
}
441
461
@@ -468,6 +488,11 @@ private interface StreamHandler {
468
488
*/
469
489
boolean supports (ServerHttpRequest request );
470
490
491
+ /**
492
+ * Update the response before streaming, e.g. to set the content-type.
493
+ */
494
+ void updateResponse (ServerWebExchange exchange );
495
+
471
496
/**
472
497
* Format the given fragment.
473
498
* @param fragmentContent the fragment serialized to data buffers
@@ -476,7 +501,6 @@ private interface StreamHandler {
476
501
* @return the formatted fragment
477
502
*/
478
503
Flux <DataBuffer > format (Flux <DataBuffer > fragmentContent , Fragment fragment , ServerWebExchange exchange );
479
-
480
504
}
481
505
482
506
@@ -492,20 +516,14 @@ public boolean supports(ServerHttpRequest request) {
492
516
}
493
517
494
518
@ Override
495
- public Flux <DataBuffer > format (
496
- Flux <DataBuffer > fragmentContent , Fragment fragment , ServerWebExchange exchange ) {
497
-
519
+ public void updateResponse (ServerWebExchange exchange ) {
520
+ MediaType mediaType = MediaType .TEXT_EVENT_STREAM ;
498
521
Charset charset = getCharset (exchange .getRequest ());
499
- DataBufferFactory bufferFactory = exchange .getResponse ().bufferFactory ();
500
-
501
- String eventLine = fragment .viewName () != null ? "event:" + fragment .viewName () + "\n " : "" ;
502
-
503
- return Flux .concat (
504
- Flux .just (encodeText (eventLine + "data:" , charset , bufferFactory )),
505
- fragmentContent ,
506
- Flux .just (encodeText ("\n \n " , charset , bufferFactory )));
522
+ mediaType = (charset != null ? new MediaType (mediaType , charset ) : mediaType );
523
+ exchange .getResponse ().getHeaders ().setContentType (mediaType );
507
524
}
508
525
526
+ @ Nullable
509
527
private Charset getCharset (ServerHttpRequest request ) {
510
528
for (MediaType mediaType : request .getHeaders ().getAccept ()) {
511
529
if (mediaType .isCompatibleWith (MediaType .TEXT_EVENT_STREAM )) {
@@ -515,7 +533,26 @@ private Charset getCharset(ServerHttpRequest request) {
515
533
break ;
516
534
}
517
535
}
518
- return StandardCharsets .UTF_8 ;
536
+ return null ;
537
+ }
538
+
539
+ @ Override
540
+ public Flux <DataBuffer > format (
541
+ Flux <DataBuffer > fragmentContent , Fragment fragment , ServerWebExchange exchange ) {
542
+
543
+ Charset charset = StandardCharsets .UTF_8 ;
544
+ MediaType contentType = exchange .getResponse ().getHeaders ().getContentType ();
545
+ if (contentType != null && contentType .getCharset () != null ) {
546
+ charset = contentType .getCharset ();
547
+ }
548
+
549
+ DataBufferFactory bufferFactory = exchange .getResponse ().bufferFactory ();
550
+
551
+ String eventLine = fragment .viewName () != null ? "event:" + fragment .viewName () + "\n " : "" ;
552
+ DataBuffer prefix = encodeText (eventLine + "data:" , charset , bufferFactory );
553
+ DataBuffer suffix = encodeText ("\n \n " , charset , bufferFactory );
554
+
555
+ return Flux .concat (Flux .just (prefix ), fragmentContent , Flux .just (suffix ));
519
556
}
520
557
521
558
private DataBuffer encodeText (String text , Charset charset , DataBufferFactory bufferFactory ) {
0 commit comments