接口请求的一些注解使用笔记

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了接口请求的一些注解使用笔记相关的知识,希望对你有一定的参考价值。

参考技术A 1.@Controller定义控制器类,再使用注解@RequestMapping方法处理请求,完成映射关系。
2.@RestController等价于@Controller加上@ResponseBody
3.@PathVariable获取URI中的变量为参数。

1.假设接口请求所需要的参数id为123123这种情况下接口请求路径为127.0.0.1:8080/product/123123
这里需要使用 到@RequestMapping,SpringBoot中 提供了比较简洁的写法
@GetMapping:处理 get请求
@PostMapping:处理post请求
@DeleteMapping:处理删除请求
@PutMapping:处理修改请求
2.获取路径中的参数

这里的接口请求参数是靠=拼接的
如果请求参数usernameValue,那么完整的请求地址为
127.0.0.1:8080/adduser?username=usernameValue

3.接收路径中的json对象

实际的实现

4.通过HttpServletRequest接收参数

5.@RequestBody接收json数据

6.上传文件,这里唯一的易忽略的点是上传文件的时候需要判断所在文件路径是否存在,判断文件是否存在,如果不存在就创建它们。

验证器Hibernate-validator当前(2022.03.15)需要在pom.xml中添加一下依赖

一个神奇的功能,自定义注解类
接口文件MyCustomConstraint,注意这里如果是idea创建的Interface,前面会少个@符号,请对照自己添加

还有验证的类MyCustomConstraintValidator

@MyCustomConstrain已经被定义,接下来看如何使用
1.创建实体Usr

2.验证控制器TestValidator的实现

这里的addAttribute方法和addFlashAttribute的区别是前者将参数拼接到url后面,后者不会将参数拼接到url后面,而是将参数暂存在session中
3.视图form.html的实现,注意这里的"*name"可能会有底部红色波浪线提示报错,处理方法是将xmlne:th=""里面的www.去掉

视图结果results.html实现

1.错误码枚举YZCode enum

2.返回类YZResponse

使用:

Spring Boot学习笔记总结

文章目录

1. Web请求参数处理 Rest映射源码解析

1.1 OrderedHiddenHttpMethodFilter对象原理


Rest风格:

  • 注意这里是Rest风格,并不是Restful风格不要混淆!
  • 可以直接认为Rest风格就是@RestController和@ResponseBody。
package com.itholmes.boot.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

//RestController就是rest风格,还有@Responsebody
@RestController
public class HelloController 
	
	//相同的路径/user,来处理不同的请求类型。
    @RequestMapping(value = "/user" ,method = RequestMethod.GET)
    //等同于@GetMapping("/user")
    public String getUser()
        return "GET-张三";
    

    @RequestMapping(value = "/user" ,method = RequestMethod.POST)
     //等同于@PostMapping("/user")
    public String saveUser()
        return "POST-张三";
    

    @RequestMapping(value = "/user" ,method = RequestMethod.PUT)
     //等同于@PutMapping("/user")
    public String putUser()
        return "PUT-张三";
    

    @RequestMapping(value = "/user" ,method = RequestMethod.DELETE)
     //等同于@DeleteMapping("/user")
    public String deleteUser()
        return "DELETE-张三";
    


对于rest风格的参数类型处理,必须要给容器中注入一个OrderedHiddenHttpMethodFilter对象,这个对象也是在WebMvcAutoConfiguration类自动配置注入。

在WebMvcAutoConfiguration类下,有一个hiddenHttpMethodFilter(排序隐藏Http方法过滤器)方法,这个方法的作用是过滤请求参数的方法类型,例如:get,post,delete,put等等。注意这个hiddenHttpMethodFilter装配到容器中,该对象本身是一个filter过滤器!

@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(
    prefix = "spring.mvc.hiddenmethod.filter",
    name = "enabled"
)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() 
    return new OrderedHiddenHttpMethodFilter();

但是在@ConditionalOnProperty注解中有一个matchIfMissing注解属性,默认是false的!这个属性的功能如下:

/**
 *该属性为true时,配置文件中缺少对应的value或name的对应的属性值,也会注入成功
 */
boolean matchIfMissing() default false;

因为是matchIfMissing默认是false,所以开始的HiddenHttpMethodFilter过滤器是没有装配到容器中的,因此我们要设置spring.mvc.hiddenmethod.filter.enabled=true来启动该过滤器(装配到容器中)。

可以理解的是spring.mvc.hiddenmethod.filter.enabled=true是专门用来处理表单的Rest功能。

spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true # 启用HiddenHttpMethodFilter过滤器,专门用来处理表单传送过来的_method的参数数据,进而来出来delete,put等类型参数。

1.2 表单提交要使用Rest的使用


对应上面内容。

这样我们在前端发送不同类型,后台就可以处理了:

  • 表单提交要带上_method=PUT , 这样的方式来能让SpringBoot自动配置的hiddenHttpMethodFilter方法识别到!
  • 再次提醒,不要忘记启动hiddenHttpMethodFilter组件对象,也就是必须要装配到容器中(被启动)。因为上面的这种方式必须基于该组件对象。
测试REST风格:
<form action="/user" method="get">
    <input type="submit" value="REST-GET">
</form>
<form action="/user" method="POST">
    <input type="submit" value="REST-POST">
</form>
<!--
    form表单是不能提交delete,put等类型的,直接定义这些类型,form表单还是走默认的get方法。
    根据hiddenHttpMethodFilter方法源码:通过传递一个name为"_method"值的参数来指定,并且方法的类型必须是post方法,因为它的源码规定这里必须是post方式!。
-->
<form action="/user" method="post">
    <input name="_method" type="hidden" value="DELETE">
    <input type="submit" value="REST-delete">
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="PUT">
    <input type="submit" value="REST-put">
</form>

hiddenHttpMethodFilter在容器中本质是一个OrderedHiddenHttpMethodFilter对象,该对象又继承了HiddenHttpMethodFilter对象,在HiddenHttpMethodFilter对象中,有一个doFilterInternal方法:

private static final List<String> ALLOWED_METHODS;

static 
	//这里定义支持哪几种请求,有delete,put,patch这三种。也就是允许这三种请求。
    ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));



protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException 
	//拿到当前的请求request
    HttpServletRequest requestToUse = request;
    //先判断是不是post方法,也说明了前面form表单传递_method=PUT形式为什么要用post,这里规定的就是post。并且判断请求是否正常,有没有error。
    if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) 
    	//在request获取参数,这里的this.methodParam是整个类写死的一个值就是"_method";
    	//也就是在request中获取到了_method的参数了,也就是form表单传的delete,put等等。
        String paramValue = request.getParameter(this.methodParam);
        //这里判断有没有长度,也就是判断是不是空
        if (StringUtils.hasLength(paramValue)) 
        	//将值进行一个大写转换。也就迎合了前端发送大写小的方法名都可以。
            String method = paramValue.toUpperCase(Locale.ENGLISH);
            //对应上面ALLOWED_METHODS,判断是否属于ALLOWED_METHODS,仅能支持delete,put,patch。
            if (ALLOWED_METHODS.contains(method)) 
            	//将原生request(post请求) , 包装模式requestWrapper重写了getMethod方法,返回的是重写后的request(就是这里的method请求[delete,put,patch请求])
                requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
            
        
    
	//之后就是将处理后的requestToUse放行了。过滤器放行。
    filterChain.doFilter((ServletRequest)requestToUse, response);


==========================================================================================
//对应上面的HttpMethodRequestWrapper
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper 
    private final String method;

    public HttpMethodRequestWrapper(HttpServletRequest request, String method) 
        super(request);
        this.method = method;
    
	//这里将request的值重写了,也就是原来是request的method是post,现在重写为了根据_method传入的类型(delete,put,patch)
    public String getMethod() 
        return this.method;
    

这样也就将原来的post请求类型,通过OrderedHiddenHttpMethodFilter过滤器转成了不同的请求类型。

1.3 Rest使用客户端工具发送不同类型的请求


例如:使用postman发送的put参数。

也就是不影响原生的功能。

1.4 如何改变默认的_method?


自己创建一个HiddenHttpMethodFilter对象,放入容器中。


自己写一个配置类,注入到容器中:

package com.itholmes.boot.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.HiddenHttpMethodFilter;

@Configuration(proxyBeanMethods = false)
public class WebConfig 

    @Bean
    //注意这里有两个HiddenHttpMethodFilter对象,一个是普通servlet使用的,一个是响应式使用的。
    public HiddenHttpMethodFilter hiddenHttpMethodFilter()
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        //设置为自己想要的名字。
        hiddenHttpMethodFilter.setMethodParam("_m");
        return hiddenHttpMethodFilter;
    


2. Web请求参数处理 请求映射原理


DispatcherServlet类实现的过程,例如,实现get方法的过程,也是从上往下:

SpringMvc功能分析都从org.springframework.web.servlet.DispatcherServlet =》doDispatch()方法实现。

分析doDispatch方法:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception 
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try 
        try 
            ModelAndView mv = null;
            Object dispatchException = null;

            try 
                processedRequest = this.checkMultipart(request);
                multipartRequestParsed = processedRequest != request;

				//找到当前请求使用哪个Handler(Controller的方法)处理。
                mappedHandler = this.getHandler(processedRequest);

=================================================================
                
//查看getHandler方法,里面有一个HandlerMapping处理器映射。
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception 
    if (this.handlerMappings != null) 
        Iterator var2 = this.handlerMappings.iterator();

        while(var2.hasNext()) 
            HandlerMapping mapping = (HandlerMapping)var2.next();
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) 
                return handler;
            
        
    

    return null;

通过发送一个路径请求,查看handlerMappings中的内容:

  • RequestMappingHandlerMapping作用就是处理所有@RequestMapping注解 和 handler的映射规则。
  • WelcomePageHandlerMapping是处理欢迎页功能的映射。


SpringBoot自动配置欢迎页的WelcomePageHandlerMapping的源码分析(访问 " / " 能访问到index.html):

因为这里while循环的遍历,依次尝试所有的HandlerMapping看是否有对应路径的请求信息

  • 如果找到了对应的请求handler就会return handler。
  • 如果没有找到就继续遍历下一个handler。

总结起来就是知道RequestMappingHandlerMappin和WelcomePageHandlerMapping在SpringMvc中什么作用!剩余的三个了解

此外,还可以自定义映射处理,我们也可以自己给容器中放HandlerMapping。自定义HandlerMapping。

3. 请求处理常用的参数注解

3.1 SpringMvc常用的一些注解


@PathVariable的用法:

  • @PathVariable(“id”) Integer id://@PathVariable(“id”)拿到对应的值。
  • @PathVariable Map<String,String> path_value://拿到所有的@PathVariable注解对应的key-value(也就是指定@PathVariable注解的value拿指定的,没有指定就拿所有和@PathVariable绑定的key-value。)

@RequestHeader的用法:

  • @RequestHeader(“User-Agent”) String userAgent://@RequestHeader注解拿到请求头的信息。
  • @RequestHeader Map<String,String> headers://和上面一样,拿到所有的请求头信息。指定@RequestHeader注解的value值就拿到对应指定的,没有指定就拿全部的。

@RequestParam的用法:

  • @RequestParam(“age”) Integer age://接受form表单格式单个。
  • @RequestParam(“inters”) List inters://当form表单对一个key指定了多个value可以使用list来接受,例如: ?inters=1&inters=2&inters=4。
  • @RequestParam Map<String,String> params://和上面一样,获取全部的form表单形式的参数。

@CookieValue的用法:

  • @CookieValue(“token”) String value://在cookie中拿到token的value值。
  • @CookieValue(“token”) Cookie cookie://拿到token的整个cookie。

@RequestBody的用法:

  • @RequestBody String content://如果是form表单提交,例如:name=zhangsan&pwd=123123 , 这里的content就会变成“name”:“zhangsan”,“pwd”:“123123”

@RequestAttribute的用法:

3.2 SpringBoot的矩阵变量 和 UrlPathHelper类


什么是矩阵变量,矩阵变量以及对比其他的方式?

  • /cars/path?xxx=xxx&aaa=ccc 这可以是queryString查询字符串,后台用@RequestParam接受。
  • /cars/sell;low=34;brand=byd,audi,yd 使用" ; "的这种形式叫做矩阵变量。

页面开发,cookie禁用了,session里面的内容怎么使用?

  • 可以使用url重写:/abc;jsessionid=xxxx 把cookie的值使用矩阵变量的方式进行传递。

/boss/1/2 对比 /boss/1;age=20/2;age=18 分号;前面是路径,后面是矩阵变量。


SpringBoot默认是禁用了矩阵变量的功能。

原理:在WebMvcAutoConfiguration中,有一个configurePathMatch方法,在该方法中有一个UrlPathHelper(Url路径帮助器) 类,在这个类中,有一个removeSemicolonContent(Semicolon分号)成员变量属性,这个属性就是规定SpringBoot支不支持矩阵变量的(也就是是否移除分号后面的内容。)。

UrlPathHelper类有很多的功能:


如何开启SpringBoot支持矩阵变量的功能?

  • 可以在配置类中自定义配置WebMvcConfigurer容器对象,来修改UrlPathHelper对象中的removeSemicolonContent变量。
package com.itholmes.boot.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;

@Configuration(proxyBeanMethods = false)
/*
    第一种方式:直接implement的WebMvcConfigurer。
        并且重写configure
*/
public class WebConfig implements WebMvcConfigurer 

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) 
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        //设置为false,也就是不移除分号后面的内容,这样矩阵变量可以生效。
        urlPathHelper.setRemoveSemicolonContent(false);
        configurer.setUrlPathHelper(urlPathHelper);
    

    /*
        第二种方式:
            使用@Bean,直接向容器中装配一个WebMvcConfigurer。

        @Bean
        public WebMvcConfigurer webMvcConfigurer()
            return new WebMvcConfigurer() 
                @Override
                public void configurePathMatch(PathMatchConfigurer configurer) 
                    UrlPathHelper urlPathHelper = new UrlPathHelper();
                    //设置为false,也就是不移除分号后面的内容,这样矩阵变量可以生效。
                    urlPathHelper.setRemoveSemicolonContent(false);
                    configurer.setUrlPathHelper(urlPathHelper);
                
            ;
        
    */


获取矩形变量也有格式上的一些要求:

  • 例如: “/cars/path” 。
  • @PathVariable(“path”) String path拿到的就是path里面分号前面的路径。
  • @MatrixVariable(“xxx”) 拿到的就是矩形变量xxx对应的内容。
 /*
    矩形变量注意点:
        1.SpringBoot默认是禁用了矩形变量的功能。
        2.矩形变量必须有url路径变量才能被解析,也就是/cars/sell;low=34 => path sell就是路径,low就是矩形变量。
*/

//第一种情况: 一个key对应一个值,一个key对应多个值。
// 例如矩阵变量的语法为: /cars/sell;low=34;brand=byd,audi,yd
@GetMapping("/cars/path")
public Map carsSell(
        //获取path的路径。
        @PathVariable("path") String path,
        //拿到path中的矩阵变量。
        @MatrixVariable("low") Integer low,
        @MatrixVariable("brand") List<String> brand
)
    HashMap<String, Object> map = new HashMap<>();
    map.put("low",low);
    map.put("brand",brand);
    map.put("path",path);
    return map;


//第二种情况: 两个下的相同key。
// /boss/1;age=20/2;age=18
@GetMapping("/boss/bossId/empId")
public Map boss(
    //通过pathVar来指定获取哪一个的age。
    @MatrixVariable(value = "age" , pathVar = "bossId") Integer bossAge,
    @MatrixVariable(value = "age", pathVar = "empId") Integer empAge
)
    HashMap<String, Object> map = new HashMap<>();
    map.put("bossAge",bossAge);
    map.put("empAge",empAge);
    return map;

4. 请求参数处理 类型参数的解析原理

4.1 找到合适的HandlerAdapter处理器的源码分析


同样和HandlerMapping一样,HandlerAdapter也会是在DispatcherServlet的doDispatch方法中运行:

接下来说的是Handler适配器,HandlerAdapter如何处理一些参数。

对于各种类型参数的解析原理,使用的是适配器HandlerAdapter来解决。

  • handlerMapping中找到能处理请求的handler(controller)。为当前Handler(controller)找一个适配器HandlerAdapter。


上图是SpringMvc默认的4个HandlerAdapter,handler适配器:

  • 0 - RequestMappingHandlerAdapter是支持方法上标注@RequestMapping。
  • 1 - HandlerFunctionAdapter是支持函数式变成的。

执行的核心又在这里:

//拿到能处理的handlerAdapter
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
//在这里执行handlerAdapter
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

4.2 HandlerAdapter的执行目标方法


//在这里执行handlerAdapter
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

mappedHandler.getHandler()的源码解析:

handle方法源码:

继续step into深入,就会看到继承了abstractHandlerMethodAdapter的RequestMappingHandlerAdapter类,在该类有一个handleInternal方法,该方法如下代码:

//这段代码就是来执行目标方法的。就是controller那些类里面

以上是关于接口请求的一些注解使用笔记的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot学习笔记总结

日常笔记:@Deprecated注解的作用及使用

日常笔记:@Deprecated注解的作用及使用

日常笔记:@Deprecated注解的作用及使用

[Spring笔记]支持注解的Spring调度器

Swagger笔记之Swagger注解