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,最後,展現在我們面前的就是一個登入頁面。
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 物件。希望文章對大家能有所幫助。