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 对象。希望文章对大家能有所帮助。

猜你喜欢

转载自blog.csdn.net/a909301740/article/details/80410660
今日推荐