博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring MVC 4.1 使用ResponseBodyAdvice支持jsonp
阅读量:7222 次
发布时间:2019-06-29

本文共 8582 字,大约阅读时间需要 28 分钟。

hot3.png

Spring MVC 4.1 使用ResponseBodyAdvice支持jsonp 博客分类: spring

Spring MVC 4.1 使用ResponseBodyAdvice支持jsonp

使用ResponseBodyAdvice支持jsonp

ResponseBodyAdvice是一个接口,接口描述,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package 
org.springframework.web.servlet.mvc.method.annotation;
 
/**
 
* Allows customizing the response after the execution of an {@code @ResponseBody}
 
* or an {@code ResponseEntity} controller method but before the body is written
 
* with an {@code HttpMessageConverter}.
 
*
 
* <p>Implementations may be may be registered directly with
 
* {@code RequestMappingHandlerAdapter} and {@code ExceptionHandlerExceptionResolver}
 
* or more likely annotated with {@code @ControllerAdvice} in which case they
 
* will be auto-detected by both.
 
*
 
* @author Rossen Stoyanchev
 
* @since 4.1
 
*/
public 
interface 
ResponseBodyAdvice<T> {
 
   
/**
    
* Whether this component supports the given controller method return type
    
* and the selected {@code HttpMessageConverter} type.
    
* @param returnType the return type
    
* @param converterType the selected converter type
    
* @return {@code true} if {@link #beforeBodyWrite} should be invoked, {@code false} otherwise
    
*/
   
boolean 
supports(MethodParameter returnType, Class<? 
extends 
HttpMessageConverter<?>> converterType);
 
   
/**
    
* Invoked after an {@code HttpMessageConverter} is selected and just before
    
* its write method is invoked.
    
* @param body the body to be written
    
* @param returnType the return type of the controller method
    
* @param selectedContentType the content type selected through content negotiation
    
* @param selectedConverterType the converter type selected to write to the response
    
* @param request the current request
    
* @param response the current response
    
* @return the body that was passed in or a modified, possibly new instance
    
*/
   
T beforeBodyWrite(T body, MethodParameter returnType, MediaType selectedContentType,
         
Class<? 
extends 
HttpMessageConverter<?>> selectedConverterType,
         
ServerHttpRequest request, ServerHttpResponse response);
 
}

作用:

Allows customizing the response after the execution of an {@code @ResponseBody} or an {@code ResponseEntity} controller method but before the body is written

with an {@code HttpMessageConverter}.

其中一个方法就是 beforeBodyWrite 在使用相应的HttpMessageConvert 进行write之前会被调用,就是一个切面方法。

和jsonp有关的实现类是AbstractJsonpResponseBodyAdvice,如下是 beforeBodyWrite 方法的实现,

1
2
3
4
5
6
7
8
9
@Override
public 
final 
Object beforeBodyWrite(Object body, MethodParameter returnType,
      
MediaType contentType, Class<? 
extends 
HttpMessageConverter<?>> converterType,
      
ServerHttpRequest request, ServerHttpResponse response) {
 
   
MappingJacksonValue container = getOrCreateContainer(body);
   
beforeBodyWriteInternal(container, contentType, returnType, request, response);
   
return 
container;
}

位于AbstractJsonpResponseBodyAdvice的父类中,而beforeBodyWriteInternal是在AbstractJsonpResponseBodyAdvice中实现的 ,如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
protected 
void 
beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
      
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
 
   
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
 
   
for 
(String name : 
this
.jsonpQueryParamNames) {
      
String value = servletRequest.getParameter(name);
      
if 
(value != 
null
) {
         
MediaType contentTypeToUse = getContentType(contentType, request, response);
         
response.getHeaders().setContentType(contentTypeToUse);
         
bodyContainer.setJsonpFunction(value);
         
return
;
      
}
   
}
}

就是根据callback 请求参数或配置的其他参数来确定返回jsonp协议的数据。

如何实现jsonp?

首先继承AbstractJsonpResponseBodyAdvice ,如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package 
com.usoft.web.controller.jsonp;
 
import 
org.springframework.web.bind.annotation.ControllerAdvice;
import 
org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice;
 
/**
 
 
*/
@ControllerAdvice
(basePackages = 
"com.usoft.web.controller.jsonp"
)
public 
class 
JsonpAdvice 
extends 
AbstractJsonpResponseBodyAdvice {
    
public 
JsonpAdvice() {
        
super
(
"callback"
"jsonp"
);
    
}
}

 super("callback", "jsonp");的意思就是当请求参数中包含callback 或 jsonp参数时,就会返回jsonp协议的数据。其value就作为回调函数的名称。

这里必须使用@ControllerAdvice注解标注该类,并且配置对哪些Controller起作用。关于注解@ControllerAdvice 的作用这里不做描述。

Controller实现jsonp,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package 
com.usoft.web.controller.jsonp;
 
import 
org.springframework.stereotype.Controller;
import 
org.springframework.web.bind.annotation.RequestMapping;
import 
org.springframework.web.bind.annotation.ResponseBody;
 
import 
com.usoft.web.controller.JsonMapper;
import 
com.usoft.web.controller.Person;
 
/**
 
* jsonp
 
*/
@Controller
public 
class 
JsonpController {
 
    
/**
     
* callback({"id":1,"age":12,"name":"lyx"})
     
     
* @param args
     
*/
    
public 
static 
void 
main(String args[]) {
        
Person person = 
new 
Person(
1
"lyx"
12
);
        
System.out.println(JsonMapper.nonNullMapper().toJsonP(
"callback"
,
            
person));
    
}
 
    
@RequestMapping
(
"/jsonp1"
)
    
public 
Person jsonp1() {
        
return 
new 
Person(
1
"lyx"
12
);
    
}
 
    
@RequestMapping
(
"/jsonp2"
)
    
@ResponseBody
    
public 
Person jsonp2() {
        
return 
new 
Person(
1
"lyx"
12
);
    
}
 
    
@RequestMapping
(
"/jsonp3"
)
    
@ResponseBody
    
public 
String jsonp3() {
        
return 
JsonMapper.nonNullMapper().toJsonP(
"callback"
,
            
new 
Person(
1
"lyx"
12
));
    
}
}

jsonp2 方法就是 一个jsonp协议的调用。http://localhost:8081/jsonp2?callback=test可以直接调用这个方法,并且返回jsonp协议的数据。

通过debug代码,我们来看一下他是怎么返回jsonp协议的数据的。

正因为我们前面在 该Controller 上配置了 JsonpAdvice 的 ControllerAdvice,在调用 MappingJackson2HttpMessageConverter的write()方法往回写数据的时候,首先会调用

beforeBodyWrite,具体的代码如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
protected 
void 
beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
      
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
 
   
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
 
   
for 
(String name : 
this
.jsonpQueryParamNames) {
      
String value = servletRequest.getParameter(name);
      
if 
(value != 
null
) {
         
MediaType contentTypeToUse = getContentType(contentType, request, response);
         
response.getHeaders().setContentType(contentTypeToUse);
         
bodyContainer.setJsonpFunction(value);
         
return
;
      
}
   
}
}

当请求参数中含有配置的相应的回调参数时,就会bodyContainer.setJsonpFunction(value);这就标志着 返回的数据时jsonp格式的数据。

然后接下来就到了 MappingJackson2HttpMessageConverter 的write()方法真正写数据的时候了。看他是怎么写数据的,相关的代码如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Override
protected 
void 
writeInternal(Object object, HttpOutputMessage outputMessage)
      
throws 
IOException, HttpMessageNotWritableException {
 
   
JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
   
JsonGenerator generator = 
this
.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
   
try 
{
      
writePrefix(generator, object);
      
Class<?> serializationView = 
null
;
      
Object value = object;
      
if 
(value 
instanceof 
MappingJacksonValue) {
         
MappingJacksonValue container = (MappingJacksonValue) object;
         
value = container.getValue();
         
serializationView = container.getSerializationView();
      
}
      
if 
(serializationView != 
null
) {
         
this
.objectMapper.writerWithView(serializationView).writeValue(generator, value);
      
}
      
else 
{
         
this
.objectMapper.writeValue(generator, value);
      
}
      
writeSuffix(generator, object);
      
generator.flush();
 
   
}
   
catch 
(JsonProcessingException ex) {
      
throw 
new 
HttpMessageNotWritableException(
"Could not write content: " 
+ ex.getMessage(), ex);
   
}
}
1
2
3
4
5
6
7
8
9
10
11
@Override
protected 
void 
writePrefix(JsonGenerator generator, Object object) 
throws 
IOException {
   
if 
(
this
.jsonPrefix != 
null
) {
      
generator.writeRaw(
this
.jsonPrefix);
   
}
   
String jsonpFunction =
         
(object 
instanceof 
MappingJacksonValue ? ((MappingJacksonValue) object).getJsonpFunction() : 
null
);
   
if 
(jsonpFunction != 
null
) {
      
generator.writeRaw(jsonpFunction + 
"("
);
   
}
}
1
2
3
4
5
6
7
8
@Override
protected 
void 
writeSuffix(JsonGenerator generator, Object object) 
throws 
IOException {
   
String jsonpFunction =
         
(object 
instanceof 
MappingJacksonValue ? ((MappingJacksonValue) object).getJsonpFunction() : 
null
);
   
if 
(jsonpFunction != 
null
) {
      
generator.writeRaw(
");"
);
   
}
}

代码非常清晰。看我们jsonp调用的结果。

1
http:
//localhost:8081/jsonp2?callback=test

响应消息如下,

HTTP/1.1 200 OK

Server: Apache-Coyote/1.1

Content-Type: application/javascript

Transfer-Encoding: chunked

Date: Sun, 19 Jul 2015 13:01:02 GMT

 

test({"id":1,"age":12,"name":"lyx"});

=================END=================

 

http://my.oschina.net/xinxingegeya/blog/480510?fromerr=yYIwo0JR

转载于:https://my.oschina.net/xiaominmin/blog/1597025

你可能感兴趣的文章
结构体类型
查看>>
SQL SERVER数据库 三种 恢复模式
查看>>
android.os.NetworkOnMainThreadException的解决方案
查看>>
16、SpringBoot-CRUD错误处理机制(3)
查看>>
git 覆盖本地变化
查看>>
Java中的内存分配机制
查看>>
找回Gnome菜单下的关机键
查看>>
CQOI2019(十二省联考)游记
查看>>
【总结整理】需求分析所需掌握技能(转)
查看>>
Linux常用命令
查看>>
PHP基础知识(二)
查看>>
android之VideoView和视频播放View的扩展
查看>>
stdout stdin stderr
查看>>
FreeMarker 一二事 - 静态模板结合spring展示
查看>>
07:企业级镜像仓库Harbor
查看>>
bzoj4427【Nwerc2015】Cleaning Pipes清理管道
查看>>
事务隔离级别
查看>>
Python 函数
查看>>
Linux64位程序中的漏洞利用
查看>>
gdb教程
查看>>