spring MVC自定义视图实现jsonp
程序员文章站
2022-04-27 08:44:27
...
约定的优先级规则:
Spring supports a couple of conventions for selecting the format required: URL suffixes and/or a URL parameter. These work alongside the use of Accept headers. As a result, the content-type can be requested in any of three ways. By default they are checked in this order:
Add a path extension (suffix) in the URL. So, if the incoming URL is something likehttp://myserver/myapp/accounts/list.html then HTML is required. For a spreadsheet the URL should be http://myserver/myapp/accounts/list.xls. The suffix to media-type mapping is automatically defined via the JavaBeans Activation Framework or JAF (so activation.jar must be on the class path).
A URL parameter like this: http://myserver/myapp/accounts/list?format=xls. The name of the parameter is format by default, but this may be changed. Using a parameter is disabled by default, but when enabled, it is checked second.
Finally the Accept HTTP header property is checked. This is how HTTP is actually defined to work, but, as previously mentioned, it can be problematic to use.
spring-web.xml配置:
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
<!--1、检查扩展名(如my.pdf);2、检查Parameter(如my?format=pdf);3、检查Accept Header-->
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<!-- 扩展名至mimeType的映射,即 /user.json => application/json -->
<property name="favorPathExtension" value="true"/>
<!-- 用于开启 /userinfo/123?format=json 的支持 -->
<property name="favorParameter" value="true"/>
<property name="parameterName" value="format"/>
<!-- 是否忽略Accept Header -->
<property name="ignoreAcceptHeader" value="false"/>
<!-- 扩展名到MIME的映射;favorPathExtension, favorParameter是true时起作用 注:此项由默认后缀匹配 -->
<property name="mediaTypes">
<map>
<entry key="json" value="application/json"/>
<entry key="xml" value="application/xml"/>
<entry key="html" value="text/html"/>
</map>
</property>
<!-- 默认的content type -->
<property name="defaultContentType" value="application/json"/>
</bean>
<!-- 内容协商视图解析器;根据客户端不同的请求决定不同的view进行响应 -->
<!-- 会自动根据解析的contentType来决定使用哪个视图解析器(默认使用整个web应用中的viewResolver) -->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<!-- 内容协商管理器 用于决定media type -->
<property name="contentNegotiationManager" ref="contentNegotiationManager"/>
<!-- 默认视图 放在解析链最后 -->
<property name="defaultViews">
<list>
<!--<bean class="com.alibaba.fastjson.support.spring.FastJsonJsonView"/>-->
<bean class="com.caiya.test.ExtendedJsonView"/>
</list>
</property>
<property name="order" value="0"/>
</bean>
<!-- 默认的视图解析器 在上边的解析错误时使用 (默认使用 html)- -->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
json和jsonp视图类:
package com.caiya.test;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.servlet.view.AbstractView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Created by caiya on 16/2/1.
*/
public class ExtendedJsonView extends AbstractView {
public static final String DEFAULT_CONTENT_TYPE = "application/json";
public final static Charset UTF8 = Charset.forName("UTF-8");
private Charset charset = UTF8;
private SerializerFeature[] serializerFeatures = new SerializerFeature[0];
private Set<String> renderedAttributes;
private boolean disableCaching = true;
private boolean updateContentLength = false;
public ExtendedJsonView(){
setContentType(DEFAULT_CONTENT_TYPE);
setExposePathVariables(false);
}
public void setRenderedAttributes(Set<String> renderedAttributes) {
this.renderedAttributes = renderedAttributes;
}
@Deprecated
public void setSerializerFeature(SerializerFeature... features) {
this.setFeatures(features);
}
public Charset getCharset() {
return this.charset;
}
public void setCharset(Charset charset) {
this.charset = charset;
}
public SerializerFeature[] getFeatures() {
return serializerFeatures;
}
public void setFeatures(SerializerFeature... features) {
this.serializerFeatures = features;
}
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
Object value = filterModel(model);
String text = JSON.toJSONString(value, serializerFeatures);
String callback = request.getParameter("callback");
if(StringUtils.isNotBlank(callback)){
text = new StringBuilder(callback).append("(").append(text).append(")").toString();
}
byte[] bytes = text.getBytes(charset);
OutputStream stream = this.updateContentLength ? createTemporaryOutputStream() : response.getOutputStream();
stream.write(bytes);
if (this.updateContentLength) {
writeToResponse(response, (ByteArrayOutputStream) stream);
}
}
@Override
protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) {
setResponseContentType(request, response);
response.setCharacterEncoding(UTF8.name());
if (this.disableCaching) {
response.addHeader("Pragma", "no-cache");
response.addHeader("Cache-Control", "no-cache, no-store, max-age=0");
response.addDateHeader("Expires", 1L);
}
}
/**
* Disables caching of the generated JSON.
* <p>
* Default is {@code true}, which will prevent the client from caching the generated JSON.
*/
public void setDisableCaching(boolean disableCaching) {
this.disableCaching = disableCaching;
}
/**
* Whether to update the 'Content-Length' header of the response. When set to {@code true}, the response is buffered
* in order to determine the content length and set the 'Content-Length' header of the response.
* <p>
* The default setting is {@code false}.
*/
public void setUpdateContentLength(boolean updateContentLength) {
this.updateContentLength = updateContentLength;
}
/**
* Filters out undesired attributes from the given model. The return value can be either another {@link Map}, or a
* single value object.
* <p>
* Default implementation removes {@link BindingResult} instances and entries not included in the
* {@link #setRenderedAttributes(Set) renderedAttributes} property.
*
* @param model the model, as passed on to {@link #renderMergedOutputModel}
* @return the object to be rendered
*/
protected Object filterModel(Map<String, Object> model) {
Map<String, Object> result = new HashMap<String, Object>(model.size());
Set<String> renderedAttributes = !CollectionUtils.isEmpty(this.renderedAttributes) ? this.renderedAttributes : model.keySet();
for (Map.Entry<String, Object> entry : model.entrySet()) {
if (!(entry.getValue() instanceof BindingResult) && renderedAttributes.contains(entry.getKey())) {
result.put(entry.getKey(), entry.getValue());
}
}
return result;
}
}
测试controller:
package com.caiya.test.controllers;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* Created by caiya on 16/1/28.
*/
@Controller
@RequestMapping(value = "/test")
public class TestController {
private static final Logger logger = Logger.getLogger(TestController.class);
@RequestMapping(value = {"/test2.json"})
public Object test2(){
Map<String, Object> map = new HashMap<String, Object>();
map.put("key", "value");
return map;
}
@RequestMapping(value = {"/test2"}, produces = {"application/json"})
public Object test22(){
Map<String, Object> map = new HashMap<String, Object>();
map.put("key", "value");
return map;
}
@RequestMapping(value = {"/test2"}, produces = {"text/html"})
public Object test222(){
return "cookie";
}
}
效果图:
--------------------------------------