web学习--SpringMVC--1 基础学习

写在前面:
所有的web学习基于springboot项目,而不会去单独的使用spring来进行。

文章目录

  • SpringMVC介绍
    • 原理
    • MVC模式
  • 入门使用
    • 导入依赖
    • 编写controller类
  • 详细介绍
    • 注解详解
      • @Controller
      • @RequestMapping
      • @ResponseBody
      • @RestController
      • @RequestParam
      • @RequestBody
      • @Request其他
      • @DateTimeFormat
    • 各类参数接收
      • 简单参数
        • 原始获取
        • map
        • 自动匹配
      • 实体参数
        • 简单实体
        • 复杂实体
        • 同属性名字的不同实体同时接收
      • 数组
      • 集合
      • 日期时间类
      • json
      • 路径参数
  • 扩展
    • 统一返回类型
    • 静态资源存放

SpringMVC介绍

原理

SpringMVC底层就是Servlet,SpringMVC就是对Servlet进行深层次的封装。

MVC模式

MVC分别是:模型model(javabean)、视图view(jsp/img)、控制器Controller(Action/servlet)。

入门使用

导入依赖

有2种方式,一般第一种

  1. 在springboot创建项目的时候可以勾选spring web
    在这里插入图片描述
  2. 手动导入依赖
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>

编写controller类

在类上添加注解

@RestController

新建一个方法,在方法上指定请求路径

@RequestMapping("/uri")

这样一个简单的controller就完成好了。

@RestController
public class HellowController {

  @RequestMapping("/hello")
  public String hello(String username,String password){
    System.out.println("hello");
    return "hello";
  }
}

详细介绍

注解详解

@Controller

作用于:类
作用:该注解用来类上标识这是一个控制器组件类并创建这个类实例,告诉spring我是一个控制器。
不加这个
web学习--SpringMVC--1 基础学习_第1张图片
如果有controller就会显示了
web学习--SpringMVC--1 基础学习_第2张图片

@RequestMapping

作用于:类、方法
作用:用来指定请求路径。类设置的是这个类全局的请求路径,所以一个方法接收的uri为 类 m a p p i n g + 方法 m a p p i n g 类mapping+方法mapping mapping+方法mapping

@ResponseBody

作用于:类
作用:将方法的返回值直接响应到浏览器
如果是数据,就直接用数据展示
如果是实体类/集合则会以json返回

先给结论,如果不加这个,会返回静态资源文件,会与视图服务器设定的前后缀拼接返回给客户端定位资源,就是resource下static的对应文件名的文件。如果加了这个就是返回数据。

因为这个,我们也可以通过一个没有ResponseBody的控制类,来进行资源跳转和资源的获取

首先让我们尝试不加这个注解
web学习--SpringMVC--1 基础学习_第3张图片
发送请求,发现没有找到
web学习--SpringMVC--1 基础学习_第4张图片
但是如果我们将返回值修改为一个我们准备的页面
web学习--SpringMVC--1 基础学习_第5张图片
这次发送直接返回的是index.html的页面
web学习--SpringMVC--1 基础学习_第6张图片
直接在浏览器返回也是
web学习--SpringMVC--1 基础学习_第7张图片
而且还必须要有.html,如果只是index会显示没有页面
web学习--SpringMVC--1 基础学习_第8张图片
接下来我们添加
web学习--SpringMVC--1 基础学习_第9张图片

重启发送请求,就只是在数据放在上面。
web学习--SpringMVC--1 基础学习_第10张图片
下面演示集合,返回json字符串

    @GetMapping()
    public Map getName(){
        return Map.of("id",123,"name","张三");
    }

web学习--SpringMVC--1 基础学习_第11张图片

@RestController

作用于:类
作用:这个注解是controller的升级,包括了controller和ResponseBody,并且建议以REST风格来书写代码
web学习--SpringMVC--1 基础学习_第12张图片

@RequestParam

作用于:方法参数
作用:获取url请求参数
可选值:

  • required:表示是否一定需要
  • defaultValue:默认值
  • name:绑定到的请求参数
  • value:别名name,书写更简洁,因为只有value的时候可以省略

@RequestBody

作用于:方法参数
作用:获取请求体的参数
可选值:

  • required:表示是否一定需要

@Request其他

作用于:方法参数
作用:方法参数应绑定到web请求标头
其他:

  • Attribute
  • Header

@DateTimeFormat

作用于:方法参数
作用:声明字段或方法参数应格式化为日期或时间。
无论何时使用style或pattern属性,格式化java.util.Date值时都将使用JVM的默认时区
可选值:

  • pattern:格式化的格式
  • iso:格式化
    web学习--SpringMVC--1 基础学习_第13张图片
  • style:格式化字段或方法参数的样式模式
    短日期、短时间默认为“SS”。如果希望根据默认样式以外的通用样式设置字段或方法参数的格式,请设置此属性
  • fallbackPatterns:在主模式、iso或style属性解析失败时用作回退的一组自定义模式

各类参数接收

首先明确,参数的位置有2种,一种在url上一种在请求体上。
在请求体上的需要@RequestBody 而且最多只能出现一次,就是说需要一个实体接收全部的请求体参数。
这里先简单的做一个了解,后面在说匹配的问题。
web学习--SpringMVC--1 基础学习_第14张图片
打印出来的id就是

System.out.println("id = " + id);

在这里插入图片描述

简单参数

只推荐使用按名字匹配的,其他不推荐。

原始获取

    public String getID(HttpServletRequest request){
        String id = request.getParameter("id");
        System.out.println("id = " + id);
        return null;
    }

web学习--SpringMVC--1 基础学习_第15张图片
web学习--SpringMVC--1 基础学习_第16张图片

map

    @PostMapping()
    public String getName(@RequestParam Map user) {
        System.out.println("user = " + user);
        return null;
    }

结果

POST http://localhost/a?id=1&name=2

user = {id=1, name=2}

自动匹配

这是springboot的方式
会根据名字来进行匹配

    public String getName(String id, @RequestParam("id") Integer i){
        System.out.println("i = " + i);
        System.out.println("id = " + id);
        return null;
    }

在这里插入图片描述
会发现都可以获取到id。
即使类型不一样,也会进行自动转换。

接下来我们将请求id设置为z
返回为400且控制台出现类型转换的错误

2023-07-05 13:59:09.160  WARN 19200 --- [p-nio-80-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'; nested exception is java.lang.NumberFormatException: For input string: "z"]

web学习--SpringMVC--1 基础学习_第17张图片

如果对应不上,那么默认就是null
但是如果是基本数据类型对应不上如定义了int id但是没有给id
那么就会报错因为int类型不能为null
所以在参数定义上都用包装类

实体参数

简单实体

只需要保证请求的形参名和实习参数一致

    record User(String name, Integer id){}
    @PostMapping()
    public String getName(User user){
        System.out.println("user = " + user);
        return null;
    }

结果

###
POST http://localhost:80/a?id=1

user = User[name=null, id=1]

###
POST http://localhost:80/a?id=1&name=2

user = User[name=2, id=1]

复杂实体

复杂类型传参就用.来区分
这里本来也想用record的,但是record由于没有遵守bean标准,不提供setter和无参构造会导致初始化失败。而无法赋值。
所以通过案例也可以知道,这里推测是调用无参构造init然后通过setter赋值

    @Data
    static class User {
        String name;
        Integer id;
        Address address;
    }

    @Data
    static class Address {
        Integer city;
        Integer id;
    }

    @PostMapping()
    public String getName(User user) {
        System.out.println("user = " + user);
        return null;
    }

结果

POST http://localhost/a?id=1&name=2&address.city=4&address.id=5

user = ResponseBodyController.User(name=2, id=1, address=ResponseBodyController.Address(city=4, id=5))

前面提到map传参,那么这里可以用吗.
可以看到这里其实吧address.city当成key而不是一个实体类了。

POST http://localhost/a?id=1&name=2&address.city=4&address.id=5

user = {id=1, name=2, address.city=4, address.id=5}

同属性名字的不同实体同时接收

    record User(String name, Integer id){}
    record Student(String name, Integer id){}
    @PostMapping()
    public String getName(User user,Student student){
        System.out.println("user = " + user);
        System.out.println("student = " + student);
        return null;
    }
###
POST http://localhost:80/a?id=1

user = User[name=null, id=1]
student = Student[name=null, id=1]


###
POST http://localhost:80/a?id=1&name=2

user = User[name=2, id=1]
student = Student[name=2, id=1]

可以看到,两个对象的值都被赋上了,但是,大部分情况下,不同的对象的值一般都是不同的

数组

    @PostMapping()
    public String getName(Integer[] id) {
        System.out.println("dis = " + Arrays.toString(id));
        return null;
    }
POST http://localhost/a?id=1&id=2

dis = [1, 2]

那么如果不是数组类型而传递了多个呢。
只有第一个有效

POST http://localhost/a?id=1&id=2

is = 1

集合

必须写来源在哪里,如RequestParam或者RequestBody
不然会出现

No primary or single unique constructor found for interface java.util.List
    public String getName(@RequestParam List<Integer> id) {
        System.out.println("id = " + id);
        return null;
    }

结果

POST http://localhost/a?id=1&id=2

id = [1, 2]

日期时间类

    public String getName(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime time) {
        System.out.println("time = " + time);
        return null;
    }

结果

POST http://localhost/a?time=2023-07-03 23:01:14

time = 2023-07-03T23:01:14

json

    @PostMapping()
    public String getName(@RequestBody User user) {
        System.out.println("user = " + user);
        return null;
    }

结果

POST http://localhost/a
Content-Type: application/json

{
  "name":"test",
  "id": 1,
  "address": {
    "city": 1,
    "id": 1
  }
}

user = ResponseBodyController.User(name=test, id=1, address=ResponseBodyController.Address(city=1, id=1))

但是这里是可以用record的,所以路径解析和url请求解析是不一样的。

    record User(String name, Integer id,Address address){}
    record Address(Integer id){}

    @PostMapping()
    public String getName(@RequestBody User user) {
        System.out.println("user = " + user);
        return null;
    }
POST http://localhost/a
Content-Type: application/json

{
  "name":"test",
  "id": 1,
  "address": {
    "id": 1
  }
}

user = User[name=test, id=1, address=Address[id=1]]

路径参数

    @PostMapping("{id}")
    public String getName(@PathVariable Integer id) {
        System.out.println("id = " + id);
        return null;
    }
POST http://localhost/a/1

id = 1

扩展

统一返回类型

因为返回的类型可能什么都有,所以我们需要一个统一的返回类型
一般有这3个属性

  • code 响应码
  • data 存放数据
  • msg 消息

由于这个返回类只用一次,所以可以用record简化。
这里给我的做的一个参考,根据自己的业务修改

package com.yu.common;

/**
 * 通用返回类
 *
 * @param code 200成功 500失败
 * @param data 返回的数据
 * @param msg  返回的信息
 */
public record R(Integer code, Object data, String msg) {
    public static R ok(Object data) {
        return new R(Code.SUCCESS.code(), data, "");
    }

    public static R ok() {
        return new R(Code.SUCCESS.code(), null, "");
    }
    public static R ok(Code code){
        return new R(code.code(),null,code.msg());
    }

    public static R error() {
        return new R(Code.ERROR.code(), null, "");
    }

    public static R error(Code code) {
        return new R(code.code(), null, code.msg());
    }

}

枚举类

package com.yu.common;

/**
 * 通用返回状态码类
 */
public enum Code {
    SUCCESS(200,"成功"),
    ERROR(500,"失败"),
    NOT_FOUND(404,"未找到"),
    NOT_LOGIN(401,"未登录"),
    NOT_AUTH(403,"未授权"),
    NOT_ALLOWED(405,"不允许的请求方式"),
    PARAM_ERROR(400,"参数错误"),
    SERVER_ERROR(500,"服务器错误"),
    PASSWORD_ERROR(1001,"密码错误"),
    NOT_NULL(1002,"不能为空"),
    NOT_EXIST_STUDENTS(1003,"不存在该学生"),
    ;

    private final int code;
    private final String msg;

    Code(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public static Code get(int code){
        for (Code value : Code.values()) {
            if (value.code == code){
                return value;
            }
        }
        return null;
    }

    public int code() {
        return code;
    }

    public String msg() {
        return msg;
    }
}

静态资源存放

springboot的静态目录(html,css,js等)默认存放目录为classpath:/static、classpath:/public、classpath:/resources
一般放在static里面
访问直接localhost:port/相当于static/
如访问static/index/index.html
就可以通过localhost:port/index/index.html访问

当然前后端分离项目不会在static下存放

你可能感兴趣的:(web的那些事,前端,学习)