SpringMVC已經大行其道。通常的,都是返回JSP視圖。若是須要返回JSON格式,咱們大都掌握了一些方法。html
在ContentNegotiatingViewResolver以前,通常使用XmlViewResolver的location屬性,手工編寫一個視圖專門處理Json類型,就是將返回的數據封裝成Json輸出。下面介紹該方案具體代碼。java
exam-servlet.xml:web
<bean id="xmlViewResolver" class="org.springframework.web.servlet.view.XmlViewResolver"> <property name="order" value="1"/> <property name="location" value="/WEB-INF/views.xml"/> </bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="order" value="2"></property> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> </bean>
views.xmlspring
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="json" class="cc.monggo.web.views.JsonView"></bean> </beans>
JsonView.java編程
/** * */ package cc.monggo.web.views; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sf.json.JSON; import net.sf.json.JSONArray; import net.sf.json.JSONSerializer; import net.sf.json.JsonConfig; import net.sf.json.filters.OrPropertyFilter; import net.sf.json.util.PropertyFilter; import org.springframework.web.servlet.view.AbstractView; import cc.monggo.web.form.BaseForm; /** * @author fangjinsong * */ public class JsonView extends AbstractView { private static final String DEFAULT_JSON_CONTENT_TYPE = "application/json;charset=UTF-8"; private boolean forceTopLevelArray = false; private boolean skipBindingResult = true; private JsonConfig jsonConfig = new JsonConfig(); public JsonView() { super(); setContentType(DEFAULT_JSON_CONTENT_TYPE); } public boolean isForceTopLevelArray() { return forceTopLevelArray; } public void setForceTopLevelArray(boolean forceTopLevelArray) { this.forceTopLevelArray = forceTopLevelArray; } public boolean isSkipBindingResult() { return skipBindingResult; } public void setSkipBindingResult(boolean skipBindingResult) { this.skipBindingResult = skipBindingResult; } public JsonConfig getJsonConfig() { return jsonConfig; } public void setJsonConfig(JsonConfig jsonConfig) { this.jsonConfig = jsonConfig != null ? jsonConfig : new JsonConfig(); if (skipBindingResult) { PropertyFilter jsonPropertyFilter = this.jsonConfig.getJavaPropertyFilter(); if (jsonPropertyFilter == null) { this.jsonConfig.setJsonPropertyFilter(new BindingResultPropertyFilter()); } else { this.jsonConfig.setJsonPropertyFilter(new OrPropertyFilter(new BindingResultPropertyFilter(), jsonPropertyFilter)); } } } public static String getDefaultJsonContentType() { return DEFAULT_JSON_CONTENT_TYPE; } public boolean isIgnoreDefaultExcludes() { return getJsonConfig().isIgnoreDefaultExcludes(); } public void setExcludedProperties(String[] excludedProperties) { jsonConfig.setExcludes(excludedProperties); } public void setIgnoreDefaultExcludes(boolean ignoreDefaultExcludes) { jsonConfig.setIgnoreDefaultExcludes(ignoreDefaultExcludes); } protected String[] getExcludedProperties() { return jsonConfig.getExcludes(); } @Override protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { response.setContentType(getContentType()); Map newModel = new HashMap(); for (Iterator it = model.keySet().iterator(); it.hasNext();) { Object key = it.next(); Object value = model.get(key); if (!(value instanceof BaseForm)) { newModel.put(key, value); } } writeJSON(newModel, request, response); } protected void writeJSON(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { JSON json = createJSON(model, request, response); if (forceTopLevelArray) { json = new JSONArray().element(json); } json.write(response.getWriter()); } protected final JSON defaultCreateJSON(Map model) { if (skipBindingResult && jsonConfig.getJavaPropertyFilter() == null) { jsonConfig.setJsonPropertyFilter(new BindingResultPropertyFilter()); } return JSONSerializer.toJSON(model, jsonConfig); } private JSON createJSON(Map model, HttpServletRequest request, HttpServletResponse response) { return defaultCreateJSON(model); } private static class BindingResultPropertyFilter implements PropertyFilter { @Override public boolean apply(Object source, String name, Object value) { return name.startsWith("org.springframework.validation.BindingResult"); } } }
這種方案會增長一個views.xml文件。另外一個方案使用ContentNegotiatingViewResolver內容協商解析器。該解析器能夠解析多種Controller返回結果,方法是分派給其餘的解析器解析。這樣就能夠返回JSP視圖,Xml視圖,Json視圖。其中Json視圖由MappingJacksonJsonView視圖表示。ContentNegotiatingViewResolver的通常配置以下:json
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> <property name="order" value="1" /> <property name="ignoreAcceptHeader" value="true" /> <property name="favorParameter" value="false" /> <property name="defaultContentType" value="text/html" /> <property name="mediaTypes"> <map> <entry key="json" value="application/json" /> </map> </property> <property name="viewResolvers"> <list> <bean class="org.springframework.web.servlet.view.BeanNameViewResolver" /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp"></property> </bean> </list> </property> <property name="defaultViews"> <list> <bean id="json" class="com.mogee.web.views.MogeeMappingJacksonJsonView" /> </list> </property> </bean>
要返回Json格式數據,Controller中通常使用JsonResult,@responseBody等技術。例如:app
@RequestMapping("/json3.json") public JsonResult testJson3(@RequestBody User u){ log.info("handle json output from ContentNegotiatingViewResolver"); return new JsonResult(true,"return ok"); }
這樣一來,有一個問題:Controller老是返回Object對象,不能返回ModelAndview類型。按筆者的經驗,常常有這樣的需求:一個請求,若是返回了正確的結果,返回正常的JSP;若是返回了錯誤的結果,咱們但願返回Json類型數據。爲了知足這樣要求,通常使用response.getWriter.print("")技術。筆者並不喜歡這樣寫法,不像Spring的風格。因此下面筆者將提出第二種方案。二者都能兼顧。Contoller代碼以下。jsp
@RequestMapping(value = "/test", method = RequestMethod.GET) public ModelAndView jsonView(HttpServletRequest request, HttpServletResponse response, HelloForm form) throws Exception { Map<String, Object> map = new HashMap<String, Object>(); map.put("hello", "Hello"); ModelAndView mv = new ModelAndView(new MappingJacksonJsonView(), map); return mv; }
經過這樣的代碼,能夠在Controller中靈活控制,返回JSON或者JSP均可以。看似完美的方案,也有一個問題,返回的時候會將Form也打包到Json中去。以下:ide
緣由是由於MappingJacksonJsonView代碼中將數據map,mv.add(),還有form,還有錯誤提示等四項數據都加入Json,原程序只是過濾了錯誤提示,因此就留下了form數據。this
爲此咱們須要重寫MappingJacksonJsonView,將剩餘三種數據中的form也過濾掉。通常的,form都會繼承BaseForm,因此只要使用instanceof判斷便可。這樣的改造簡潔明瞭,符合Spring編程的特色。具體代碼以下:
public class MogeeMappingJacksonJsonView extends MappingJacksonJsonView { @Override protected Object filterModel(Map<String, Object> model) { Map<String, Object> result = new HashMap<String, Object>(model.size()); Set<String> renderedAttributes = !CollectionUtils.isEmpty(super.getRenderedAttributes()) ? super .getRenderedAttributes() : model.keySet(); for (Map.Entry<String, Object> entry : model.entrySet()) { if (!(entry.getValue() instanceof BindingResult) && renderedAttributes.contains(entry.getKey())) { if (!(entry.getValue() instanceof BaseForm)) { result.put(entry.getKey(), entry.getValue()); } } } return result; } }
相應的,exam-servlet.xml中也要修改,代碼以下:
如此,就完成了輸出Json的方案。
方勁鬆 東莞虎門信豐物流
2015.3.20