java Spring Rest 和 Jsonp

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26974835/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 10:58:53  来源:igfitidea点击:

Spring Rest and Jsonp

javajqueryspringrestjsonp

提问by Damien Gallagher

I am trying to get my Spring rest controller to return jsonpbut I am having no joy

我试图让我的 Spring rest 控制器返回jsonp,但我没有快乐

The exact same code works ok if I want to return json but I have a requirement to return jsonpI have added in a converter I found source code for online for performing the jsonp conversion

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

I am using Spring version 4.1.1.RELEASE and Java 7

我使用的是 Spring 版本 4.1.1.RELEASE 和 Java 7

Any help is greatly appreciated

任何帮助是极大的赞赏

Here is the code in question

这是有问题的代码

mvc-dispatcher-servlet.xml

mvc-dispatcher-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.MappingHymansonJsonpView" />
            </list>
        </property>
    </bean>

</beans>

com.webapp.handler.MappingHymansonJsonpView

com.webapp.handler.MappingHymansonJsonpView

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.MappingHymanson2JsonView;

public class MappingHymansonJsonpView extends MappingHymanson2JsonView {
    /** Local log variable. **/
    private static final Logger LOG = LoggerFactory.getLogger(MappingHymansonJsonpView.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);
        }
    }
}

Controller Method In Question

有问题的控制器方法

 @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 **

**Jquery Ajax 代码 **

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

A snippet of the error stacktrace that I get is as follows

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

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]

回答by Kamill Sokol

As stated on the spring.io blogregarding the Spring 4.1 release:

正如 spring.io博客上关于 Spring 4.1 版本所述:

JSONP is now supported with Hymanson. For response body methods declare an @ControllerAdvice as shown below. For View-based rendering simply configure the JSONP query parameter name(s) on MappingHymanson2JsonView.

Hymanson 现在支持 JSONP。对于响应体方法,声明一个@ControllerAdvice,如下所示。对于基于视图的渲染,只需在MappingHymanson2JsonView上配置 JSONP 查询参数名称 。

@ControllerAdvice
private static class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {

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

}

[...] In 4.1 an @ControllerAdvice can also implement ResponseBodyAdvice in which case it will be called after the controller method returns but before the response is written and therefore committed. This has a number of useful applications with @JsonView the JSONP already serving as two examples built on it.

[...] 在 4.1 中,@ControllerAdvice 也可以实现 ResponseBodyAdvice,在这种情况下,它将在控制器方法返回之后但在响应被写入并因此提交之前被调用。这有许多有用的应用程序,@JsonView JSONP 已经作为构建在它上面的两个示例。

Javadoc taken from MappingHymanson2JsonView:

Javadoc 取自MappingHymanson2JsonView

Set JSONP request parameter names. Each time a request has one of those parameters, the resulting JSON will be wrapped into a function named as specified by the JSONP request parameter value. The parameter names configured by default are "jsonp" and "callback".

设置 JSONP 请求参数名称。每次请求具有这些参数之一时,生成的 JSON 将被包装到一个由 JSONP 请求参数值指定的函数中。默认配置的参数名称为“jsonp”和“callback”。

You don't need to implement this stuff by yourself. Just reuse the bits from the Spring Framework.

你不需要自己实现这些东西。只需重用 Spring Framework 中的位即可。

Spring Boot example

Spring Boot 示例

Following simple Spring Boot application demonstrates use of build in JSONP support in Spring MVC 4.1. Example requires at least Spring Boot 1.2.0.RC1.

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

import com.fasterxml.Hymanson.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.MappingHymanson2HttpMessageConverter;
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.Hymanson.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 MappingHymanson2HttpMessageConverter()));
    }

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

URL http://localhost:8080/demo?callback=testconverts a POJO into a JSONPresponse:

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

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

URL http://localhost:8080/demo2?callback=testconverts a Stringinto a JSONPresponse:

URLhttp://localhost:8080/demo2?callback=test将 aString转换为JSONP响应:

test("demo2");