zoukankan      html  css  js  c++  java
  • SpringMVC返回JSON方案

      SpringMVC已经大行其道。一般的,都是返回JSP视图。如果需要返回JSON格式,我们大都掌握了一些方法。

      在ContentNegotiatingViewResolver之前,一般使用XmlViewResolver的location属性,手工编写一个视图专门处理Json类型,就是将返回的数据封装成Json输出。下面介绍该方案具体代码。

      exam-servlet.xml:

        <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.xml

    <?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");
            }
        }
    
    }
    View Code

      这种方案会增加一个views.xml文件。另一个方案使用ContentNegotiatingViewResolver内容协商解析器。该解析器可以解析多种Controller返回结果,方法是分派给其他的解析器解析。这样就可以返回JSP视图,Xml视图,Json视图。其中Json视图由MappingJacksonJsonView视图表示。ContentNegotiatingViewResolver的一般配置如下:

        <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>
    View Code

      要返回Json格式数据,Controller中一般使用JsonResult,@responseBody等技术。例如:

    @RequestMapping("/json3.json")
        public JsonResult testJson3(@RequestBody User u){
            log.info("handle json output from ContentNegotiatingViewResolver");
            return new JsonResult(true,"return ok");
        }
    View Code

      这样一来,有一个问题:Controller总是返回Object对象,不能返回ModelAndview类型。按笔者的经验,经常有这样的需求:一个请求,如果返回了正确的结果,返回正常的JSP;如果返回了错误的结果,我们希望返回Json类型数据。为了满足这样要求,一般使用response.getWriter.print("")技术。笔者并不喜欢这样写法,不像Spring的风格。所以下面笔者将提出第二种方案。两者都能兼顾。Contoller代码如下。

    @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中去。如下:

      原因是因为MappingJacksonJsonView代码中将数据map,mv.add(),还有form,还有错误提示等四项数据都加入Json,原程序只是过滤了错误提示,所以就留下了form数据。

    为此我们需要重写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

     

     

  • 相关阅读:
    css问题
    前端性能优化整理
    算法之排序
    浅谈webpack优化
    js设计模式
    事件模型
    浏览器缓存
    ucGUI 12864 从打点起
    ucGUI例程收藏
    Qt 自动搜索串口号列表
  • 原文地址:https://www.cnblogs.com/fangjins/p/4353991.html
Copyright © 2011-2022 走看看