【Spring MVC学习笔记 三】深入实践Spring MVC控制器(下)

简介: 【Spring MVC学习笔记 三】深入实践Spring MVC控制器(下)

2 RestFul风格传参

什么是REST? REST(英文:Representational State Transfer,简称REST,意思:表述性状态转换,描述了一个架构样式的网络系统,比如web应用)。它是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件,它主要用于客户端和服务端交互类的软件。基于这个风格设计的软件可以更简介,更有层次,更易于实现缓存等机制。

什么是RESTFUL

什么是RESTFUL?满足REST约束条件和原则的应用程序或设计就是RESTFUL

  • 资源:互联网所有的事物都可以被抽象为资源
  • 资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。分别对应 添加、 删除、修改、查询。

更简化的理解,可以认为url路径不是写死的而是可以作为参数传递的

@PathVariable注解

例如我们的请求路径上包含了具体的资源id,那么就会请求到该资源,当然也可以附加一些操作,例如增删改查

@RequestMapping("/restfulParam/{id}")
    public void restfulParam(@PathVariable("id") Long id) {
        System.out.println("restfulParam参数为" + id);
    }

请求的url如下:

http://localhost:8081/base_annotation/user/restfulParam/10

返回结果如下:

3 请求附加信息获取

如果我们想获取请求的一些附加信息,例如 获取请求头中的User-Agent和Cookie信息,可以这么去获取:

@RequestMapping("/headerInfo")
    public void headerInfo(@RequestHeader("User-Agent") String userAgent, @CookieValue("JSESSIONID") String cookieName) {
        System.out.println("User-Agent:" + userAgent);
        System.out.println("cookieName" + cookieName);
    }

请求的url如下:

http://localhost:8081/base_annotation/user/headerInfo

返回结果如下:

4 method属性指定请求类型

我们一般默认的请求方式是Get,但其实也可以有别的请求方式,例如我们用POST去请求接口

@RequestMapping(value="/javaBeanParamPost",method = {RequestMethod.POST})
    public void javaBeanParamPost(User user) {
        System.out.println(user);
    }

请求的url如下:

http://localhost:8081/base_annotation/user/javaBeanParamPost?id=1&username=tml&age=30&accountIds=50&accountIds=80

返回结果如下:

当然会报错,因为我们方法需要的是POST请求,浏览器请求的是Get,不支持。除了用以上的方式还可以使用如下的注解直接表明方法的属性:

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

5 请求数据乱码问题处理

之前我们遇到乱码的问题都是需要进行手动设置的:

request.setCharacterEncoding("UTF-8");

现在有了框架,整体进行拦截,GET请求框架已经帮我们做好了,针对POST请求我们只需要在web.xml中加入如下配置即可解决乱码问题:

<filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <!-- 如果有编码格式,设置强制使用上面我们设置的编码格式-->
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Controller响应处理

聊完了请求我们再来聊聊响应,我们知道响应返回的一部分是页面,一部分是数据,所以我们分别聊聊数据的返回格式以及页面的跳转方式。

1 返回数据模型格式

我们先重新设置下userInfo.jsp文件来进行实验,其中Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。

<%--
  Created by IntelliJ IDEA.
  User: 13304
  Date: 2021/8/31
  Time: 11:47
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
modelMap: ${modelMap} <br>
model: ${model}<br>
modelAndView: ${modelAndView}<br>
</body>
</html>

1-1 返回ModelAndView

第一种就是我们使用的经典方式ModelAndView:

@RequestMapping("/getUserInfoByModelAndView")
    public ModelAndView getUserInfoByModelAndView(@RequestParam("name") String username,@RequestParam("age") String age) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("modelAndView", "username: "+username + " age: "+age+" "+LocalDate.now());
        modelAndView.setViewName("userInfo");
        return modelAndView;
    }

请求的url为:

http://localhost:8081/base_annotation/user/getUserInfoByModelAndView?name=tml&age=30

返回的结果为:

1-2 返回String类型-通过Model

第二种我们使用Model接口,视图不需要设置:

@RequestMapping("/getUserInfoByStringModel")
    public String getUserInfo(@RequestParam("name") String username,@RequestParam("age") String age, ModelMap modelMap) {
        modelMap.addAttribute("modelMap", "username"+username + "age"+age+LocalDate.now());
        return "userInfo";
    }

请求的url为:

http://localhost:8081/base_annotation/user/getUserInfoByStringModel?name=tml&age=30

返回的结果为:

1-3 返回String类型-通过ModelMap

最后一种我们使用ModelMap:

http://localhost:8081/base_annotation/user/getUserInfoByStringModelMap?name=tml&age=30

请求的url为:

@RequestMapping("/getUserInfoByStringModelMap")
    public String getUserInfoByStringModelMap(@RequestParam("name") String username,@RequestParam("age") String age, Model model) {
        model.addAttribute("model", "username: "+username + " age: "+age+" "+LocalDate.now());
        return "userInfo";
    }

返回的结果为:

除了提到的这几个Model,参数为Map以及List也可以传递,底层就是使用request.setAttribute()在赋值

2 返回页面跳转方式

学习Servlet的时候我们就知道页面的跳转有两种:请求转发和重定向。二者的区别就不再赘述了,在我的另一篇博客中有详细介绍:【Java Web编程 八】深入理解Servlet常用对象,之前是怎么实现的呢?

@Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //请求转发的写法
        RequestDispatcher requestDispatcher=request.getRequestDispatcher("/first.jsp");
        requestDispatcher.forward(request,response);
        //重定向的写法
        response.sendRedirect("/first.jsp");
    }

那么有了Spring MVC之后又是怎么实现的呢?

2-1 返回到指定页面

首先来看一个标准的返回到指定页面:

@RequestMapping("/getUserInfoByStringModel")
    public String getUserInfo(@RequestParam("name") String username, @RequestParam("age") String age, ModelMap modelMap) {
        modelMap.addAttribute("modelMap", "username: " + username + " age: " + age + " " + LocalDate.now());
        return "userInfo";
    }

2-2 请求转发到新页面

然后我们看下请求转发是如何实现的:

@RequestMapping("requestDispatch")
    public String requestDispatch(@RequestParam("name") String username,@RequestParam("age") String age, ModelMap modelMap) {
        modelMap.addAttribute("modelMap", "username: "+username + " age: "+age+" "+LocalDate.now());
        return "forward:/index.jsp";
    }

请求的url为:

http://localhost:8081/base_annotation/user/requestDispatch?name=tml&age=30

返回结果为:

可以看到共享对象被保留在了request的作用域内。

2-3 URL重定向到新页面

我们再来看下重定向如何实现:

@RequestMapping("requestRedirect")
    public String requestRedirect(@RequestParam("name") String username,@RequestParam("age") String age, ModelMap modelMap) {
        modelMap.addAttribute("modelMap", "username: "+username + " age: "+age+" "+LocalDate.now());
        return "redirect:/index.jsp";
    }

请求的url为:

http://localhost:8081/base_annotation/user/requestRedirect?name=tml&age=30

返回结果为:

可以看到共享对象由于发了两次请求丢失了。

项目结构及代码清单

整体实践完成后的项目结构如下图所示:

核心的实践代码如下UserController

package com.example.base_annotation.controller;
import com.example.base_annotation.controller.dto.Account;
import com.example.base_annotation.controller.dto.User;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Date;
@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("requestDispatch")
    public String requestDispatch(@RequestParam("name") String username, @RequestParam("age") String age, ModelMap modelMap) {
        modelMap.addAttribute("modelMap", "username: " + username + " age: " + age + " " + LocalDate.now());
        return "forward:/index.jsp";
    }
    @RequestMapping("requestRedirect")
    public String requestRedirect(@RequestParam("name") String username, @RequestParam("age") String age, ModelMap modelMap) {
        modelMap.addAttribute("modelMap", "username: " + username + " age: " + age + " " + LocalDate.now());
        return "redirect:/index.jsp";
    }
    @RequestMapping("/getUserInfoByStringModel")
    public String getUserInfo(@RequestParam("name") String username, @RequestParam("age") String age, ModelMap modelMap) {
        modelMap.addAttribute("modelMap", "username: " + username + " age: " + age + " " + LocalDate.now());
        return "userInfo";
    }
    @RequestMapping("/getUserInfoByStringModelMap")
    public String getUserInfoByStringModelMap(@RequestParam("name") String username, @RequestParam("age") String age, Model model) {
        model.addAttribute("model", "username: " + username + " age: " + age + " " + LocalDate.now());
        return "userInfo";
    }
    @RequestMapping("/getUserInfoByModelAndView")
    public ModelAndView getUserInfoByModelAndView(@RequestParam("name") String username, @RequestParam("age") String age) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("modelAndView", "username: " + username + " age: " + age + " " + LocalDate.now());
        modelAndView.setViewName("userInfo");
        return modelAndView;
    }
    @RequestMapping("/servletParam")
    public void servletParam(HttpServletRequest request, HttpServletResponse responser, HttpSession session) {
        System.out.println(request.getParameter("username"));
        System.out.println(request);
        System.out.println(responser);
        System.out.println(session);
    }
    @RequestMapping("/simpleParamMatch")
    public void simpleParamMatch(String username, int age) {
        System.out.println("username:" + username);
        System.out.println("age:" + age);
    }
    @RequestMapping("/simpleParamDiff")
    public void simpleParamDiff(@RequestParam("name") String username, @RequestParam(value = "age") Integer age) {
        System.out.println("username:" + username);
        System.out.println("age:" + age);
    }
    @RequestMapping("/restfulParam/{id}")
    public void restfulParam(@PathVariable("id") Long id) {
        System.out.println("restfulParam参数为" + id);
    }
    @RequestMapping("/arrayParam")
    public void arrayParam(Long[] accountIds) {
        System.out.println(Arrays.asList(accountIds));
    }
    @RequestMapping("/listParam")
    public void listParam(User user) {
        System.out.println(user.getAccountIds());
    }
    @RequestMapping("/dateParam")
    public void dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
        System.out.println(date);
    }
    @RequestMapping("/javaBeanParam")
    public void javaBeanParam(User user) {
        System.out.println(user);
    }
    @RequestMapping("/javaBeanModelAttributeParam")
    public String javaBeanModelAttributeParam(@ModelAttribute("userAllies") User user) {
        System.out.println(user);
        return "userInfo";
    }
    @RequestMapping("/headerInfo")
    public void headerInfo(@RequestHeader("User-Agent") String userAgent, @CookieValue("JSESSIONID") String cookieName) {
        System.out.println("User-Agent:" + userAgent);
        System.out.println("cookieName" + cookieName);
    }
    @InitBinder("user")
    public void initBinderUserType(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("user.");
    }
    @InitBinder("account")
    public void initBinderCatType(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("account.");
    }
    @RequestMapping("/multiModelParam")
    public void multiModelParam(User user, Account account) {
        System.out.println(user);
        System.out.println(account);
    }
    @RequestMapping(value = "/javaBeanParamPost", method = {RequestMethod.POST})
    public void javaBeanParamPost(User user) {
        System.out.println(user);
    }
}

总结一下

整体深入的实践了一遍Spring MVC的控制器,其实就是比照Servlet之前的实现方式进行了一下类比实现,这样可以看出框架的方便之处,为什么说Spring MVC和Spring兼容好呢?还记得学习Spring注解开发时候的Controller么,不就是一种Compnent么。再去从实践的角度看框架,整体上看一个Controller就像我们的某个小模块,这个小模块的各种方法是模块的实现,这和现实也比较契合,每个方法就像之前的每个Servlet里的doGet或doPost,现在我们不需要再为了一个模块写一堆Servlet了,用一个映射的方法代替就行了。再看入参,入参可以统一接受过滤器处理整体解决乱码问题,并且对各种入参类型都有很好的兼容,各种注解代替了我们的各种数据转换实现,给我们省心不少,返回结果页面的显示和跳转也很方便了,使用预置的Model不需要再将对象放到作用域了,因为框架已经替我们做好了,而请求转发和重定向也仅仅只是在返回的字符串上加个单词即可,这就是框架的魅力吧。

相关文章
|
6天前
|
前端开发 Java Spring
Spring MVC 是如何对对象参数进行校验的
【6月更文挑战第4天】对象参数校验是使用 SpringMVC 时常用的功能,这篇文章尝试分析了,Spring 是如何实现这一功能的。
17 5
|
30天前
|
前端开发 Java 测试技术
Java一分钟之Spring MVC:构建Web应用
【5月更文挑战第15天】Spring MVC是Spring框架的Web应用模块,基于MVC模式实现业务、数据和UI解耦。常见问题包括:配置DispatcherServlet、Controller映射错误、视图解析未设置、Model数据传递遗漏、异常处理未配置、依赖注入缺失和忽视单元测试。解决这些问题可提升代码质量和应用性能。注意配置`web.xml`、`@RequestMapping`、`ViewResolver`、`Model`、`@ExceptionHandler`、`@Autowired`,并编写测试用例。
309 3
|
30天前
|
设计模式 存储 前端开发
MVC(模型-视图-控制器)是一种在Web应用程序开发中广泛使用的软件设计模式
【5月更文挑战第12天】MVC模式是Web应用开发中的常见设计模式,将逻辑、数据和界面分离,提升代码可维护性和重用性。模型处理数据逻辑,视图展示数据,控制器协调用户输入与模型视图交互。优点包括代码分离、易维护、可扩展和组件重用,促进高效灵活的开发。
18 2
|
23小时前
|
前端开发 Java Spring
Spring MVC 请求处理流程
Spring MVC 请求处理流程
4 0
|
4天前
|
JSON 前端开发 Java
Spring MVC 级联对象参数校验
【6月更文挑战第6天】在 Spring MVC 的使用过程中,我们会发现很多非常符合直觉的功能特性,但往往我们会习惯这种「被照顾得很好」的开发方式,依靠直觉去判断很多功能特性的用法。
9 1
|
7天前
|
前端开发 Java Spring
自定义 Spring MVC Controller 方法参数处理
【6月更文挑战第3天】在 Spring MVC Controller 的方法参数,Spring 会自动为我们注入一些特殊的参数值,比如 HttpServletRequest、HttpServletResponse 等对象,或者 HTTP 请求参数。
47 0
|
8天前
|
JSON 前端开发 Java
Spring第四课,MVC终章,应用分层的好处,总结
Spring第四课,MVC终章,应用分层的好处,总结
|
10天前
|
JSON 前端开发 Java
Spring Web MVC入门(3)——响应
Spring Web MVC入门(3)——响应
12 1
|
10天前
|
存储 前端开发 Java
Spring Web MVC入门(2)——请求(下)
Spring Web MVC入门(2)——请求
16 0
|
10天前
|
JSON 前端开发 Java
Spring Web MVC入门(2)——请求(上)
Spring Web MVC入门(2)——请求
10 0