SpringMVC的Controller中常用的三种返回值类型
1. 返回字符串
Controller方法返回字符串可以指定逻辑视图的名称,根据视图解析器为物理视图的地址
1.1 使用场景1 跳转到某个功能主页
通常的使用是在当前查找数据存储到Model中,也就是存储到Request域中,然后在跳转到的页面中取出使用
@Controller
@RequestMapping("/user")
public class UserController {
/**
* 请求参数的绑定
*/
@RequestMapping(value="/initUpdate")
public String initUpdate(Model model) {
// 模拟从数据库中查询的数据
User user = new User();
user.setUsername("张三");
user.setPassword("123");
user.setMoney(100d);
user.setBirthday(new Date());
model.addAttribute("user", user);
return "update";
}
}
<h3>修改用户</h3>
${ requestScope }
<form action="user/update" method="post">
姓名:<input type="text" name="username" value="${ user.username }"><br>
密码:<input type="text" name="password" value="${ user.password }"><br>
金额:<input type="text" name="money" value="${ user.money }"><br>
<input type="submit" value="提交">
</form>
注意我们在 springmvc.xml 中已经配置了视图名的前缀和后缀,所以前端控制器实际上获得的视图名为 prefix配置的值+ returnStr(方法返回值)+ suffix配置的值,即/WEB-INF/update.jsp,所以页面最终会跳转到网站根目录下的 update.jsp
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置视图名的默认前缀 -->
<property name="prefix" value="/WEB-INF/"></property>
<!-- 配置视图名的默认后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>
Question: 我想访问某个页面我直接去输入这个页面的地址 /userEdit.jsp 不就可以了吗,为什么还要先去请求 SpringMVC,再由它返回?
Answer: 对于 WebContent 这一层目录下面的页面,用户如果知道某个 jsp 页面可以直接访问。但是,通常情况下为了保证页面的安全,我们一般的做法是在 WebContent 这一层目录下只留一个引导页面(index.jsp)作为跳转,把网站相关的页面放入 WEB-INF文件夹下保护起来。因为放在 WEB-INF文件夹下的页面没有办法通过地址栏直接访问,只能通过后台的跳转来间接的访问,所以就需要请求 SpingMVC 来返回相应的页面
1.2 使用场景2 转发和重定向
String 类型的返回值除了返回一个可以被视图解析器解析的视图名以外,还可以返回 含有 redirect 或 forward 标签的字符串,我们使用它在登录成功的时候重定向到主页面,登录失败的时候转发回登录页面
@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
Question: 为什么登录成功需要重定向到主页面?
Answer: 因为转发客户端只发送了一次请求,如果登录成功转发到主页面的话,浏览器的地址栏中的 url 保留的是提交表单的请求,此时如果按 F5 刷新,就会造成二次提交表单,增大服务端的压力不说,还影响用户的体验,所以登录成功时要重定向到主页面,这样浏览器的地址栏保留的就是当前主页面的 url,无论怎么刷新都没事
Question: 为什么登录失败需要转发回登录页面?
Answer: 用户登录失败,我们需要给用户提供相应的提示信息,如:“用户名密码不匹配”。如果重定向到登录页面,用户就没有办法获取到服务端设置在 request 域里面的错误提示信息,因为重定向的本质是客户端发送了两次请求,在第二次请求里面获取不到第一次请求中的数据,所以没有办法给用户相应的提示信息。而转发只发送了一次请求,转发回登录页面的时候可以获取到请求中的数据,所以登录失败时需要转发会登录页面
2. 返回值是void
- 如果控制器的方法返回值编写成void,默认会跳转到页面名为当前请求的value的页面,如果默认查找的JSP页面没有找到,执行程序报404的异常
@RequestMapping(value="/initUpdate") ---> initUpdate
- 可以使用请求转发或者重定向跳转到指定的页面
public void test(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 转发到指定页面
request.getRequestDispatcher("xxx").forward(request, response);
// 或者重定向到指定页面
response.sendRedirect("/xxx");
}
请求转发和重定向的路径的写法
- 请求转发:是一次请求,不用再编写项目的路径,但是不再执行视图解析器
- 重定向:两次请求
3. ModelAndView
ModelAndView对象是Spring提供的一个对象,可以用来调整具体的JSP视图,可以携带数据,也是存在request域中
@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;
}
}
4. Model和ModelAndView
Model、ModelMap和ModelAndView的使用详解
控制器Controller执行业务逻辑,用于产生模型数据Model,而视图View用于渲染模型数据。
使用Model和ModelAndView这两个类在spring的视图解析时作用以及区别。
这两者之间有着很大的区别,具体就表现在Model只是用来传输数据的,并不会进行业务的寻址。ModelAndView 却是可以进行业务寻址的,就是设置对应的要请求的静态文件,这里的静态文件指的是类似jsp的文件。
其次,两者还有一个最大的区别,那就是Model是每一次请求可以自动创建,但是ModelAndView 是需要我们自己去new的。
Model:
Model是包含四个addAttribute 和一个 merAttribute方法的接口,其实现类为ExtendedModelMap,继承了ModelMap类
ModelMap:
实现了Map接口,包含Map方法。视图层通过request找到ModelMap中的数据
Model 和 ModelMap 的实例都是spirng mvc框架来自动创建并作为控制器方法参数传入,用户无需自己创建
而且需要return 返回指定的页面路径
public String toProvinceView(Model model, HttpSession session,) {
model.addAttribute("colModel", colModel);
model.addAttribute("colNames", colNames);
model.addAttribute("buttonName", buttonName);
return "statistic/StatisticChart";
}
ModelAndView:
是包含ModelMap 和视图对象的容器。正如名字暗示的一样既包含模型也包含视图,而ModelMap只是包含模型的信息
Model是每一次请求可以自动创建,但是ModelAndView 是需要我们自己去new的
public class CarListController implements Controller {
public ModelAndView handleRequest(HttpServletRequest arg0,
HttpServletResponse arg1) throws Exception {
CarManager carManager = new CarManager();
ModelAndView modelAndView = new ModelAndView("carList");
modelAndView.addObject("carList", carManager.getCarList());
return modelAndView;
}
}