SpringMVC
参考于尚硅谷视频ssm框架
一、SpringMVC简介
1.1 介绍
Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就包含在Spring Framework中。正式名称“Spring Web MVC”来自其源模块的名称( spring-webmvc
),但它通常被称为“Spring MVC”。
在控制层框架历经Strust、WebWork、Strust2等诸多产品的历代更迭之后,目前业界普遍选择了SpringMVC作为Java EE项目表述层开发的首选方案。之所以能做到这一点,是因为SpringMVC具备如下显著优势:
-
Spring 家族原生产品,与IOC容器等基础设施无缝对接
-
表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
-
代码清新简洁,大幅度提升开发效率
-
内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
-
性能卓著,尤其适合现代大型、超大型互联网项目要求
1.2 调用流程理解和核心组件
Spring MVC与许多其他Web框架一样,是围绕前端控制器模式设计的,其中中央 Servlet
DispatcherServlet
做整体请求处理调度!
除了DispatcherServlet
SpringMVC还会提供其他特殊的组件协作完成请求处理和响应呈现。
SpringMVC处理请求流程:
(1)用户发送请求至前端控制器DispatcherServlet
(2)DispatcherServlet收到请求调用HandlerMapping处理器映射器。
(3)处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果 有则生成)一并返回给DispatcherServlet。
(4)DispatcherServlet调用HandlerAdapter处理器适配器。
(5)HandlerAdapter经过适配调用具体的Handler处理器(Controller,也叫后端控制器)。Controller执行完成返回 ModelAndView。
(6)HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
(7)DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
(8)ViewReslover解析后返回具体View视图。
(9)DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
(10)DispatcherServlet响应用户。
SpringMVC涉及组件理解:
-
DispatcherServlet : SpringMVC提供,我们需要使用web.xml配置使其生效,它是整个流程处理的核心,所有 请求都经过它的处理和分发!
-
HandlerMapping : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它内部缓存handler(controller方法)和handler访问路径数据,被DispatcherServlet调用,用于查找路径对应的handler!
-
HandlerAdapter : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它可以处理请求参数和处理响应数据数据,每次DispatcherServlet都是通过handlerAdapter间接调用handler,他是handler和DispatcherServlet之间的适配器!
-
Handler : handler又称处理器,他是Controller类内部的方法简称,是由我们自己定义,用来接收参数,向后调用业务,最终返回响应结果!
-
ViewResovler : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效!视图解析器主要作用简化模版视图页面查找的,但是需要注意,前后端分离项目,后端只返回JSON数据,不返回页面,那就不需要视图解析器!所以,视图解析器,相对其他的组件不是必须的!
1.3 使用之前的配置
导入依赖(jdk17环境)
<properties>
<spring.version>6.0.6</spring.version>
<servlet.api>9.1.0</servlet.api>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- springioc相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- web相关依赖 -->
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>${servlet.api}</version>
<scope>provided</scope>
</dependency>
<!-- springwebmvc相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- json处理依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
<!-- jsp需要依赖! jstl-->
<dependency>
<groupId>jakarta.servlet.jsp.jstl</groupId>
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
<version>3.0.0</version>
</dependency>
<!-- get/set/toString-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
</dependencies>
Spring MVC核心组件配置类 (config包下)
package com.cqie.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
/**
* @Title: MvcConfig
* @Author helong
* @Package com.cqie.config
* @Date 2023/11/20 16:46
* @description:配置类,controller handlerMapping handlerAdapater加入ioc容器
*/
@Configuration//配置类
@ComponentScan("com.cqie")//扫描包
@EnableWebMvc //给handlerAdapter配置了json转化器 @EnableWebMvc
public class MvcConfig {
/*
@EnableWebMvc相当于
↓
@Bean
public RequestMappingHandlerMapping handlerMapping(){
return new RequestMappingHandlerMapping();
}
@Bean
public RequestMappingHandlerAdapter handlerAdapter(){
return new RequestMappingHandlerAdapter();
}
*/
}
SpringMVC环境搭建 (config包下)
package com.cqie.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* @Title: SpringMvcInit
* @Author helong
* @Package com.cqie.config
* @Date 2023/11/20 16:49
* @description: 初始化类
*/
public class SpringMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
/*springmvc 需要组件的配置类 */
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{MvcConfig.class};
}
/*
* Servlet DispatcherServlet
* 设置dispatcherServlet的处理路径!
* 一般情况下为 / 代表处理所有请求!
**/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
二、SpringMVC接收数据
2.1 访问路径设置
@RequestMapping注解的作用就是将请求的 URL 地址和处理请求的方式(handler方法)关联起来,建立映射 关系。
SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的方法来处理这个请求。
精准路径匹配
在@RequestMapping注解指定 URL 地址时,不使用任何通配符,按照请求地址进行精确匹配。
@Controller
public class UserController {
/**
* 精准设置访问地址 /user/login
*/
@RequestMapping("/user/login")
@ResponseBody//直接返回字符串给前端,不要找视图解析器!!!
public String login(){
System.out.println("UserController.login");
return "login success!!";
}
/**
* 精准设置访问地址 /user/register
*/
@RequestMapping("/user/register")
@ResponseBody
public String register(){
System.out.println("UserController.register");
return "register success!!";
}
}
模糊路径匹配
在@RequestMapping注解指定 URL 地址时,通过使用通配符,匹配多个类似的地址。
@Controller
public class ProductController {
/**
* 路径设置为 /product/*
* /* 为单层任意字符串 /product/a /product/aaa 可以访问此handler
* /product/a/a 不可以
* 路径设置为 /product/**
* /** 为任意层任意字符串 /product/a /product/aaa 可以访问此handler
* /product/a/a 也可以访问
*/
@RequestMapping("/product/*")
@ResponseBody
public String show(){
System.out.println("ProductController.show");
return "product show!";
}
}
单层匹配和多层匹配:
/*:只能匹配URL地址中的一层,如果想准确匹配两层,那么就写“/*/*”以此类推。
/**:可以匹配URL地址中的多层。
其中所谓的一层或多层是指一个URL地址字符串被“/”划分出来的各个层次
这个知识点虽然对于@RequestMapping注解来说实用性不大,但是将来配置拦截器的时候也遵循这个规则。
类和方法级别区别
@RequestMapping
注解可以用于类级别和方法级别,它们之间的区别如下:
- 设置到类级别:
@RequestMapping
注解可以设置在控制器类上,用于映射整个控制器的通用请求路径。这样,如果控制器中的多个方法都需要映射同一请求路径,就不需要在每个方法上都添加映射路径。 - 设置到方法级别:
@RequestMapping
注解也可以单独设置在控制器方法上,用于更细粒度地映射请求路径和处理方法。当多个方法处理同一个路径的不同操作时,可以使用方法级别的@RequestMapping
注解进行更精细的映射。
//1.标记到handler方法
@RequestMapping("/user/login")
@RequestMapping("/user/register")
@RequestMapping("/user/logout")
//2.优化标记类+handler方法
//类上
@RequestMapping("/user")
//handler方法上
@RequestMapping("/login")
@RequestMapping("/register")
@RequestMapping("/logout")
总结:
//handler -> handlerMapping 指定访问地址
/**
* @WebServlet("必须使用/开头")
* @RequestMapping(不必须使用/开头) /user/login《===》user/login
* 1.精准地址[一个/多个] /user/login {”地址1“,”地址2“}
* 2.支持模糊地址* 任意一层字符串/ **任意层任意字符串
* /user/* -> user/a user/aaaa 可以 /user/a/b 不行
* /user/** -> user user/a user/a/a/a/a 可以
* 3.类上和方法上添加@RequestMapping的区别
* 类上提取通用的访问地址
* 方法上是具体的handler地址
* 访问:类地址+方法地址即可
* 4.请求方式指定
* 客户端 -》http (get/post/delete) -》ds -》handler
* 默认情况: @RequestMapping("/login")主要地址正确,任何请求方式都可以访问
* 指定请求方式:method={RequestMethod.GET,RequestMethod.POST}
* 不符合请求方式:会出现405异常
* 5.注解进阶
* get @GetMapping《==》method = RequestMethod.Get get请求 只能使用在方法上
* post @PostMapping 《==》method = RequestMethod.POST post请求。。。。
*
*/
2.2 参数接收
param参数接收
package com.cqie.param;
import com.cqie.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
/**
* @Title: ParamController
* @Author helong
* @Package com.cqie.controller
* @Date 2023/11/20 16:41
* @description: 如何接收前端的param参数
*/
@Controller
@RequestMapping("/param")
public class ParamController {
//直接接收
// /param/data?name=root&age=18
// 形参列表填写对应名称的参数即可,请求参数名=形参参数名即可!
// 1.名称相同 2.可以不传递 不报错
@RequestMapping("/data")
@ResponseBody
public String data(String name,int age){
System.out.println("name = "+ name+",age = "+age);
return "name = "+ name+",age = "+age;
}
//注解指定
//指定任意的请求参数名 要求必须传递 要求不必须传递 给一个默认值
// /param/data1?account=root&page=18
// account必须传递 page可以不必须传递,如果不传递默认值是1
/*
@RequestParam ->形参列表 指定请求参数名 或者是否必须传递 或者 非必须传递设置默认值
用法 @RequestParam(value="指定请求参数名(如果形参名和请求参数名一致,可以省略)",
required="前端是否必须传递此参数(默认是必须的(true) 不传400异常!)" ,
defaultValue="默认值"(可以设置默认值,不传的时候就是默认)前提required=false))
*/
@RequestMapping("/data1")
@ResponseBody
public String data1(@RequestParam("account") String username, @RequestParam(required = false,defaultValue = "1") int page){
System.out.println("username = "+ username+",page = "+page);
return "username = "+ username+",page = "+page;
}
//特殊值
//一名多值 key=1&key=2 直接使用集合接值即可
// /param/data2?hbs=吃&hbs=玩&hbs=玩
//不加注解@RequestParam 将hbs对应的一个字符串直接赋值给集合! 类型异常!
//加了注解,经理就会将集合add加入对应的字符串
@RequestMapping(value = "/data2",produces = "text/html;charset=utf-8")
@ResponseBody
public String data2(@RequestParam() List<String> hbs){
//设置system.out的字符集 默认字符集是windows默认
//PrintStream ps = new PrintStream(System.out, true, StandardCharsets.UTF_8);
//System.setOut(ps);
System.out.println("hbs = "+hbs);
return"ok";
}
//使用实体对象接值 用户注册(用户的信息) -》对应的实体类 -》 插入到数据库 表
// /param/data3?name=二狗子&age=18 准备对应属性和get/set方法的实体类即可 ->形参列表声明对象参数即可
@RequestMapping(value = "/data3",produces = "text/html;charset=utf-8")
@ResponseBody
public String data3(User user){
System.out.println(user);
return user.toString();
}
}
路径参数接收
package com.cqie.path;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @Title: Pathcontroller
* @Author helong
* @Package com.cqie.path
* @Date 2023/11/20 21:24
* @description: 路径参数演示
*/
@Controller
@RequestMapping("/path")
@ResponseBody
public class Pathcontroller {
// /path/账号/密码
//动态路径的设计 {key} = *{key} 从形参列表获取传入的参数
//接收路径参数 String account,String password -》接收param格式参数
//必须使用 @PathVariable(value=别名)
@RequestMapping("{account}/{password}")
public String login(@PathVariable(value = "account") String username,@PathVariable String password){
System.out.println("username = " + username + ", password = " + password);
return "username = " + username + ", password = " + password;
}
}
json参数接收(注意这里需使用Postman去模拟请求)
//实体类
@Data//需要lombok依赖
public class Person {
private String name;
private int age;
private String gender;
}
package com.cqie.json;
import com.cqie.entity.Person;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @Title: JsonController
* @Author helong
* @Package com.cqie.json
* @Date 2023/11/20 21:38
* @description:
*/
@RequestMapping("/json")
@ResponseBody
@Controller
public class JsonController {
// data -> 请求体 post {name,age,gender}
// 前端 -》json -》 415 不支持数据类型??
// 原因 java原生的api,只支持路径参数和param参数 request。getParameter("key");param 不支持json
// json就是前端格式
//解决 :1.导入json处理的依赖 2.handlerAdapter配置json转化器
@RequestMapping(value = "/data")
public String data(@RequestBody Person person){
System.out.println("person = " + person);
return person.toString();
}
}
2.3接收Cookie数据
package com.cqie.cookie;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* @Title: CookieController
* @Author helong
* @Package com.cqie.cookie
* @Date 2023/11/20 21:58
* @description: 接收cookie
*/
@Controller
@ResponseBody
@RequestMapping("/cookie")
public class CookieController {
@RequestMapping("/data")
public String data(@CookieValue(value = "cookieName") String value){
System.out.println("value = " + value);
return value;
}
//@GetMapping("save")<=>@RequestMapping(value = "/save",method = RequestMethod.GET)
@GetMapping("save")
public String save(HttpServletResponse response) {
Cookie cookie = new Cookie("cookieName","root");
response.addCookie(cookie);
return "ok";
}
}
2.4 接收请求头数据
package com.cqie.header;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @Title: HeaderController
* @Author helong
* @Package com.cqie.header
* @Date 2023/11/20 22:03
* @description:获取请求头
*/
@Controller
@ResponseBody
@RequestMapping("/header")
public class HeaderController {
@RequestMapping("/data")
public String data(@RequestHeader("Host") String host){
System.out.println("host = " + host);
return "host = " + host;
}
2.5原生api对象操作
下表描述了支持的控制器方法参数
Controller method argument 控制器方法参数 | Description |
---|---|
jakarta.servlet.ServletRequest , jakarta.servlet.ServletResponse | 请求/响应对象 |
jakarta.servlet.http.HttpSession | 强制存在会话。因此,这样的参数永远不会为 null 。 |
java.io.InputStream , java.io.Reader | 用于访问由 Servlet API 公开的原始请求正文。 |
java.io.OutputStream , java.io.Writer | 用于访问由 Servlet API 公开的原始响应正文。 |
@PathVariable | 接收路径参数注解 |
@RequestParam | 用于访问 Servlet 请求参数,包括多部分文件。参数值将转换为声明的方法参数类型。 |
@RequestHeader | 用于访问请求标头。标头值将转换为声明的方法参数类型。 |
@CookieValue | 用于访问Cookie。Cookie 值将转换为声明的方法参数类型。 |
@RequestBody | 用于访问 HTTP 请求正文。正文内容通过使用 HttpMessageConverter 实现转换为声明的方法参数类型。 |
java.util.Map , org.springframework.ui.Model , org.springframework.ui.ModelMap | 共享域对象,并在视图呈现过程中向模板公开。 |
Errors , BindingResult | 验证和数据绑定中的错误信息获取对象! |
/**
* 如果想要获取请求或者响应对象,或者会话等,可以直接在形参列表传入,并且不分先后顺序!
* 注意: 接收原生对象,并不影响参数接收!
*/
@GetMapping("api")
@ResponseBody
public String api(HttpSession session , HttpServletRequest request,
HttpServletResponse response){
String method = request.getMethod();
System.out.println("method = " + method);
return "api";
}
2.6 共享域操作
package com.cqie.share;
/**
* @Title: ShareController
* @Author helong
* @Package com.cqie.share
* @Date 2023/11/20 22:16
* @description: 演示共享对象获取
*/
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import java.util.Map;
@Controller
@ResponseBody
@RequestMapping("share")
public class ShareController {
@Autowired
private ServletContext servletContext;
@RequestMapping("data")
public void data(HttpServletRequest request, HttpSession session){
}
//springmvc提供的方法: request提供了几种
//model modelMap map modelAndView
public void data1(Model model){
model.addAttribute("key","value");//request
}
public void data2(ModelMap model){
model.addAttribute("key","value");//request
}
public void data3(Map map){
map.put("key","value");//request
}
public ModelAndView data4(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("key","value");
modelAndView.setViewName("视图名 页面名称");
return modelAndView;
}
}
三 、SpringMVC响应数据
3.1 理解handler方法的作用和组成:
/**
* TODO: 一个controller的方法是控制层的一个处理器,我们称为handler
* TODO: handler需要使用@RequestMapping/@GetMapping系列,声明路径,在HandlerMapping中注册,供DS查找!
* TODO: handler作用总结:
* 1.接收请求参数(param,json,pathVariable,共享域等)
* 2.调用业务逻辑
* 3.响应前端数据(页面(不讲解模版页面跳转),json,转发和重定向等)
* TODO: handler如何处理呢
* 1.接收参数: handler(形参列表: 主要的作用就是用来接收参数)
* 2.调用业务: { 方法体 可以向后调用业务方法 service.xx() }
* 3.响应数据: return 返回结果,可以快速响应前端数据
*/
@GetMapping
public Object handler(简化请求参数接收){
调用业务方法
返回的结果 (页面跳转,返回数据(json))
return 简化响应前端数据;
}
总结: 请求数据接收,我们都是通过handler的形参列表
前端数据响应,我们都是通过handler的return关键字快速处理!springmvc简化了参数接收和响应!
3.2 页面跳转
配置视图解析器
//在MvcConfig中配置添加一下语句(可返回1.3查看添加)
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
//registry可以快速添加前后缀
registry.jsp("/WEB-INF/views/",".jsp");
//handler -> index
}
package com.cqie.jsp;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @Title: JspController
* @Author helong
* @Package com.cqie.Jsp
* @Date 2023/11/20 23:04
* @description:
*/
@Controller
@RequestMapping("jsp")
public class JspController {
/**
* TODO:快速查找视图
* 1.方法的返回值是字符串类型
* 2.不能添加@ResponseBody,直接返回字符串给浏览器,不找视图,不走视图解析器
* 3.返回值 对应中间的视图名称即可
* 4.需要配置视图解析器
*
* */
// /jsp/index
@GetMapping("index")
public String index(HttpServletRequest request){
request.setAttribute("data","hello jsp!!");
System.out.println("JspController.index");
return "index";
}
/*
* 转发:只能是项目下的资源
* 1.方法的返回值写成字符串
* 2.不能添加responseBody注解
* 3.返回的字符串前 forward:/转发地址
*
* */
//转发
@GetMapping("forward")
public String forward(){
System.out.println("JspController.forward");
return "forward:/jsp/index";
}
/*
* 重定向
* 1.方法的返回值写成字符串类型
* 2.不能添加responseBody注解
* 3.返回字符串前面 redirect:/重定向的地址
*
*
* */
/*
* 路径细节[不使用springmvc request response]
* 转发是项目下的资源跳转,路径:项目下的地址 /jsp/index 忽略 applicationContext
* 重定向项目下的资源可以是项目外的地址 重定向属于二次请求 路径: 项目下的地址 全地址/springmvc/jsp/index 不忽略 applicationContext
* 使用springmvc路径语法
* “forward:路径 / redirect:路径” 重定向,资源的资质也不需要些项目的根路径! /jsp/index
* 转发和重定向的地址都一样! 这时写成/springmvc/jsp/index -》/springmvc/springmvc/jsp/index
*
*
* */
//重定向
@GetMapping("redirect")
public String redirect(){
System.out.println("JspController.redirect");
return "redirect:/jsp/index";
}
@GetMapping("redirectBaidu")
public String redirectBaidu(){
System.out.println("JspController.redirectBaidu");
return "redirect:http://www.baidu.com";
}
}
返回json数据(需要json依赖)
//实体
@Data
public class User {
private String name;
private int age;
}
package com.cqie.json;
import com.cqie.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* @Title: JsonController
* @Author helong
* @Package com.cqie.json
* @Date 2023/11/21 11:04
* @description:
*/
@Controller
@RequestMapping("json")
@ResponseBody//不会进入视图解析器
//@Controller + @@ResponseBody = @RestController
public class JsonController {
/*
* TODO:@ResponseBody 数据直接放入响应体中,也不会走视图解析器
* 快速查找视图,转发和重定向都不生效了
*
*
* */
@GetMapping("data")
public User data(){
//对象 -》 json-》{}
///集合 -》json-》[]
User user = new User();
user.setName("two dogs!");
user.setAge(3);
return user;//user -> handlderAdpater -> json ->@ResponseBody->json直接返回[前后端分离]
}
@GetMapping("data1")
public List<User> data1(){
User user = new User();
user.setName("two dogs!");
user.setAge(3);
List<User> users = new ArrayList<>();
users.add(user);
return users;
}
}
四、RESTFul风格设计
4.1 RESFTFul风格介绍
RESTful(Representational State Transfer)是一种软件架构风格,用于设计网络应用程序和服务之间的通信。它是一种基于标准 HTTP 方法的简单和轻量级的通信协议,广泛应用于现代的Web服务开发。
通过遵循 RESTful 架构的设计原则,可以构建出易于理解、可扩展、松耦合和可重用的 Web 服务。RESTful API 的特点是简单、清晰,并且易于使用和理解,它们使用标准的 HTTP 方法和状态码进行通信,不需要额外的协议和中间件。
总而言之,RESTful 是一种基于 HTTP 和标准化的设计原则的软件架构风格,用于设计和实现可靠、可扩展和易于集成的 Web 服务和应用程序!
4.2 RESFTFul风格设计规范
HTTP协议请求方式要求
REST 风格主张在项目设计、开发过程中,具体的操作符合HTTP协议定义的请求方式的语义。
操作 | 请求方式 |
---|---|
查询操作 | GET |
保存操作 | POST |
删除操作 | DELETE |
更新操作 | PUT |
URL路径风格要求
REST风格下每个资源都应该有一个唯一的标识符,例如一个 URI(统一资源标识符)或者一个 URL(统一资源定位符)。资源的标识符应该能明确地说明该资源的信息,同时也应该是可被理解和解释的!
使用URL+请求方式确定具体的动作,他也是一种标准的HTTP协议请求!
操作 | 传统风格 | REST 风格 |
---|---|---|
保存 | /CRUD/saveEmp | URL 地址:/CRUD/emp 请求方式:POST |
删除 | /CRUD/removeEmp?empId=2 | URL 地址:/CRUD/emp/2 请求方式:DELETE |
更新 | /CRUD/updateEmp | URL 地址:/CRUD/emp 请求方式:PUT |
查询 | /CRUD/editEmp?empId=2 | URL 地址:/CRUD/emp/2 请求方式:GET |
-
总结
根据接口的具体动作,选择具体的HTTP协议请求方式
路径设计从原来携带动标识,改成名词,对应资源的唯一标识即可!
RESFTFul风格好处
-
含蓄,安全
使用问号键值对的方式给服务器传递数据太明显,容易被人利用来对系统进行破坏。使用 REST 风格携带数据不再需要明显的暴露数据的名称。
-
风格统一
URL 地址整体格式统一,从前到后始终都使用斜杠划分各个单词,用简单一致的格式表达语义。
-
无状态
在调用一个接口(访问、操作资源)的时候,可以不用考虑上下文,不用考虑当前状态,极大的降低了系统设计的复杂度。
-
严谨,规范
严格按照 HTTP1.1 协议中定义的请求方式本身的语义进行操作。
-
简洁,优雅
过去做增删改查操作需要设计4个不同的URL,现在一个就够了。
操作 | 传统风格 | REST 风格 |
---|---|---|
保存 | /CRUD/saveEmp | URL 地址:/CRUD/emp 请求方式:POST |
删除 | /CRUD/removeEmp?empId=2 | URL 地址:/CRUD/emp/2 请求方式:DELETE |
更新 | /CRUD/updateEmp | URL 地址:/CRUD/emp 请求方式:PUT |
查询 | /CRUD/editEmp?empId=2 | URL 地址:/CRUD/emp/2 请求方式:GET |
-
丰富的语义
通过 URL 地址就可以知道资源之间的关系。它能够把一句话中的很多单词用斜杠连起来,反过来说就是可以在 URL 地址中用一句话来充分表达语义。
五、拦截器
5.1 拦截器的作用
Spring MVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。
-
拦截器和过滤器的区别
区别 过滤器 拦截器 使用范围 是servlet规范中的一部分,任何Java Web工程都可以使用 是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能用 拦截范围 在url-pattern中配置了/*之后,可以对所有要访问的资源拦截 只会拦截访问的控制器方法,如果访问的是jsp,html,css,image或者js 是不会进行拦截的 -
拦截器方法说明
方法名 说明 preHandle 方法将在请求处理之前进行调用,该方法的返回值是布尔值Boolean类型的,当它返回为false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值为true时就会继续调用下一个Interceptor的preHandle方法 postHandle 该方法是在当前请求进行处理之后被调用,前提是preHandle方法的返回值为true时才能被调用,且它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作 afterCompletion 该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行,前提是preHandle方法的返回值为true时才能被调用
5.2 配置拦截器
//在MvcConfig中配置添加一下语句(可返回1.3查看添加)
@Override
public void addInterceptors(InterceptorRegistry registry) {
//配置方案1;拦截全部请求
//registry.addInterceptor(new MyInterceptor());
//配置方案2;指定地址拦截
//* 任意一层字符串 **任意多层字符串
// registry.addInterceptor(new MyInterceptor())
// .addPathPatterns("/user/**");
//配置方案3;排除拦截 排除的地址应该在拦截地址内部
// registry.addInterceptor(new MyInterceptor())
// .addPathPatterns("/user/**")
// .excludePathPatterns("/user/data1");
registry.addInterceptor(new MyInterceptor());
registry.addInterceptor(new MyInterceptor1());
}
package com.cqie.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* @Title: MyInterceptor
* @Author helong
* @Package com.cqie.interceptor
* @Date 2023/11/21 22:09
* @description: 拦截器
*/
public class MyInterceptor implements HandlerInterceptor {
//再执行handler之前!调用拦截器方法!
//编码格式设置,登录保护,权限处理!!
/**
*
* @param request 请求对象
* @param response 响应对象
* @param handler handler就是我们要调用的方法对象
* @return true 放行 false 拦截
* @author helong
* @date 22:10 2023/11/21
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("request = " + request + ", response = " + response + ", handler = " + handler);
return true;
}
/**
* 当handler执行完毕后,触发的方法,没有拦截机制了
* 此方法只有 preHandler return true
*
*
* 对结果处理!,敏感词检查!!!
* @param request 请求
* @param response 响应
* @param handler handler方法
* @param modelAndView 返回的视图和共享域数据对象
* @return void
* @author helong
* @date 22:12 2023/11/21
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor.postHandle");
}
/**
* 整体处理完毕!!
*
* @param request
* @param response
* @param handler
* @param ex handler报错了 异常对象
* @return void
* @author helong
* @date 22:14 2023/11/21
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor.afterCompletion");
}
}
六、参数校验
6.1 导入依赖
<!-- 校验注解 -->
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>9.1.0</version>
<scope>provided</scope>
</dependency>
<!-- 校验注解实现-->
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.0.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator-annotation-processor -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>8.0.0.Final</version>
</dependency>
6.2 配置实体
package com.cqie.entity;
import jakarta.validation.constraints.*;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import java.util.Date;
/**
* @Title: User
* @Author helong
* @Package com.cqie.entity
* @Date 2023/11/21 22:43
* @description:
*
* 1.name 不为空
* 字符串 @NotBlank 集合@NotEmpty 包装@NotNull
* 2.password 长度大于6
*
* 3.age 必须大于等于1
* 4.email 邮箱格式的字符串
* 5.birth
*/
@Data
public class User {
@NotBlank
private String name;
@Length(min=6)
private String password;
@Min(1)
private int age;
@Email
private String email;
@Past
private Date birthday;
}
6.3 handler标记和绑定错误收集
package com.cqie.controller;
import com.cqie.entity.User;
import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
/**
* @Title: UserController
* @Author helong
* @Package com.cqie.controller
* @Date 2023/11/21 21:46
* @description:
*/
@RestController
@RequestMapping("user")
public class UserController {
//接收用户数据,用户有校验注解
/*
* 1.实体类属性添加校验注解
* 2.handler(@Validdated 实体类 对象)
* 细节:param/json 校验注解都有效果
* json参数 - @RequestBody
*
* 如果不符合校验规则,直接向前端抛出异常!
* 接收错误绑定信息!自定义返回结果! 约定: 参数错误-》{code:400} -》前端
* 捕捉错误绑定错误信息
* 1.handler(校验对象,BindingResult result) 要求:bindingResult必须紧挨着校验对象
* 2.通过bindingresult获取绑定错误
* 3.
* */
@PostMapping("register")
public Object register(@Validated @RequestBody User user, BindingResult result, HttpSession session){
if(result.hasErrors()){
//有绑定错误,就不直接返回了,由我们自己决定!
Map data = new HashMap();
data.put("code",400);
data.put("msg","参数校验异常");
return data;
}
System.out.println("user = " + user);
return user;
}
}