Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
爱吃血肠
spring-framework
提交
91d06389
S
spring-framework
项目概览
爱吃血肠
/
spring-framework
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
S
spring-framework
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
91d06389
编写于
5月 25, 2016
作者:
R
Rossen Stoyanchev
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Polish ResponseBody result handling
上级
da3b21b0
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
98 addition
and
84 deletion
+98
-84
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/SimpleResultHandler.java
...ingframework/web/reactive/result/SimpleResultHandler.java
+2
-2
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java
...e/result/method/annotation/ResponseBodyResultHandler.java
+65
-73
spring-web-reactive/src/main/java/org/springframework/web/reactive/view/ViewResolverResultHandler.java
...ramework/web/reactive/view/ViewResolverResultHandler.java
+4
-4
spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java
...ult/method/annotation/ResponseBodyResultHandlerTests.java
+27
-5
未找到文件。
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/SimpleResultHandler.java
浏览文件 @
91d06389
...
...
@@ -42,10 +42,10 @@ import org.springframework.web.server.ServerWebExchange;
*/
public
class
SimpleResultHandler
implements
Ordered
,
HandlerResultHandler
{
private
int
order
=
Ordered
.
LOWEST_PRECEDENCE
;
private
ConversionService
conversionService
;
private
int
order
=
Ordered
.
LOWEST_PRECEDENCE
;
public
SimpleResultHandler
()
{
}
...
...
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java
浏览文件 @
91d06389
...
...
@@ -19,10 +19,8 @@ package org.springframework.web.reactive.result.method.annotation;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.Comparator
;
import
java.util.HashMap
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Optional
;
import
java.util.Set
;
import
java.util.stream.Collectors
;
...
...
@@ -40,7 +38,6 @@ import org.springframework.http.converter.reactive.HttpMessageConverter;
import
org.springframework.http.server.reactive.ServerHttpRequest
;
import
org.springframework.http.server.reactive.ServerHttpResponse
;
import
org.springframework.util.Assert
;
import
org.springframework.util.MimeType
;
import
org.springframework.web.bind.annotation.ResponseBody
;
import
org.springframework.web.method.HandlerMethod
;
import
org.springframework.web.reactive.HandlerResult
;
...
...
@@ -50,6 +47,10 @@ import org.springframework.web.server.ServerWebExchange;
/**
* {@code HandlerResultHandler} that handles return values from methods annotated
* with {@code @ResponseBody} writing to the body of the request or response with
* an {@link HttpMessageConverter}.
*
* @author Rossen Stoyanchev
* @author Stephane Maldini
* @author Sebastien Deleuze
...
...
@@ -57,47 +58,48 @@ import org.springframework.web.server.ServerWebExchange;
*/
public
class
ResponseBodyResultHandler
implements
HandlerResultHandler
,
Ordered
{
private
static
final
MediaType
MEDIA_TYPE_APPLICATION
=
new
MediaType
(
"application"
);
private
static
final
MediaType
MEDIA_TYPE_APPLICATION
_ALL
=
new
MediaType
(
"application"
);
private
final
List
<
HttpMessageConverter
<?>>
messageConverters
;
private
final
ConversionService
conversionService
;
private
final
List
<
MediaType
>
all
MediaTypes
;
private
final
List
<
MediaType
>
supported
MediaTypes
;
private
final
Map
<
HttpMessageConverter
<?>,
List
<
MediaType
>>
mediaTypesByEncoder
;
private
int
order
=
0
;
private
int
order
=
0
;
// TODO: should be MAX_VALUE
/**
* Constructor with message converters and conversion service.
* @param messageConverters converters for writing the response body with
* @param conversionService for converting to Flux and Mono from other reactive types
*/
public
ResponseBodyResultHandler
(
List
<
HttpMessageConverter
<?>>
messageConverters
,
ConversionService
service
)
{
ConversionService
conversionService
)
{
Assert
.
notEmpty
(
messageConverters
,
"At least one message converter is required."
);
Assert
.
notNull
(
s
ervice
,
"'conversionService' is required."
);
Assert
.
notNull
(
conversionS
ervice
,
"'conversionService' is required."
);
this
.
messageConverters
=
messageConverters
;
this
.
conversionService
=
service
;
this
.
allMediaTypes
=
getAllMediaTypes
(
messageConverters
);
this
.
mediaTypesByEncoder
=
getMediaTypesByConverter
(
messageConverters
);
this
.
conversionService
=
conversionService
;
this
.
supportedMediaTypes
=
initSupportedMediaTypes
(
messageConverters
);
}
private
static
List
<
MediaType
>
getAllMediaTypes
(
List
<
HttpMessageConverter
<?>>
messageConverters
)
{
private
static
List
<
MediaType
>
initSupportedMediaTypes
(
List
<
HttpMessageConverter
<?>>
converters
)
{
Set
<
MediaType
>
set
=
new
LinkedHashSet
<>();
messageConverters
.
forEach
(
converter
->
set
.
addAll
(
converter
.
getWritableMediaTypes
()));
converters
.
forEach
(
converter
->
set
.
addAll
(
converter
.
getWritableMediaTypes
()));
List
<
MediaType
>
result
=
new
ArrayList
<>(
set
);
MediaType
.
sortBySpecificity
(
result
);
return
Collections
.
unmodifiableList
(
result
);
}
private
static
Map
<
HttpMessageConverter
<?>,
List
<
MediaType
>>
getMediaTypesByConverter
(
List
<
HttpMessageConverter
<?>>
converters
)
{
Map
<
HttpMessageConverter
<?>,
List
<
MediaType
>>
result
=
new
HashMap
<>(
converters
.
size
());
converters
.
forEach
(
converter
->
result
.
put
(
converter
,
converter
.
getWritableMediaTypes
()));
return
Collections
.
unmodifiableMap
(
result
);
}
/**
* Set the order for this result handler relative to others.
* <p>By default this is set to 0 and is generally save to be ahead of other
* result handlers since it only gets involved if the method (or class) is
* annotated with {@code @ResponseBody}.
* @param order the order
*/
public
void
setOrder
(
int
order
)
{
this
.
order
=
order
;
}
...
...
@@ -144,27 +146,23 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
elementType
=
returnType
;
}
List
<
MediaType
>
compatibleMediaTypes
=
getCompatibleMediaTypes
(
exchange
.
getRequest
()
,
elementType
);
ServerHttpRequest
request
=
exchange
.
getRequest
();
List
<
MediaType
>
compatibleMediaTypes
=
getCompatibleMediaTypes
(
request
,
elementType
);
if
(
compatibleMediaTypes
.
isEmpty
())
{
return
Mono
.
error
(
new
NotAcceptableStatusException
(
getProducibleMediaTypes
(
elementType
)
));
List
<
MediaType
>
supported
=
getProducibleMediaTypes
(
elementType
);
return
Mono
.
error
(
new
NotAcceptableStatusException
(
supported
));
}
Optional
<
MediaType
>
selectedMediaType
=
selectBestMediaType
(
compatibleMediaTypes
);
if
(
selectedMediaType
.
isPresent
())
{
HttpMessageConverter
<?>
converter
=
resolveEncoder
(
elementType
,
selectedMediaType
.
get
());
MediaType
bestMediaType
=
selectBestMediaType
(
compatibleMediaTypes
);
if
(
bestMediaType
!=
null
)
{
HttpMessageConverter
<?>
converter
=
resolveEncoder
(
elementType
,
bestMediaType
);
if
(
converter
!=
null
)
{
ServerHttpResponse
response
=
exchange
.
getResponse
();
return
converter
.
write
((
Publisher
)
publisher
,
elementType
,
selectedMediaType
.
get
(),
response
);
return
converter
.
write
((
Publisher
)
publisher
,
elementType
,
bestMediaType
,
response
);
}
}
return
Mono
.
error
(
new
NotAcceptableStatusException
(
this
.
all
MediaTypes
));
return
Mono
.
error
(
new
NotAcceptableStatusException
(
this
.
supported
MediaTypes
));
}
private
List
<
MediaType
>
getCompatibleMediaTypes
(
ServerHttpRequest
request
,
...
...
@@ -174,11 +172,12 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
List
<
MediaType
>
producibleMediaTypes
=
getProducibleMediaTypes
(
elementType
);
Set
<
MediaType
>
compatibleMediaTypes
=
new
LinkedHashSet
<>();
for
(
MediaType
acceptableMediaType
:
acceptableMediaTypes
)
{
compatibleMediaTypes
.
addAll
(
producibleMediaTypes
.
stream
().
filter
(
acceptableMediaType:
:
isCompatibleWith
).
map
(
producibleType
->
getMostSpecificMediaType
(
acceptableMediaType
,
producibleType
)).
collect
(
Collectors
.
toList
()));
for
(
MediaType
acceptable
:
acceptableMediaTypes
)
{
for
(
MediaType
producible
:
producibleMediaTypes
)
{
if
(
acceptable
.
isCompatibleWith
(
producible
))
{
compatibleMediaTypes
.
add
(
getMostSpecificMediaType
(
acceptable
,
producible
));
}
}
}
List
<
MediaType
>
result
=
new
ArrayList
<>(
compatibleMediaTypes
);
...
...
@@ -191,44 +190,37 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
return
(
mediaTypes
.
isEmpty
()
?
Collections
.
singletonList
(
MediaType
.
ALL
)
:
mediaTypes
);
}
private
Optional
<
MediaType
>
selectBestMediaType
(
List
<
MediaType
>
compatibleMediaTypes
)
{
for
(
MediaType
mediaType
:
compatibleMediaTypes
)
{
if
(
mediaType
.
isConcrete
())
{
return
Optional
.
of
(
mediaType
);
}
else
if
(
mediaType
.
equals
(
MediaType
.
ALL
)
||
mediaType
.
equals
(
MEDIA_TYPE_APPLICATION
))
{
return
Optional
.
of
(
MediaType
.
APPLICATION_OCTET_STREAM
);
}
}
return
Optional
.
empty
();
}
private
List
<
MediaType
>
getProducibleMediaTypes
(
ResolvableType
type
)
{
List
<
MediaType
>
result
=
this
.
messageConverters
.
stream
()
return
this
.
messageConverters
.
stream
()
.
filter
(
converter
->
converter
.
canWrite
(
type
,
null
))
.
flatMap
(
encoder
->
this
.
mediaTypesByEncoder
.
get
(
encoder
).
stream
())
.
collect
(
Collectors
.
toList
());
if
(
result
.
isEmpty
())
{
result
.
add
(
MediaType
.
ALL
);
}
return
result
;
.
flatMap
(
converter
->
converter
.
getWritableMediaTypes
(
).
stream
())
.
collect
(
Collectors
.
collectingAndThen
(
Collectors
.
toList
(),
result
->
{
if
(
result
.
isEmpty
())
{
result
.
add
(
MediaType
.
ALL
);
}
return
result
;
}))
;
}
/**
* Return the more specific of the acceptable and the producible media types
* with the q-value of the former.
*/
private
MediaType
getMostSpecificMediaType
(
MediaType
acceptType
,
MediaType
produceType
)
{
produceType
=
produceType
.
copyQualityValue
(
acceptType
);
private
MediaType
getMostSpecificMediaType
(
MediaType
acceptable
,
MediaType
producible
)
{
producible
=
producible
.
copyQualityValue
(
acceptable
);
Comparator
<
MediaType
>
comparator
=
MediaType
.
SPECIFICITY_COMPARATOR
;
return
(
comparator
.
compare
(
acceptType
,
produceType
)
<=
0
?
acceptType
:
produceType
);
return
(
comparator
.
compare
(
acceptable
,
producible
)
<=
0
?
acceptable
:
producible
);
}
private
MediaType
selectBestMediaType
(
List
<
MediaType
>
compatibleMediaTypes
)
{
for
(
MediaType
mediaType
:
compatibleMediaTypes
)
{
if
(
mediaType
.
isConcrete
())
{
return
mediaType
;
}
else
if
(
mediaType
.
equals
(
MediaType
.
ALL
)
||
mediaType
.
equals
(
MEDIA_TYPE_APPLICATION_ALL
))
{
return
MediaType
.
APPLICATION_OCTET_STREAM
;
}
}
return
null
;
}
private
HttpMessageConverter
<?>
resolveEncoder
(
ResolvableType
type
,
MediaType
mediaType
)
{
private
HttpMessageConverter
<?>
resolveEncoder
(
ResolvableType
type
,
MediaType
mediaType
)
{
for
(
HttpMessageConverter
<?>
converter
:
this
.
messageConverters
)
{
if
(
converter
.
canWrite
(
type
,
mediaType
))
{
return
converter
;
...
...
spring-web-reactive/src/main/java/org/springframework/web/reactive/view/ViewResolverResultHandler.java
浏览文件 @
91d06389
...
...
@@ -53,7 +53,7 @@ public class ViewResolverResultHandler implements HandlerResultHandler, Ordered
private
final
ConversionService
conversionService
;
private
int
order
=
Integer
.
MAX_VALU
E
;
private
int
order
=
Ordered
.
LOWEST_PRECEDENC
E
;
public
ViewResolverResultHandler
(
List
<
ViewResolver
>
resolvers
,
ConversionService
service
)
{
...
...
@@ -87,17 +87,17 @@ public class ViewResolverResultHandler implements HandlerResultHandler, Ordered
@Override
public
boolean
supports
(
HandlerResult
result
)
{
Class
<?>
clazz
=
result
.
getReturnValueType
().
getRawClass
();
if
(
is
ViewName
OrViewReference
(
clazz
))
{
if
(
is
String
OrViewReference
(
clazz
))
{
return
true
;
}
if
(
this
.
conversionService
.
canConvert
(
clazz
,
Mono
.
class
))
{
clazz
=
result
.
getReturnValueType
().
getGeneric
(
0
).
getRawClass
();
return
is
ViewName
OrViewReference
(
clazz
);
return
is
String
OrViewReference
(
clazz
);
}
return
false
;
}
private
boolean
is
ViewName
OrViewReference
(
Class
<?>
clazz
)
{
private
boolean
is
String
OrViewReference
(
Class
<?>
clazz
)
{
return
(
CharSequence
.
class
.
isAssignableFrom
(
clazz
)
||
View
.
class
.
isAssignableFrom
(
clazz
));
}
...
...
spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java
浏览文件 @
91d06389
...
...
@@ -16,37 +16,44 @@
package
org.springframework.web.reactive.result.method.annotation
;
import
java.util.Collections
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.stream.Collectors
;
import
org.junit.Test
;
import
org.reactivestreams.Publisher
;
import
org.springframework.core.ResolvableType
;
import
org.springframework.core.codec.Encoder
;
import
org.springframework.core.codec.support.StringEncoder
;
import
org.springframework.core.convert.support.DefaultConversionService
;
import
org.springframework.http.converter.reactive.CodecHttpMessageConverter
;
import
org.springframework.http.converter.reactive.HttpMessageConverter
;
import
org.springframework.ui.ExtendedModelMap
;
import
org.springframework.web.bind.annotation.ResponseBody
;
import
org.springframework.web.method.HandlerMethod
;
import
org.springframework.web.reactive.HandlerResult
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
/**
* Unit tests for {@link ResponseBodyResultHandler}.
*
* @author Sebastien Deleuze
* @author Rossen Stoyanchev
*/
public
class
ResponseBodyResultHandlerTests
{
@Test
public
void
supports
()
throws
NoSuchMethodException
{
ResponseBodyResultHandler
handler
=
new
ResponseBodyResultHandler
(
Collections
.
singletonList
(
new
CodecHttpMessageConverter
<
String
>(
new
StringEncoder
(),
null
)),
new
DefaultConversionService
());
ResponseBodyResultHandler
handler
=
createResultHandler
(
new
StringEncoder
());
TestController
controller
=
new
TestController
();
HandlerMethod
hm
=
new
HandlerMethod
(
controller
,
TestController
.
class
.
getMethod
(
"notAnnotated"
));
HandlerMethod
hm
=
new
HandlerMethod
(
controller
,
TestController
.
class
.
getMethod
(
"notAnnotated"
));
ResolvableType
type
=
ResolvableType
.
forMethodParameter
(
hm
.
getReturnType
());
assertFalse
(
handler
.
supports
(
new
HandlerResult
(
hm
,
null
,
type
,
new
ExtendedModelMap
())));
...
...
@@ -59,6 +66,21 @@ public class ResponseBodyResultHandlerTests {
assertTrue
(
handler
.
supports
(
new
HandlerResult
(
hm
,
null
,
type
,
new
ExtendedModelMap
())));
}
@Test
public
void
defaultOrder
()
throws
Exception
{
ResponseBodyResultHandler
handler
=
createResultHandler
(
new
StringEncoder
());
assertEquals
(
0
,
handler
.
getOrder
());
}
private
ResponseBodyResultHandler
createResultHandler
(
Encoder
<?>...
encoders
)
{
List
<
HttpMessageConverter
<?>>
converters
=
Arrays
.
stream
(
encoders
)
.
map
(
encoder
->
new
CodecHttpMessageConverter
<>(
encoder
,
null
))
.
collect
(
Collectors
.
toList
());
return
new
ResponseBodyResultHandler
(
converters
,
new
DefaultConversionService
());
}
@SuppressWarnings
(
"unused"
)
private
static
class
TestController
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录