春季休息和Jsonp

时间:2014-11-17 14:24:53

标签: java jquery spring rest jsonp

我想让我的Spring休息控制器返回 jsonp ,但我没有快乐

完全相同的代码可以正常工作,如果我想返回json但我需要返回 jsonp 我在转换器中添加了一个用于执行jsonp转换的在线源代码

我正在使用Spring版本4.1.1.RELEASE和Java 7

非常感谢任何帮助

以下是有问题的代码

MVC-调度-servlet.xml中

    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">


  <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
       <property name="favorPathExtension" value="false" />
       <property name="favorParameter" value="true" />
       <property name="parameterName" value="mediaType" />
       <property name="ignoreAcceptHeader" value="false"/>
       <property name="useJaf" value="false"/>
       <property name="defaultContentType" value="application/json" />

       <property name="mediaTypes">
            <map>
                <entry key="atom"  value="application/atom+xml" />
                <entry key="html"  value="text/html" />
                <entry key="jsonp" value="application/javascript" />
                <entry key="json"  value="application/json" />
                <entry key="xml"   value="application/xml"/>
            </map>
        </property>
  </bean>

    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="contentNegotiationManager" ref="contentNegotiationManager" />
        <property name="viewResolvers">
            <list>
                <bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
                <bean
                    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                    <property name="prefix" value="/WEB-INF/templates/slim/${views.template.directory}/" />
                    <property name="suffix" value=".jsp" />
                </bean>
            </list>
        </property>
        <property name="defaultViews">
            <list>
                <bean class="com.webapp.handler.MappingJacksonJsonpView" />
            </list>
        </property>
    </bean>

</beans>

com.webapp.handler.MappingJacksonJsonpView

package com.webapp.handler;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

public class MappingJacksonJsonpView extends MappingJackson2JsonView {
    /** Local log variable. **/
    private static final Logger LOG = LoggerFactory.getLogger(MappingJacksonJsonpView.class);

    /**
     * Default content type. Overridable as bean property.
     */
    public static final String DEFAULT_CONTENT_TYPE = "application/javascript";

    @Override
    public String getContentType() {
        return DEFAULT_CONTENT_TYPE;
    }

    /**
     * Prepares the view given the specified model, merging it with static
     * attributes and a RequestContext attribute, if necessary.
     * Delegates to renderMergedOutputModel for the actual rendering.
     * @see #renderMergedOutputModel
     */
    @Override
    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        LOG.info("Entered render Method :{}", request.getMethod());

        if("GET".equals(request.getMethod().toUpperCase())) {
            LOG.info("Request Method is a GET call");

            Map<String, String[]> params = request.getParameterMap();

            if(params.containsKey("callback")) {
                String callbackParam = params.get("callback")[0];
                LOG.info("callbackParam:{}", callbackParam);
                response.getOutputStream().write(new String(callbackParam + "(").getBytes());
                super.render(model, request, response);
                response.getOutputStream().write(new String(");").getBytes());
                response.setContentType(DEFAULT_CONTENT_TYPE);
            }
            else {
                LOG.info("Callback Param not contained in request");
                super.render(model, request, response);
            }
        }

        else {
            LOG.info("Request Method is NOT a GET call");
            super.render(model, request, response);
        }
    }
}

问题中的控制器方法

 @RequestMapping(value = { "/sources"}, method = RequestMethod.GET, 
        produces={MediaType.ALL_VALUE,
        "text/javascript",
        "application/javascript",
        "application/ecmascript",
        "application/x-ecmascript",
        "application/x-javascript", 
        MediaType.APPLICATION_JSON_VALUE})
@ResponseBody
public Object getSources(@PathVariable(value = API_KEY) String apiKey, 
        @RequestParam(value = "searchTerm", required = true) String searchTerm,
        @RequestParam(value = "callBack", required = false) String callBack) {
    LOG.info("Entered getSources - searchTerm:{}, callBack:{} ", searchTerm, callBack);

    List<SearchVO> searchVOList = myServices.findSources(searchTerm);

    if (CollectionUtils.isEmpty(searchVOList)) {
        LOG.error("No results exist for the searchterm of {}", searchTerm);
        return searchVOList;
    }         

    LOG.debug("{} result(s) exist for the searchterm of {}", searchVOList.size(), searchTerm);        

    LOG.info("Exiting getSources");
    return searchVOList;
}

** Jquery Ajax Code **

$.ajax({
                type: "GET",
                url: "http://localhost:8080/my-web/rest/sources,
                data: {
                    "searchTerm": request.term
                },
                //contentType: "application/json; charset=utf-8",
                //dataType: "json",
                contentType: "application/javascript",
                dataType: "jsonp",
                 success: function(data) {
                    alert("success");
                },
                error: function(XMLHttpRequest, textStatus, errorThrown) {
                    alert("Failure");
                }
            });

我得到的错误堆栈跟踪的片段如下

org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:168) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:101) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:198) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:71) ~[spring-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:122) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:781) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:721) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:620) [servlet-api.jar:na]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) [servlet-api.jar:na]

1 个答案:

答案 0 :(得分:11)

正如关于Spring 4.1发布的spring.io blog所述:

  杰克逊现在支持JSONP。对于响应体方法声明   一个@ControllerAdvice,如下所示。仅适用于基于视图的渲染   配置JSONP查询参数名称   MappingJackson2JsonView

@ControllerAdvice
private static class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {

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

}
  

[...]在4.1中,@ ControllerAdvice也可以实现   ResponseBodyAdvice在这种情况下会被调用   controller方法返回但在写入响应之前   因此承诺。这有许多有用的应用程序   @Json查看JSONP已经作为构建于其上的两个示例。

Javadoc取自MappingJackson2JsonView

  

设置JSONP请求参数名称。每次请求都有其中一个   参数,生成的JSON将被包装到一个名为的函数中   由JSONP请求参数值指定。   默认配置的参数名称为&#34; jsonp&#34;和&#34;回调&#34;。

您不需要自己实施这些内容。只需重用Spring Framework中的位。

Spring Boot示例

以下简单的Spring Boot应用程序演示了在Spring MVC 4.1中使用build in JSONP支持。 示例至少需要Spring Boot 1.2.0.RC1。

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice;

import java.util.Collections;

import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

@RestController
@SpringBootApplication
class Application {

    @JsonAutoDetect(fieldVisibility = ANY)
    static class MyBean {
        String attr = "demo";
    }

    @ControllerAdvice
    static class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
        public JsonpAdvice() {
            super("callback");
        }
    }

    @Bean
    public HttpMessageConverters customConverters() {
        return new HttpMessageConverters(false, Collections.<HttpMessageConverter<?> >singleton(new MappingJackson2HttpMessageConverter()));
    }

    @RequestMapping
    MyBean demo() {
        return new MyBean();
    }

    @RequestMapping(produces = APPLICATION_JSON_VALUE)
    String demo2() {
        return "demo2";
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

网址http://localhost:8080/demo?callback=test将POJO转换为JSONP响应:

test({"attr":"demo"});

网址http://localhost:8080/demo2?callback=testString转换为JSONP响应:

test("demo2");