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
Spring Rest and Jsonp
提问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=test
converts a POJO into a JSONP
response:
URLhttp://localhost:8080/demo?callback=test
将 POJO 转换为JSONP
响应:
test({"attr":"demo"});
URL http://localhost:8080/demo2?callback=test
converts a String
into a JSONP
response:
URLhttp://localhost:8080/demo2?callback=test
将 aString
转换为JSONP
响应:
test("demo2");