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

相关文章
|
1月前
|
SQL JavaScript Java
springboot+springm vc+mybatis实现增删改查案例!
springboot+springm vc+mybatis实现增删改查案例!
26 0
|
1月前
|
SQL Java 数据库连接
挺详细的spring+springmvc+mybatis配置整合|含源代码
挺详细的spring+springmvc+mybatis配置整合|含源代码
42 1
|
15天前
|
监控 Java 数据库连接
Spring高手之路17——动态代理的艺术与实践
本文深入分析了JDK和CGLIB两种动态代理技术在Spring框架中的应用。讨论了动态代理的基础概念,通过实例展示了如何实现和应用这两种方法,并比较了它们的性能差异及适用场景。进一步,探讨了在动态代理中实现熔断限流和日志监控的策略,以及如何利用动态代理优化Spring应用的设计和功能。
30 6
Spring高手之路17——动态代理的艺术与实践
|
16天前
|
数据采集 前端开发 Java
数据塑造:Spring MVC中@ModelAttribute的高级数据预处理技巧
数据塑造:Spring MVC中@ModelAttribute的高级数据预处理技巧
23 3
|
16天前
|
存储 前端开发 Java
会话锦囊:揭示Spring MVC如何巧妙使用@SessionAttributes
会话锦囊:揭示Spring MVC如何巧妙使用@SessionAttributes
14 1
|
16天前
|
前端开发 Java Spring
数据之桥:深入Spring MVC中传递数据给视图的实用指南
数据之桥:深入Spring MVC中传递数据给视图的实用指南
31 3
|
25天前
|
前端开发 安全 Java
使用Java Web框架:Spring MVC的全面指南
【4月更文挑战第3天】Spring MVC是Spring框架的一部分,用于构建高效、模块化的Web应用。它基于MVC模式,支持多种视图技术。核心概念包括DispatcherServlet(前端控制器)、HandlerMapping(请求映射)、Controller(处理请求)、ViewResolver(视图解析)和ModelAndView(模型和视图容器)。开发流程涉及配置DispatcherServlet、定义Controller、创建View、处理数据、绑定模型和异常处理。
使用Java Web框架:Spring MVC的全面指南
|
1月前
|
敏捷开发 监控 前端开发
Spring+SpringMVC+Mybatis的分布式敏捷开发系统架构
Spring+SpringMVC+Mybatis的分布式敏捷开发系统架构
74 0
|
6月前
|
XML 前端开发 安全
Spring Mvc 拦截器详解
Spring Mvc 拦截器详解
68 0
|
5月前
|
前端开发 Java Spring
Spring MVC拦截器+注解方式实现防止表单重复提交
Spring MVC拦截器+注解方式实现防止表单重复提交