SpringMVC 的 Controller 返回各種視圖的處理方式

SpringMVC 的 Controller 可以返回各種各樣的視圖。比如 JSP, JSON, Velocity, FreeMarker, XML, PDF, Excel, 還有Html字符流 等等。那它們該如何的進行處理的呢?這裏就涉及到 各種視圖(View)對應的各種視圖解析器(ViewResolver). 基本上上面說的每一種視圖就對應用一種視圖解析器來處理。基本上各種視圖解析器大致上可以分爲兩類:一是基於URL的視圖解析器;一種是其它解析器;所謂的UrlBasedViewResolver,就是將返回值作爲最終視圖的url的一部分,然後和相關配置組合起來,就是最終的視圖地址url. 我們看下UrlBasedViewResolver的解析器有哪些:

1. 返回JSP

返回JSP是最簡單的,JSP視圖的視圖解析器爲 InternalResourceViewResolver,也是一個UrlBasedViewResolver解析器。其對應的解析器的配置一般如下:

<bean id="viewResolver" 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"/>
</bean>

使用該例子,我們好好理解下什麼是 “基於URL” 的視圖解析器,比如我們的 Controller 中最後的返回的處理代碼爲: return "index"; 那麼“基於URL” 的視圖解析器就會將返回值 “index” 作爲最後視圖的URL的一部分,然後結合上面的配置 <property name="prefix" value="/WEB-INF/jsp/"/> 和 <property name="suffix" value=".jsp"/>,最後得到最終的URL: "/WEB-INF/jsp/" + "index" + ".jsp" == "/WEB-INF/jsp/index.jsp"

這就是所謂的 “基於URL” 的視圖解析器的工作方式。

2. 返回 HTML 頁面

我們知道在Servlet中,我們是可以直接在其中打印輸出HTML字符流到最終頁面,比如下面的代碼來自阿里巴巴的支付寶的接入示例中的代碼:

複製代碼
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");        
                // ... ...        
        //建立請求
        String sHtmlText = AlipaySubmit.buildRequest(sParaTemp,"get","確認");
        response.getWriter().println(sHtmlText);
    }
複製代碼
複製代碼
    /**
     * 建立請求,以表單HTML形式構造(默認)
     * @param sParaTemp 請求參數數組
     * @param strMethod 提交方式。兩個值可選:post、get
     * @param strButtonName 確認按鈕顯示文字
     * @return 提交表單HTML文本
     */
    public static String buildRequest(Map<String, String> sParaTemp, String strMethod, String strButtonName) {
        //待請求參數數組
        Map<String, String> sPara = buildRequestPara(sParaTemp);
        List<String> keys = new ArrayList<String>(sPara.keySet());

        StringBuffer sbHtml = new StringBuffer();
        sbHtml.append("<!doctype html><html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
        sbHtml.append("<title>支付寶即時到賬交易接口</title></head><body>");
        sbHtml.append("<form id=\"alipaysubmit\" name=\"alipaysubmit\" action=\"" + ALIPAY_GATEWAY_NEW
                      + "_input_charset=" + AlipayConfig.input_charset + "\" method=\"" + strMethod
                      + "\">");

        for (int i = 0; i < keys.size(); i++) {
            String name = (String) keys.get(i);
            String value = (String) sPara.get(name);

            sbHtml.append("<input type=\"hidden\" name=\"" + name + "\" value=\"" + value + "\"/>");
        }

        //submit按鈕控件請不要含有name屬性
        sbHtml.append("<input type=\"submit\" value=\"" + strButtonName + "\" style=\"display:none;\"></form>");
        sbHtml.append("<script>document.forms['alipaysubmit'].submit();</script>");
        sbHtml.append("</body></html>");
        return sbHtml.toString();
    }
複製代碼

很顯然,Servlet直接將HTML的字符流輸出到了瀏覽器端,那麼在SpringMVC中該如何做呢?其實在SpringMVC中我們也是可以如下實現的:

複製代碼
    @RequestMapping(value="/getPage")
    public void writeSubmitHtml(Reader reader, Writer writer, HttpSession session) throws IOException {
        User user = (User) session.getAttribute(ConstantConfig.LONGIN_USER);
        StringBuffer sbHtml = new StringBuffer();
        sbHtml.append("<!doctype html><html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
        sbHtml.append("<title>支付寶即時到賬交易接口</title></head><body>"+ user.getNo() +"</body></html>");
        writer.write(sbHtml.toString());  
    }
複製代碼

我們看到我們直接使用了參數 Writer writer,返回值爲 void, 其實參數 Writer writer 也可以換成 PrintWriter writer; 直接寫出HTML的字符流

我們也知道在Servlet中,我們是可以直接forward或者redirecit到html頁面,所以我們也可以如下在springmvc中返回到html頁面:

    @RequestMapping(value="/htmlView")
    public void htmlView(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        // ...
        request.getRequestDispatcher("index.html").forward(request, response);
     //response.sendRedirect("http://www.baidu.com");
    }

這裏,我們體會到了:springmvc他是建立在servlet之上的,所以在servlet中能夠做到的,同樣在springmvc一樣有效

3. 返回JSON格式

返回JSON格式在SpringMVC中有多種處理方式,一種是使用SpirngMVC自帶的 MappingJackson2JsonView 來處理,一種是自己寫代碼將返回值JSON格式化,然後直接用PrintWrite類型的對象寫出就行了。

1)直接用PrintWrite類型的對象寫出JSON格式

複製代碼
    @RequiresRoles(value={"student"})
    @RequestMapping(value="/queryScoreForStudent")
    public void queryScoreForStudent(ScoreQueryParam param, HttpSession sesion, PrintWriter printWriter){
        Student student = (Student)sesion.getAttribute(ConstantConfig.LONGIN_STUDENT);
        param.setStudentId(student.getId());
        PageBean<StudentScore> scoreList = this.studentCourseService.queryScoreForStudent(param);
        if(scoreList != null && scoreList.getSize() > 0){
            Map<String, Object> map = new HashMap<>();
            map.put("result", "ok");
            map.put("data", scoreList);
            printWriter.write(JSON.toJSONString(map));
        }
    }
複製代碼

如上代碼所示,我們在方法中加入了 PrintWriter printWriter 參數,最後的返回結果使用了fastjson庫來格式化最後的返回的對象map. 然後使用printWriter寫出,就行了。我們看到方法上的註解並沒有使用 @ResponseBody. 當然最好這裏是在方法上加上 @ResponseBody,但是因爲返回的map已經是JSON格式的,所以並不需要配置 MappingJackson2JsonView 

2)使用MappingJackson2JsonView 配合@ResponseBody來返回JSON格式

首先需要進行相關的視圖解析器的配置:

複製代碼
     <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="mediaTypes">
            <map>
                <entry key="atom" value="application/atom+xml"/>
                <entry key="html" value="text/html"/>
                <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="prefix" value="/"/>
                    <property name="suffix" value=".jsp"/>
                </bean>
            </list>
        </property>
        <property name="defaultViews">
            <list>
                <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" />
            </list>
        </property>
    </bean>
複製代碼

這裏用到了 ContentNegotiatingViewResolver ,“內容協商視圖解析器”,其實就是根據返回什麼類型的視圖,就協商使用哪種視圖解析器。如果返回jsp就使用InternalResourceViewResolver視圖解析器,如果返回JSON格式就使用MappingJackson2JsonView來處理。如此而已。在 <property name="viewResolvers"> 下的<list> 標籤下,還可以加入其他的各種視圖解析器的配置。

配置了 MappingJackson2JsonView 之後,就沒有必要在自己手動 JSON格式化了,上面的例子,可以改成:

複製代碼
    @RequiresRoles(value={"student"})
    @RequestMapping(value="/queryScoreForStudent")
    @ResponseBody
    public Map<String, Object> queryScoreForStudent(ScoreQueryParam param, HttpSession sesion){
        Student student = (Student)sesion.getAttribute(ConstantConfig.LONGIN_STUDENT);
        param.setStudentId(student.getId());
        PageBean<StudentScore> scoreList = this.studentCourseService.queryScoreForStudent(param);
        System.out.println(JSON.toJSONString(scoreList));
        if(scoreList != null && scoreList.getSize() > 0){
            Map<String, Object> map = new HashMap<>();
            map.put("result", "ok");
            map.put("data", scoreList);
            return map;
        }
    }
複製代碼

其他格式Velocity, FreeMarker, XML, PDF, Excel等視圖的處理,後面有時間在補上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章