17
17
package org .springframework .web .servlet .function ;
18
18
19
19
import java .io .IOException ;
20
+ import java .lang .reflect .Type ;
20
21
import java .net .URI ;
21
22
import java .time .Instant ;
22
23
import java .time .ZonedDateTime ;
39
40
import org .reactivestreams .Subscriber ;
40
41
import org .reactivestreams .Subscription ;
41
42
43
+ import org .springframework .core .ParameterizedTypeReference ;
42
44
import org .springframework .http .CacheControl ;
43
45
import org .springframework .http .HttpHeaders ;
44
46
import org .springframework .http .HttpMethod ;
45
47
import org .springframework .http .HttpStatus ;
46
48
import org .springframework .http .InvalidMediaTypeException ;
47
49
import org .springframework .http .MediaType ;
50
+ import org .springframework .http .converter .GenericHttpMessageConverter ;
48
51
import org .springframework .http .converter .HttpMessageConverter ;
49
52
import org .springframework .http .server .ServletServerHttpResponse ;
50
53
import org .springframework .lang .Nullable ;
@@ -64,7 +67,7 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
64
67
65
68
private final T entity ;
66
69
67
- private final BuilderFunction < T > builderFunction ;
70
+ private final Type entityType ;
68
71
69
72
private int status = HttpStatus .OK .value ();
70
73
@@ -73,9 +76,9 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
73
76
private final MultiValueMap <String , Cookie > cookies = new LinkedMultiValueMap <>();
74
77
75
78
76
- private DefaultEntityResponseBuilder (T entity , BuilderFunction < T > builderFunction ) {
79
+ private DefaultEntityResponseBuilder (T entity , @ Nullable Type entityType ) {
77
80
this .entity = entity ;
78
- this .builderFunction = builderFunction ;
81
+ this .entityType = ( entityType != null ) ? entityType : entity . getClass () ;
79
82
}
80
83
81
84
@ Override
@@ -185,52 +188,40 @@ public EntityResponse.Builder<T> varyBy(String... requestHeaders) {
185
188
return this ;
186
189
}
187
190
191
+ @ SuppressWarnings ({"rawtypes" , "unchecked" })
188
192
@ Override
189
193
public EntityResponse <T > build () {
190
- return this .builderFunction .build (this .status , this .headers , this .cookies , this .entity );
194
+ if (this .entity instanceof CompletionStage ) {
195
+ CompletionStage completionStage = (CompletionStage ) this .entity ;
196
+ return new CompletionStageEntityResponse (this .status , this .headers , this .cookies ,
197
+ completionStage , this .entityType );
198
+ }
199
+ else if (this .entity instanceof Publisher ) {
200
+ Publisher publisher = (Publisher ) this .entity ;
201
+ return new PublisherEntityResponse (this .status , this .headers , this .cookies , publisher ,
202
+ this .entityType );
203
+ }
204
+ else {
205
+ return new DefaultEntityResponse <>(this .status , this .headers , this .cookies , this .entity ,
206
+ this .entityType );
207
+ }
191
208
}
192
209
193
210
194
211
/**
195
212
* Return a new {@link EntityResponse.Builder} from the given object.
196
213
*/
197
214
public static <T > EntityResponse .Builder <T > fromObject (T t ) {
198
- return new DefaultEntityResponseBuilder <>(t , DefaultEntityResponse ::new );
199
- }
200
-
201
- /**
202
- * Return a new {@link EntityResponse.Builder} from the given completion stage.
203
- */
204
- public static <T > EntityResponse .Builder <CompletionStage <T >> fromCompletionStage (
205
- CompletionStage <T > completionStage ) {
206
- return new DefaultEntityResponseBuilder <>(completionStage ,
207
- CompletionStageEntityResponse ::new );
215
+ return new DefaultEntityResponseBuilder <>(t , null );
208
216
}
209
217
210
218
/**
211
- * Return a new {@link EntityResponse.Builder} from the given Reactive Streams publisher .
219
+ * Return a new {@link EntityResponse.Builder} from the given object and type reference .
212
220
*/
213
- public static <T > EntityResponse .Builder <Publisher < T >> fromPublisher ( Publisher < T > publisher ) {
214
- return new DefaultEntityResponseBuilder <>(publisher , PublisherEntityResponse :: new );
221
+ public static <T > EntityResponse .Builder <T > fromObject ( T t , ParameterizedTypeReference <?> bodyType ) {
222
+ return new DefaultEntityResponseBuilder <>(t , bodyType . getType () );
215
223
}
216
224
217
- @ SuppressWarnings ("unchecked" )
218
- private static <T > HttpMessageConverter <T > cast (HttpMessageConverter <?> messageConverter ) {
219
- return (HttpMessageConverter <T >) messageConverter ;
220
- }
221
-
222
-
223
- /**
224
- * Defines contract for building {@link EntityResponse} instances.
225
- */
226
- private interface BuilderFunction <T > {
227
-
228
- EntityResponse <T > build (int statusCode , HttpHeaders headers ,
229
- MultiValueMap <String , Cookie > cookies , T entity );
230
-
231
- }
232
-
233
-
234
225
/**
235
226
* Default {@link EntityResponse} implementation for synchronous bodies.
236
227
*/
@@ -240,12 +231,15 @@ private static class DefaultEntityResponse<T>
240
231
241
232
private final T entity ;
242
233
234
+ private final Type entityType ;
235
+
243
236
244
237
public DefaultEntityResponse (int statusCode , HttpHeaders headers ,
245
- MultiValueMap <String , Cookie > cookies , T entity ) {
238
+ MultiValueMap <String , Cookie > cookies , T entity , Type entityType ) {
246
239
247
240
super (statusCode , headers , cookies );
248
241
this .entity = entity ;
242
+ this .entityType = entityType ;
249
243
}
250
244
251
245
@ Override
@@ -258,11 +252,12 @@ protected ModelAndView writeToInternal(HttpServletRequest servletRequest,
258
252
HttpServletResponse servletResponse , Context context )
259
253
throws ServletException , IOException {
260
254
261
- writeEntityWithMessageConverters (this .entity , servletRequest , servletResponse , context );
255
+ writeEntityWithMessageConverters (this .entity , servletRequest ,servletResponse , context );
262
256
263
257
return null ;
264
258
}
265
259
260
+ @ SuppressWarnings ("unchecked" )
266
261
protected void writeEntityWithMessageConverters (Object entity ,
267
262
HttpServletRequest request , HttpServletResponse response ,
268
263
ServerResponse .Context context )
@@ -271,30 +266,39 @@ protected void writeEntityWithMessageConverters(Object entity,
271
266
ServletServerHttpResponse serverResponse = new ServletServerHttpResponse (response );
272
267
273
268
MediaType contentType = getContentType (response );
274
- Class <?> entityType = entity .getClass ();
275
-
276
- HttpMessageConverter <Object > messageConverter = context .messageConverters ().stream ()
277
- .filter (converter -> converter .canWrite (entityType , contentType ))
278
- .findFirst ()
279
- .map (DefaultEntityResponseBuilder ::cast )
280
- .orElseThrow (() -> new HttpMediaTypeNotAcceptableException (
281
- producibleMediaTypes (context .messageConverters (), entityType )));
269
+ Class <?> entityClass = entity .getClass ();
270
+
271
+ for (HttpMessageConverter <?> messageConverter : context .messageConverters ()) {
272
+ if (messageConverter instanceof GenericHttpMessageConverter <?>) {
273
+ GenericHttpMessageConverter <Object > genericMessageConverter =
274
+ (GenericHttpMessageConverter <Object >) messageConverter ;
275
+ if (genericMessageConverter .canWrite (this .entityType , entityClass , contentType )) {
276
+ genericMessageConverter .write (entity , this .entityType , contentType , serverResponse );
277
+ return ;
278
+ }
279
+ }
280
+ if (messageConverter .canWrite (entityClass , contentType )) {
281
+ ((HttpMessageConverter <Object >)messageConverter ).write (entity , contentType , serverResponse );
282
+ return ;
283
+ }
284
+ }
282
285
283
- messageConverter .write (entity , contentType , serverResponse );
286
+ List <MediaType > producibleMediaTypes = producibleMediaTypes (context .messageConverters (), entityClass );
287
+ throw new HttpMediaTypeNotAcceptableException (producibleMediaTypes );
284
288
}
285
289
286
290
@ Nullable
287
- private MediaType getContentType (HttpServletResponse response ) {
291
+ private static MediaType getContentType (HttpServletResponse response ) {
288
292
try {
289
- return MediaType .parseMediaType (response .getContentType ());
293
+ return MediaType .parseMediaType (response .getContentType ()). removeQualityValue () ;
290
294
}
291
295
catch (InvalidMediaTypeException ex ) {
292
296
return null ;
293
297
}
294
298
}
295
299
296
- protected final void tryWriteEntityWithMessageConverters (Object entity ,
297
- HttpServletRequest request , HttpServletResponse response ,
300
+ protected void tryWriteEntityWithMessageConverters (Object entity ,
301
+ HttpServletRequest request , HttpServletResponse response ,
298
302
ServerResponse .Context context ) {
299
303
try {
300
304
writeEntityWithMessageConverters (entity , request , response , context );
@@ -323,10 +327,10 @@ private static List<MediaType> producibleMediaTypes(
323
327
private static class CompletionStageEntityResponse <T >
324
328
extends DefaultEntityResponse <CompletionStage <T >> {
325
329
326
- public CompletionStageEntityResponse (int statusCode ,
327
- HttpHeaders headers ,
328
- MultiValueMap < String , Cookie > cookies , CompletionStage < T > entity ) {
329
- super (statusCode , headers , cookies , entity );
330
+ public CompletionStageEntityResponse (int statusCode , HttpHeaders headers ,
331
+ MultiValueMap < String , Cookie > cookies , CompletionStage < T > entity ,
332
+ Type entityType ) {
333
+ super (statusCode , headers , cookies , entity , entityType );
330
334
}
331
335
332
336
@ Override
@@ -338,6 +342,7 @@ protected ModelAndView writeToInternal(HttpServletRequest servletRequest,
338
342
entity ().whenComplete ((entity , throwable ) -> {
339
343
try {
340
344
if (entity != null ) {
345
+
341
346
tryWriteEntityWithMessageConverters (entity ,
342
347
(HttpServletRequest ) asyncContext .getRequest (),
343
348
(HttpServletResponse ) asyncContext .getResponse (),
@@ -358,8 +363,9 @@ else if (throwable != null) {
358
363
private static class PublisherEntityResponse <T > extends DefaultEntityResponse <Publisher <T >> {
359
364
360
365
public PublisherEntityResponse (int statusCode , HttpHeaders headers ,
361
- MultiValueMap <String , Cookie > cookies , Publisher <T > entity ) {
362
- super (statusCode , headers , cookies , entity );
366
+ MultiValueMap <String , Cookie > cookies , Publisher <T > entity ,
367
+ Type entityType ) {
368
+ super (statusCode , headers , cookies , entity , entityType );
363
369
}
364
370
365
371
@ Override
@@ -425,6 +431,8 @@ public void onError(Throwable t) {
425
431
(HttpServletRequest ) this .asyncContext .getRequest (),
426
432
(HttpServletResponse ) this .asyncContext .getResponse (),
427
433
this .context );
434
+
435
+ this .asyncContext .complete ();
428
436
}
429
437
430
438
@ Override
0 commit comments