1. 程式人生 > >SpringMVC的Controller中常用的三種返回值型別

SpringMVC的Controller中常用的三種返回值型別

SpringMVC 通過註解實現的 Controller 常用的返回值型別主要有三種:返回字串,返回 ModelAndView,返回 void。

先看一下專案的目錄結構:
這裡寫圖片描述

再看一下 springmvc.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"
    xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"
>
<!-- 開啟spring的元件掃描,自動載入bean --> <context:component-scan base-package="com.lyu.qjl.interview.controller" /> <!-- 可以用mvc的註解驅動 --> <mvc:annotation-driven /> <!-- 配置檢視解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
>
<!-- 配置檢視名的預設字首 --> <property name="prefix" value="/"></property> <!-- 配置檢視名的預設字尾 --> <property name="suffix" value=".jsp"></property> </bean> </beans>

返回字串

應用場景1:直接返回檢視名稱,例如跳轉到某個功能主頁。

下面的 Controller 是用來處理使用者頁面的請求,gotoUserEdit 方法是用來處理跳轉到使用者編輯頁面的請求。

package com.lyu.qjl.interview.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.lyu.qjl.interview.entity.User;
import com.lyu.qjl.interview.vo.UserVO;

/**
 * 類名稱:處理使用者請求的handler
 * 全限定性類名: com.lyu.qjl.interview.controller.UserController
 * @author 曲健磊
 * @date 2018年5月21日下午7:24:07
 * @version V1.0
 */
@Controller
public class UserController{

    /**
     * 跳轉到編輯使用者頁面
     * @return
     */
    @RequestMapping("/gotoUserEdit")
    public String gotoUserEdit() {
        return "userEdit";
    }

}

這個 gotoUserEdit 方法匹配 /gotoUserEdit 請求。返回值為一個 userEdit 字串,表示的是一個檢視名,但是由於我們在 springmvc.xml 中已經配置了檢視名的字首和字尾,所以前端控制器實際上獲得的檢視名為 prefix配置的值 + returnStr(方法返回值) + suffix配置的值,即 /userEdit.jsp,所以頁面最終會跳轉到網站根目錄下的 userEdit.jsp。

Question:我想訪問某個頁面我直接去輸入這個頁面的地址 /userEdit.jsp 不就可以了嗎,為什麼還要先去請求 SpringMVC,再由它返回?

Answer:對於 WebContent 這一層目錄下面的頁面,使用者如果知道某個 jsp 頁面可以直接訪問。但是,通常情況下為了保證頁面的安全,我們一般的做法是在 WebContent 這一層目錄下只留一個引導頁面(index.jsp)作為跳轉,把網站相關的頁面放入 WEB-INF 資料夾下保護起來。因為放在 WEB-INF 資料夾下的頁面沒有辦法通過位址列直接訪問,只能通過後臺的跳轉來間接的訪問,所以就需要請求 SpingMVC 來返回相應的頁面。

實際應用中的 web 頁面的目錄結構:
這裡寫圖片描述

注:login.jsp, main.jsp, userEdit.jsp, userList.jsp, userMain.jsp 都是放在 WEB-INF 這層目錄下的,只有 index.jsp 是放在 WebContext 這一層下的。

測試:直接訪問 WEB-INF 資料夾下的 login.jsp 頁面的結果如下:
這裡寫圖片描述

這樣我們就沒有辦法直接從位址列去訪問 WEB-INF 下的 login.jsp 頁面了,只能通過 SpringMVC 來進行頁面的跳轉了。

這裡還需要注意一點:由於頁面已經放到了 WEB-INF 目錄下,所以 springmvc.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"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    ...

    <!-- 配置檢視解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置檢視名的預設字首 -->
        <property name="prefix" value="/WEB-INF/"></property>
        <!-- 配置檢視名的預設字尾 -->
        <property name="suffix" value=".jsp"></property>
    </bean>

</beans>

擴充套件:

大家注意到那個放在 WebContent 那一層目錄下的 index.jsp 頁面了嗎?它在實際應用中有什麼作用呢?

它一般配置為專案的歡迎頁面,即 web.xml 中的 welcome-file。當我們在位址列輸入 localhost:8080/interview 的時候,首先就會訪問該頁面,然後由該頁面向後臺發請求,通過後臺的 Controller 跳轉到被 WEB-INF 資料夾保護起來的登入頁面 login.jsp,最後,展現在我們面前的就是一個登入頁面。
login.jsp
index.jsp 中的內容如下:

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%
// 轉發一個 "/login" 請求給後臺的 Controller,由 Controller 返回 WEB-INF 目錄下的 login.jsp 頁面
request.getRequestDispatcher("/login").forward(request, response);
%>

應用場景2:在登入成功的時候重定向到主頁面,登入失敗的時候轉發回登入頁面

String 型別的返回值除了返回一個可以被檢視解析器解析的檢視名以外,還可以返回 含有 redirect 或 forward 標籤的字串,如下:

package com.lyu.qjl.interview.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * 類名稱:用於處理登入請求的處理器
 * 全限定性類名: com.lyu.qjl.interview.controller.LoginController
 * @author 曲健磊
 * @date 2018年5月21日下午7:02:03
 * @version V1.0
 */
@Controller
public class LoginController{

    @RequestMapping("/loginUser")
    public String login(String username, String password, Model model) throws Exception {
        if (username != null && password != null) {
            if ("admin".equals(username) && "123".equals(password)) {
                // 登入成功,重定向到主頁面
                return "redirect:/main";
            } else {
                // 向 request 域中設定錯誤提示資訊
                model.addAttribute("loginFlag", "使用者名稱或密碼錯誤");
                // 登入失敗,轉發到登入頁面
                return "forward:/login";
            }
        } else {
            // 只是把模型放到request域裡面,並沒有真正的安排好
            model.addAttribute("loginFlag", "使用者名稱或密碼錯誤");
            // 登入失敗,轉發到登入頁面
            return "forward:/login";
        }
    }

    @RequestMapping("/login")
    // 對外部提供介面的時候需要指定某些引數
    public String login() {
        return "login";
    }

    @RequestMapping("/main")
    public String main() {
        return "main";
    }
}

redirect:/main 表示這是一個重定向到 /main 的請求, forward:/login 表示這是一個轉發到 /login 的請求。

注意:如果在返回的字串前加上了 redirect 或者 forward 標籤,就不會走檢視解析器,而是直接轉發或重定向到指定的路徑,所以對於重定向標籤後面就不能加 WEB-INF ,因為重定向是客戶端重新發送的請求,WEB-INF 目錄下的頁面仍然訪問不到,但是對於轉發標籤的話後面可以跟 WEB-INF。

以上就是 Controller 的返回值為 String 時的另一種常見用途。

Question:為什麼登入成功需要重定向到主頁面?

Answer:因為轉發客戶端只發送了一次請求,如果登入成功轉發到主頁面的話,瀏覽器的位址列中的 url 保留的是提交表單的請求,此時如果按 F5 重新整理,就會造成二次提交表單,增大服務端的壓力不說,還影響使用者的體驗,所以登入成功時要重定向到主頁面,這樣瀏覽器的位址列保留的就是當前主頁面的 url,無論怎麼重新整理都沒事。
這裡寫圖片描述

Question:為什麼登入失敗需要轉發回登入頁面?

Answer:使用者登入失敗,我們需要給使用者提供相應的提示資訊,如:“使用者名稱密碼不匹配”。如果重定向到登入頁面,使用者就沒有辦法獲取到服務端設定在 request 域裡面的錯誤提示資訊,因為重定向的本質是客戶端傳送了兩次請求,在第二次請求裡面獲取不到第一次請求中的資料,所以沒有辦法給使用者相應的提示資訊。而轉發只發送了一次請求,轉發回登入頁面的時候可以獲取到請求中的資料,所以登入失敗時需要轉發會登入頁面。
這裡寫圖片描述

返回ModelAndView

應用場景:其實這種返回值的應用場景比較隨意,既可以用來做頁面的跳轉,也可以在跳轉到頁面的時候攜帶一些資料,例如:查詢使用者列表。

package com.lyu.qjl.interview.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.lyu.qjl.interview.entity.User;
import com.lyu.qjl.interview.vo.UserVO;

/**
 * 類名稱:處理使用者請求的handler
 * 全限定性類名: com.lyu.qjl.interview.controller.UserController
 * @author 曲健磊
 * @date 2018年5月21日下午7:24:07
 * @version V1.0
 */
@Controller
public class UserController{

    @RequestMapping("/getUserList")
    public ModelAndView getUserList() {
        System.out.println("進入getUseList");
        ModelAndView modelAndView = new ModelAndView();

        List<User> userList = new ArrayList<User>();
        User user1 = new User("arry", 18, "男");
        user1.setUserId(1L);
        User user2 = new User("cc", 28, "女");
        user2.setUserId(2L);
        User user3 = new User("dd", 38, "男");
        user3.setUserId(3L);
        userList.add(user1);
        userList.add(user2);
        userList.add(user3);

        // 將資料放入modelAndView物件
        modelAndView.addObject("userList", userList);

        // 將返回的邏輯檢視名稱放入modelAndView物件
        modelAndView.setViewName("userList");

        return modelAndView;
    }
}

如果想在跳轉到某個頁面的時候攜帶一些資料,呼叫 ModelAndView 物件的 addObject 方法設定一些屬性即可,不想帶資料也可以不設定,但是必須得設定檢視名 setViewName ,要不然它哪知道返回到哪個頁面去呢?

返回void

如果是返回 void 會,就必須要在方法的引數中新增 HttpServletRequest 和 HttpServletResponse 來進行頁面的跳轉,程式碼如下:

public void test(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 轉發到指定頁面
    request.getRequestDispatcher("xxx").forward(request, response);
    // 或者重定向到指定頁面
    response.sendRedirect("/xxx");
}

其實,從本質上來說前面兩種方法(String,ModelAndView)就是間接呼叫的 request 和 response 這個兩個物件來進行頁面的跳轉,以及資料的儲存。

稍微的總結一下,這篇文章主要講了 SpringMVC 的 Controller 的三種返回值型別:String,ModelAndView,void。在講 String 的時候擴充套件了一些 redirect 和 forward 標籤,提了一下在實際應用中轉發和重定向的用處,以及實際專案中需要把頁面放入 WEB-INF 目錄裡保護起來。ModelAndView 可以攜帶資料也可以不攜帶資料,以及前兩種方法本質上都是在操作 request 和 response 物件。希望文章對大家能有所幫助。