SpringMVC

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 做整体请求处理调度!

除了DispatcherServletSpringMVC还会提供其他特殊的组件协作完成请求处理和响应呈现。

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涉及组件理解:

  1. DispatcherServlet : SpringMVC提供,我们需要使用web.xml配置使其生效,它是整个流程处理的核心,所有 请求都经过它的处理和分发!

  2. HandlerMapping : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它内部缓存handler(controller方法)和handler访问路径数据,被DispatcherServlet调用,用于查找路径对应的handler!

  3. HandlerAdapter : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它可以处理请求参数和处理响应数据数据,每次DispatcherServlet都是通过handlerAdapter间接调用handler,他是handler和DispatcherServlet之间的适配器!

  4. Handler : handler又称处理器,他是Controller类内部的方法简称,是由我们自己定义,用来接收参数,向后调用业务,最终返回响应结果!

  5. 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 注解可以用于类级别和方法级别,它们之间的区别如下:

  1. 设置到类级别:@RequestMapping 注解可以设置在控制器类上,用于映射整个控制器的通用请求路径。这样,如果控制器中的多个方法都需要映射同一请求路径,就不需要在每个方法上都添加映射路径。
  2. 设置到方法级别:@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/saveEmpURL 地址:/CRUD/emp 请求方式:POST
删除/CRUD/removeEmp?empId=2URL 地址:/CRUD/emp/2 请求方式:DELETE
更新/CRUD/updateEmpURL 地址:/CRUD/emp 请求方式:PUT
查询/CRUD/editEmp?empId=2URL 地址:/CRUD/emp/2 请求方式:GET
  • 总结

    根据接口的具体动作,选择具体的HTTP协议请求方式

    路径设计从原来携带动标识,改成名词,对应资源的唯一标识即可!

RESFTFul风格好处
  1. 含蓄,安全

    使用问号键值对的方式给服务器传递数据太明显,容易被人利用来对系统进行破坏。使用 REST 风格携带数据不再需要明显的暴露数据的名称。

  2. 风格统一

    URL 地址整体格式统一,从前到后始终都使用斜杠划分各个单词,用简单一致的格式表达语义。

  3. 无状态

    在调用一个接口(访问、操作资源)的时候,可以不用考虑上下文,不用考虑当前状态,极大的降低了系统设计的复杂度。

  4. 严谨,规范

    严格按照 HTTP1.1 协议中定义的请求方式本身的语义进行操作。

  5. 简洁,优雅

    过去做增删改查操作需要设计4个不同的URL,现在一个就够了。

操作传统风格REST 风格
保存/CRUD/saveEmpURL 地址:/CRUD/emp 请求方式:POST
删除/CRUD/removeEmp?empId=2URL 地址:/CRUD/emp/2 请求方式:DELETE
更新/CRUD/updateEmpURL 地址:/CRUD/emp 请求方式:PUT
查询/CRUD/editEmp?empId=2URL 地址:/CRUD/emp/2 请求方式:GET
  1. 丰富的语义

    通过 URL 地址就可以知道资源之间的关系。它能够把一句话中的很多单词用斜杠连起来,反过来说就是可以在 URL 地址中用一句话来充分表达语义。

五、拦截器

5.1 拦截器的作用

Spring MVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。

将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。

  1. 拦截器和过滤器的区别

    区别过滤器拦截器
    使用范围是servlet规范中的一部分,任何Java Web工程都可以使用是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能用
    拦截范围在url-pattern中配置了/*之后,可以对所有要访问的资源拦截只会拦截访问的控制器方法,如果访问的是jsp,html,css,image或者js 是不会进行拦截的
  2. 拦截器方法说明

    方法名说明
    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;
    }
}

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值