Spring 4: MappingJackson2HttpMessageConverter does

2019-06-01 00:14发布

问题:

Using Spring 4.1.6.RELEASE and Jackson libraries v2.5.4

Want to use JSONP in my REST Services using Spring 4; Spring 4 supports JSONP out of the box.

CONFIGURATION

<mvc:annotation-driven
    content-negotiation-manager="contentNegotiationManager">
...
</mvc:annotation-driven>

Using contentNegotiationManager in following manner

<bean id="contentNegotiationManager"
    class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">

    <property name="defaultContentType" value="application/json" />


    <!-- Configuration of Path Extension Based ContentNegotiationStrategy -->
    <property name="favorPathExtension" value="false" />

    <!-- Configuration of Request Parameter Based ContentNegotiationStrategy -->
    <property name="favorParameter" value="true" />
    <property name="parameterName" value="formatType" />
    <property name="mediaTypes" ref="mediaTypesMapping" />
    <!-- Configuration of HTTP ACCEPT Header Based ContentNegotiationStrategy -->
    <property name="ignoreAcceptHeader" value="true" />

</bean>

Here is the MediaTypeMapping

<util:map id="mediaTypesMapping">
    <entry key="json" value="application/json" />
    <entry key="xml" value="application/xml" />
    <entry key="jsonp" value="application/javascript" />
</util:map>

Here is the jsonpParameterNames

<util:set id="jsonpParameterNames">
    <value>param1</value>
    <value>param2</value>
    <value>param3</value>
</util:set>

Deployed my REST Service.

  • hitting the following URL gives data in json format

     /contextRoot/info/1?formatType=json
    
  • hitting the following URL gives data in xmlformat

     /contextRoot/info/1?formatType=xml
    
  • hitting the following URL gives 406 EXCEPTION

     /contextRoot/info/1?formatType=jsonp&param1=callback1
    

Observations

On doing investigation, discovered that MappingJackson2HttpMessageConverter does not supports application/javascript.

Surprised by this since Spring 4 claims to support JSONP Out of the box.

Furthermore, MappingJackson2JsonView does support JSONP out of the box.

Question

Am I missing something in the configuration which once added will enable me to use content type application/javascript out of the box with SPRING 4 for JSONP?

EDIT

Here is my ControllerAdvice Configuration:

<bean id="jsonpAdvice"
    class="com.advice.AccountServiceControllerJsonpAdvice">
    <constructor-arg ref="jsonpParameterNames" />
</bean>

And here is the ControllerAdvice

 public class AccountServiceControllerJsonpAdvice extends
    AbstractJsonpResponseBodyAdvice {

private String[] callbackList = {"callback"};

public String[] getCallbackList() {
    return callbackList;
}

public void setCallbackList(String[] callbackList) {
    this.callbackList = callbackList;
}

public AccountServiceControllerJsonpAdvice(String... callbackList) {
    super(callbackList);
}
}

However, this does not enable my application to understand a URL like following and returns a 406 error

   /contextRoot/info/1?formatType=jsonp&param1=callback1

It only makes my application understand a request URL like following and return a JSONP response:

   /contextRoot/info/1?param1=callback1

回答1:

Yes, Spring 4.1+ supports JSONP, but it's not a conversion format per se.

MappingJackson2HttpMessageConverter supports "application/json" and "*+json" media types, but does not support "application/javascript". If it did, then you'd expect it to parse javascript code, which is not the case.

Here, we're merely wrapping the output to make it "application/javascript", but really, it's still JSON.

Your current configuration is disabling content negotiation with HTTP Accept headers (why?). In order to support JSONP you only need this in your MVC configuration:

<mvc:annotation-driven/>

And an additional ControllerAdvice like this (see reference documentation):

@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {

    public JsonpAdvice() {
        super("callback");
    }
}

That's it.

Now you'll get:

GET /contextRoot/info/1
// will return what's best depending on the Accept header sent by the client

GET /contextRoot/info/1.xml
// will return XML

GET /contextRoot/info/1.json
// will return JSON

GET /contextRoot/info/1.json?callback=myFunction
// will return JSONP wrapped in a "myFunction" call

In your case, you should:

  1. Remove "application/javascript" from your media types mappings, or for backwards compatibility associate "jsonp" with the "application/json" media type.
  2. Use GET /contextRoot/info/1?formatType=json&param1=callback1

See AbstractJsonpResponseBodyAdvice for more details on the implementation.