SpringMVC 4 1使用ResponseBodyAdvice支持jsonp

               

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

  1. package org.springframework.web.servlet.mvc.method.annotation;  
  2.   
  3. /** 
  4.  * Allows customizing the response after the execution of an {@code @ResponseBody} 
  5.  * or an {@code ResponseEntity} controller method but before the body is written 
  6.  * with an {@code HttpMessageConverter}. 
  7.  * 
  8.  * <p>Implementations may be may be registered directly with 
  9.  * {@code RequestMappingHandlerAdapter} and {@code ExceptionHandlerExceptionResolver} 
  10.  * or more likely annotated with {@code @ControllerAdvice} in which case they 
  11.  * will be auto-detected by both. 
  12.  * 
  13.  * @author Rossen Stoyanchev 
  14.  * @since 4.1 
  15.  */  
  16. public interface ResponseBodyAdvice<T> {  
  17.   
  18.    /** 
  19.     * Whether this component supports the given controller method return type 
  20.     * and the selected {@code HttpMessageConverter} type. 
  21.     * @param returnType the return type 
  22.     * @param converterType the selected converter type 
  23.     * @return {@code true} if {@link #beforeBodyWrite} should be invoked, {@code false} otherwise 
  24.     */  
  25.    boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);  
  26.   
  27.    /** 
  28.     * Invoked after an {@code HttpMessageConverter} is selected and just before 
  29.     * its write method is invoked. 
  30.     * @param body the body to be written 
  31.     * @param returnType the return type of the controller method 
  32.     * @param selectedContentType the content type selected through content negotiation 
  33.     * @param selectedConverterType the converter type selected to write to the response 
  34.     * @param request the current request 
  35.     * @param response the current response 
  36.     * @return the body that was passed in or a modified, possibly new instance 
  37.     */  
  38.    T beforeBodyWrite(T body, MethodParameter returnType, MediaType selectedContentType,  
  39.          Class<? extends HttpMessageConverter<?>> selectedConverterType,  
  40.          ServerHttpRequest request, ServerHttpResponse response);  
  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. @Override  
  2. public final Object beforeBodyWrite(Object body, MethodParameter returnType,  
  3.       MediaType contentType, Class<? extends HttpMessageConverter<?>> converterType,  
  4.       ServerHttpRequest request, ServerHttpResponse response) {  
  5.   
  6.    MappingJacksonValue container = getOrCreateContainer(body);  
  7.    beforeBodyWriteInternal(container, contentType, returnType, request, response);  
  8.    return container;  
  9. }  
@Overridepublic 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. @Override  
  2. protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,  
  3.       MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {  
  4.   
  5.    HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();  
  6.   
  7.    for (String name : this.jsonpQueryParamNames) {  
  8.       String value = servletRequest.getParameter(name);  
  9.       if (value != null) {  
  10.          MediaType contentTypeToUse = getContentType(contentType, request, response);  
  11.          response.getHeaders().setContentType(contentTypeToUse);  
  12.          bodyContainer.setJsonpFunction(value);  
  13.          return;  
  14.       }  
  15.    }  
  16. }  
@Overrideprotected 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. package com.usoft.web.controller.jsonp;  
  2.   
  3. import org.springframework.web.bind.annotation.ControllerAdvice;  
  4. import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice;  
  5.   
  6. /** 
  7.  *  
  8.  */  
  9. @ControllerAdvice(basePackages = "com.usoft.web.controller.jsonp")  
  10. public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {  
  11.     public JsonpAdvice() {  
  12.         super("callback""jsonp");  
  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. package com.usoft.web.controller.jsonp;  
  2.   
  3. import org.springframework.stereotype.Controller;  
  4. import org.springframework.web.bind.annotation.RequestMapping;  
  5. import org.springframework.web.bind.annotation.ResponseBody;  
  6.   
  7. import com.usoft.web.controller.JsonMapper;  
  8. import com.usoft.web.controller.Person;  
  9.   
  10. /** 
  11.  * jsonp 
  12.  */  
  13. @Controller  
  14. public class JsonpController {  
  15.   
  16.     /** 
  17.      * callback({"id":1,"age":12,"name":"lyx"}) 
  18.      *  
  19.      * @param args 
  20.      */  
  21.     public static void main(String args[]) {  
  22.         Person person = new Person(1"lyx"12);  
  23.         System.out.println(JsonMapper.nonNullMapper().toJsonP("callback",  
  24.             person));  
  25.     }  
  26.   
  27.     @RequestMapping("/jsonp1")  
  28.     public Person jsonp1() {  
  29.         return new Person(1"lyx"12);  
  30.     }  
  31.   
  32.     @RequestMapping("/jsonp2")  
  33.     @ResponseBody  
  34.     public Person jsonp2() {  
  35.         return new Person(1"lyx"12);  
  36.     }  
  37.   
  38.     @RequestMapping("/jsonp3")  
  39.     @ResponseBody  
  40.     public String jsonp3() {  
  41.         return JsonMapper.nonNullMapper().toJsonP("callback",  
  42.             new Person(1"lyx"12));  
  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 */@Controllerpublic 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. @Override  
  2. protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,  
  3.       MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {  
  4.   
  5.    HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();  
  6.   
  7.    for (String name : this.jsonpQueryParamNames) {  
  8.       String value = servletRequest.getParameter(name);  
  9.       if (value != null) {  
  10.          MediaType contentTypeToUse = getContentType(contentType, request, response);  
  11.          response.getHeaders().setContentType(contentTypeToUse);  
  12.          bodyContainer.setJsonpFunction(value);  
  13.          return;  
  14.       }  
  15.    }  
  16. }  
@Overrideprotected 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. @Override  
  2. protected void writeInternal(Object object, HttpOutputMessage outputMessage)  
  3.       throws IOException, HttpMessageNotWritableException {  
  4.   
  5.    JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());  
  6.    JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);  
  7.    try {  
  8.       writePrefix(generator, object);  
  9.       Class<?> serializationView = null;  
  10.       Object value = object;  
  11.       if (value instanceof MappingJacksonValue) {  
  12.          MappingJacksonValue container = (MappingJacksonValue) object;  
  13.          value = container.getValue();  
  14.          serializationView = container.getSerializationView();  
  15.       }  
  16.       if (serializationView != null) {  
  17.          this.objectMapper.writerWithView(serializationView).writeValue(generator, value);  
  18.       }  
  19.       else {  
  20.          this.objectMapper.writeValue(generator, value);  
  21.       }  
  22.       writeSuffix(generator, object);  
  23.       generator.flush();  
  24.   
  25.    }  
  26.    catch (JsonProcessingException ex) {  
  27.       throw new HttpMessageNotWritableException("Could not write content: " + ex.getMessage(), ex);  
  28.    }  
  29. }  
  30. @Override  
  31. protected void writePrefix(JsonGenerator generator, Object object) throws IOException {  
  32.    if (this.jsonPrefix != null) {  
  33.       generator.writeRaw(this.jsonPrefix);  
  34.    }  
  35.    String jsonpFunction =  
  36.          (object instanceof MappingJacksonValue ? ((MappingJacksonValue) object).getJsonpFunction() : null);  
  37.    if (jsonpFunction != null) {  
  38.       generator.writeRaw(jsonpFunction + "(");  
  39.    }  
  40. }  
  41. @Override  
  42. protected void writeSuffix(JsonGenerator generator, Object object) throws IOException {  
  43.    String jsonpFunction =  
  44.          (object instanceof MappingJacksonValue ? ((MappingJacksonValue) object).getJsonpFunction() : null);  
  45.    if (jsonpFunction != null) {  
  46.       generator.writeRaw(");");  
  47.    }  
  48. }  
@Overrideprotected 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);   }}@Overrideprotected 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 + "(");   }}@Overrideprotected void writeSuffix(JsonGenerator generator, Object object) throws IOException {   String jsonpFunction =         (object instanceof MappingJacksonValue ? ((MappingJacksonValue) object).getJsonpFunction() : null);   if (jsonpFunction != null) {      generator.writeRaw(");");   }}


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

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"});

           

猜你喜欢

转载自blog.csdn.net/qq_44894516/article/details/89361942
今日推荐