【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不需要再将对象放到作用域了,因为框架已经替我们做好了,而请求转发和重定向也仅仅只是在返回的字符串上加个单词即可,这就是框架的魅力吧。

相关文章
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
本文介绍了 `@RequestParam` 注解的使用方法及其与 `@PathVariable` 的区别。`@RequestParam` 用于从请求中获取参数值(如 GET 请求的 URL 参数或 POST 请求的表单数据),而 `@PathVariable` 用于从 URL 模板中提取参数。文章通过示例代码详细说明了 `@RequestParam` 的常用属性,如 `required` 和 `defaultValue`,并展示了如何用实体类封装大量表单参数以简化处理流程。最后,结合 Postman 测试工具验证了接口的功能。
85 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
深入理解 Spring Boot 中日期时间格式化:@DateTimeFormat 与 @JsonFormat 完整实践
在 Spring Boot 开发中,日期时间格式化是前后端交互的常见痛点。本文详细解析了 **@DateTimeFormat** 和 **@JsonFormat** 两个注解的用法,分别用于将前端传入的字符串解析为 Java 时间对象,以及将时间对象序列化为指定格式返回给前端。通过完整示例代码,展示了从数据接收、业务处理到结果返回的全流程,并总结了解决时区问题和全局配置的最佳实践,助你高效处理日期时间需求。
147 0
Spring Boot 注册登录系统:问题总结与优化实践
在Spring Boot开发中,注册登录模块常面临数据库设计、密码加密、权限配置及用户体验等问题。本文以便利店销售系统为例,详细解析四大类问题:数据库字段约束(如默认值缺失)、密码加密(明文存储风险)、Spring Security配置(路径权限不当)以及表单交互(数据丢失与提示不足)。通过优化数据库结构、引入BCrypt加密、完善安全配置和改进用户交互,提供了一套全面的解决方案,助力开发者构建更 robust 的系统。
45 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestBody
`@RequestBody` 是 Spring 框架中的注解,用于将 HTTP 请求体中的 JSON 数据自动映射为 Java 对象。例如,前端通过 POST 请求发送包含 `username` 和 `password` 的 JSON 数据,后端可通过带有 `@RequestBody` 注解的方法参数接收并处理。此注解适用于传递复杂对象的场景,简化了数据解析过程。与表单提交不同,它主要用于接收 JSON 格式的实体数据。
105 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@PathVariable
`@PathVariable` 是 Spring Boot 中用于从 URL 中提取参数的注解,支持 RESTful 风格接口开发。例如,通过 `@GetMapping(&quot;/user/{id}&quot;)` 可以将 URL 中的 `{id}` 参数自动映射到方法参数中。若参数名不一致,可通过 `@PathVariable(&quot;自定义名&quot;)` 指定绑定关系。此外,还支持多参数占位符,如 `/user/{id}/{name}`,分别映射到方法中的多个参数。运行项目后,访问指定 URL 即可验证参数是否正确接收。
77 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestMapping
@RequestMapping 是 Spring MVC 中用于请求地址映射的注解,可作用于类或方法上。类级别定义控制器父路径,方法级别进一步指定处理逻辑。常用属性包括 value(请求地址)、method(请求类型,如 GET/POST 等,默认 GET)和 produces(返回内容类型)。例如:`@RequestMapping(value = &quot;/test&quot;, produces = &quot;application/json; charset=UTF-8&quot;)`。此外,针对不同请求方式还有简化注解,如 @GetMapping、@PostMapping 等。
84 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RestController
本文主要介绍 Spring Boot 中 MVC 开发常用的几个注解及其使用方式,包括 `@RestController`、`@RequestMapping`、`@PathVariable`、`@RequestParam` 和 `@RequestBody`。其中重点讲解了 `@RestController` 注解的构成与特点:它是 `@Controller` 和 `@ResponseBody` 的结合体,适用于返回 JSON 数据的场景。文章还指出,在需要模板渲染(如 Thymeleaf)而非前后端分离的情况下,应使用 `@Controller` 而非 `@RestController`
63 0
Spring Cloud OpenFeign详解与实践
总结起来说,Spring Cloud OpenFeign提供了一种简单易懂且高效的方式去实现微服务之间通信.它隐藏了许多复杂性,并且允许开发者以声明式方式编写HTTP客户端代码.如果你正在开发基于Spring Cloud 的微服务架构系统,Spring Cloud Open Feign是一个非常好用且强大工具.
122 33
深入理解 Spring Boot 中日期时间格式化:@DateTimeFormat 与 @JsonFormat 完整实践
在 Spring Boot 开发中,处理前后端日期交互是一个常见问题。本文通过 **@DateTimeFormat** 和 **@JsonFormat** 两个注解,详细讲解了如何解析前端传来的日期字符串以及以指定格式返回日期数据。文章从实际案例出发,结合代码演示两者的使用场景与注意事项,解决解析失败、时区偏差等问题,并提供全局配置与局部注解的实践经验。帮助开发者高效应对日期时间格式化需求,提升开发效率。
180 2
DDD四层架构和MVC三层架构的个人理解和学习笔记
领域驱动设计(DDD)是一种以业务为核心的设计方法,与传统MVC架构不同,DDD将业务逻辑拆分为应用层和领域层,更关注业务领域而非数据库设计。其四层架构包括:Interface(接口层)、Application(应用层)、Domain(领域层)和Infrastructure(基础层)。各层职责分明,避免跨层调用,确保业务逻辑清晰。代码实现中,通过DTO、Entity、DO等对象的转换,结合ProtoBuf协议,完成请求与响应的处理流程。为提高复用性,实际项目中可增加Common层存放公共依赖。DDD强调从业务出发设计软件,适应复杂业务场景,是微服务架构的重要设计思想。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等