SpringBoot-Web开发-请求映射与请求参数处理

目录

一、请求映射

1.1 Rest风格

1.2 Rest风格的原理

1.3 请求映射的原理

二、参数处理

2.1 普通参数注解

2.2 @MatrixVariable注解

2.3 ServletAPI 类型的参数

2.4 复杂参数

2.5 自定义对象参数

三、参数处理原理

3.1 执行目标方法

3.2 参数解析器-HandlerMethodArgumentResolver

3.3 返回值处理器 HandlerMethodReturnValueHandler

3.4 自定义类型参数封装POJO

3.5 目标方法执行完成


一、请求映射

1.1 Rest风格

        @RequestMapping注解可以用下面几个注解代替

@Getmapping    @PostMapping    @DeleteMapping    @PutMapping

        分别代表 获取、保存、删除、修改的请求类型。url采用路径式的风格。

SpringBoot-Web开发-请求映射与请求参数处理_第1张图片

         使用RestFul风格提交请求时,要先在配置文件开启Rest功能,然后表单要加上一个隐藏域,表明请求方式。

        application.yml:

spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true   #开启页面表单的Rest功能

        index.html:

        controller:

@RestController
public class testRESTful {
    @GetMapping("/user")
    public String testGet(){
        return "get user";
    }

    @PostMapping("/user")
    public String testPost(){
        return "post user";
    }

    @PutMapping("/user")
    public String testPut(){
        return "put user";
    }

    @DeleteMapping("/user")
    public String testDelete(){
        return "delete user";
    }
}

1.2 Rest风格的原理

Rest原理(表单提交要使用REST的时候)

  • 表单提交会带上隐藏域的参数:_method=PUT/DELETE
  • 请求过来被HiddenHttpMethodFilter拦截
  • 判断表单请求类型是否是POST,只有是POST才是REST风格
  • 获取_method的参数,判断是PUT还是DELETE、PATCH
  • 过滤器HttpMethodRequestWrapper重写了getMethod方法,封装_method的值,这里用到了包装模式,返回的是我们想要的请求类型

  • 过滤器链放行的时候用wrapper,以后的方法调用getMethod是调用HttpMethodRequestWrapper的,就是包装后的,而不是原生的。这样就可以使用PUTDELTETE请求啦。

1.3 请求映射的原理

         doGet(HttpServlet)->processRequest(HttpServletBean)->

        doService(FramworkServlet)->doDispatch(DispatchServlet)

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// 找到当前请求使用哪个Handler(Controller的方法)处理
				mappedHandler = getHandler(processedRequest);
        ... ...
  • 所有请求都会调用DispatchServletdoDispatch方法,在该方法中找到当前请求使用哪个Handler(Controller中的方法)进行处理。
  • 如何找呢?通过handlerMapping(处理器映射)

SpringBoot-Web开发-请求映射与请求参数处理_第2张图片

  • 总共有五个handlerMapping,其中第一个保存了所有@RequestMapping 注解和handler映射规则,就是我们用户标注的那些控制器方法。SpringMVC启动时将所有@RequestMapping 注解信息保存到这个handlerMappingmappingRegistry中。
  • 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
    • 如果有就找到这个请求对应的handler
    • 如果没有就是下一个 HandlerMapping

二、参数处理

2.1 普通参数注解

@PathVariable        Rest风格路径动态的参数映射到处理器方法的形参

@RequestHeader   请求头信息和控制器方法形参创建映射关系

@ModelAttribute

@RequestParam    请求参数控制器方法形参创建映射关系

@MatrixVariable      获取矩阵变量

@CookieValue         cookie数据控制器方法形参创建映射关系

@RequestBody        获取请求体

@ResponseBody        标注方法返回值作为响应体

@RequestAttritube        获取request域属性(页面跳转时取域数据)

@PathVariable
@RestController
public class testParam {
    @GetMapping("/car/{id}/owner/{username}")
    public Map getCar(@PathVariable("id") Integer id,@PathVariable Map vars){
        Map map=new HashMap<>();
        map.put("id",id);
        map.put("vars",vars);
        return map;
    }
}
@RequestHeader
@RestController
public class testParam {
    @GetMapping("/car/{id}/owner/{username}")
    public Map getCar(@RequestHeader  Map headers,@RequestHeader("User-Agent") String header){
        Map map=new HashMap<>();
        map.put("header",header);//某个key 对应的 value
        map.put("headers",headers);//所有key 对应的 value
        return map;
    }
}


结果:
{"headers":{"host":"localhost:8080","connection":"keep-alive","sec-ch-ua":"\" Not;A Brand\";v=\"99\", \"Microsoft Edge\";v=\"103\", \"Chromium\";v=\"103\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Windows\"","upgrade-insecure-requests":"1","user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36 Edg/103.0.1264.77","accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9","sec-fetch-site":"same-origin","sec-fetch-mode":"navigate","sec-fetch-user":"?1","sec-fetch-dest":"document","referer":"http://localhost:8080/index.html","accept-encoding":"gzip, deflate, br","accept-language":"zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6","cookie":"Idea-78910da9=e89c6d10-8e05-4fd7-8e94-8d41c1b52e21"},"header":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36 Edg/103.0.1264.77"}
@RequestParam
@RestController
public class testParam {
    @GetMapping("/car/{id}/owner/{username}")
    public Map getCar(@RequestParam("age") Integer age,@RequestParam Map params){
        Map map=new HashMap<>();
        map.put("age",age);
        map.put("params",params);
        return map;
    }
}


结果:
{"params":{"age":"18","interest":"rap"},"age":18}
@CookieValue
@RestController
public class testParam {
    @GetMapping("/car/{id}/owner/{username}")
    public Map getCar(@CookieValue("Idea-78910da9") String c){
        Map map=new HashMap<>();
        map.put("Idea-78910da9",c);
        return map;
    }
}


结果:
{"Idea-78910da9":"e89c6d10-8e05-4fd7-8e94-8d41c1b52e21"}

汽车品牌: 汽车型号:
@RestController
public class testParam {
    @PostMapping("/car")
    public Map getCar(@RequestBody String body){
        Map map=new HashMap<>();
        map.put("RequestBody",body);
        return map;
    }

}


结果:
{"RequestBody":"brand=Toyota & model=Camary"}

2.2 @MatrixVariable注解


矩阵变量请求是一种新的请求风格,严格来说矩阵变量的请求需要用到rest风格但是又不同于rest.

//queryString请求方式
/cars?brand=audi&model=A6&price=500000

//REST请求
/cars/audi/A6/500000

//MatrixVariable矩阵变量
/cars;brand=audi;model=A6;price=500000

        这个功能默认关闭的,要在配置类中手动开启:

@Configuration
public class myConfig implements WebMvcConfigurer {
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);//矩阵变量功能生效
        configurer.setUrlPathHelper(urlPathHelper);
    }
}

        下面是遇到一个path时的情况: 

@MatrixVariable
@GetMapping("/cars/{path}")
public Map carsSell(@MatrixVariable("brand") String brand,
                     @MatrixVariable("model") String model,
                     @MatrixVariable("price") List price,
                     @PathVariable("path") String path){
     Map map = new HashMap<>();
     map.put("brand",brand);
     map.put("model",model);
     map.put("price",price);
     map.put("path",path);
     return map;
}


结果:
{"path":"sell","price":["500000","4000000"],"model":"A6","brand":"audi"}

        下面是遇到多个path时的情况:

@MatrixVariable
@GetMapping("/cars/{id1}/{id2}")
    public Map carBrand(@MatrixVariable(value = "brand", pathVar = "id1") String id1brand,
                        @MatrixVariable(value = "brand", pathVar = "id2") String id2brand){
        Map map = new HashMap<>();
        map.put("id1brand",id1brand);
        map.put("id2brand",id2brand);
        return map;
    }


结果:
{"id1brand":"audi","id2brand":"benz"}

2.3 ServletAPI 类型的参数

WebRequestServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId

        如何获取到这些参数呢?

        ServletRequestMethodArgumentResolver 帮我们解析以上部分参数的。

2.4 复杂参数

        如MapModel(map、model里面的数据会被放在request的请求域)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder

         Map、Model类型的参数,会返回 mavContainer.getModel();---> BindingAwareModelMapModeMap类型的,最终都会在DispatcherServlet封装成一个ModelAndView

        如何使用已经在SpringMVC中介绍了。

2.5 自定义对象参数

        就是自动将表单提交的内容与javaBean的属性相对应。可以自动类型转换与格式化,可以级联封装。

/**
 *     姓名:  
* 年龄:
* 生日:
* 宠物姓名:
* 宠物年龄: */
@Data
public class Person {
    
    private String userName;
    private Integer age;
    private Date birth;
    private Pet pet;
    
}

@Data
public class Pet {

    private String name;
    private String age;

}

三、参数处理原理

  • HandlerMapping中找到能处理请求的Handler(Controller.method())
  • 为当前Handler 找一个适配器 HandlerAdapterRequestMappingHandlerAdapter
  • 适配器执行目标方法并确定方法参数的每一个值

3.1 执行目标方法

// Actually invoke the handler.
//DispatcherServlet -- doDispatch
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
mav = invokeHandlerMethod(request, response, handlerMethod); //执行目标方法
//ServletInvocableHandlerMethod
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//获取方法的参数值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

3.2 参数解析器-HandlerMethodArgumentResolver

        确定将要执行的目标方法的每一个参数的值是什么;SpringMVC目标方法能写多少种参数类型。取决于参数解析器

SpringBoot-Web开发-请求映射与请求参数处理_第3张图片

         解析器挨个判断是否支持解析这种参数,如果支持就调用 resolveArgument 方法解析

	@Nullable
	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		if (result == null) {
			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
				if (resolver.supportsParameter(parameter)) {
					result = resolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}

3.3 返回值处理器 HandlerMethodReturnValueHandler

SpringBoot-Web开发-请求映射与请求参数处理_第4张图片

        这个处理器会根据返回值类型调用对应的处理类进行处理。 

返回值类型 处理类
String(方法上无ResponseBody注解) ViewNameMethodReturnValueHandler
View ViewMethodReturnValueHandler
ModelAndView ModelAndViewMethodReturnValueHandle
Model ModelMethodProcessor
Map MapMethodProcessor
HttpHeaders HttpHeadersReturnValueHandler
ModelAttribute(或者是自定义对象) ModelAttributeMethodProcessor
HttpEntity HttpEntityMethodProcessor
ResponseEntity ResponseBodyEmitterReturnValueHandler
ResponseBody注解 RequestResponseBodyMethodProcessor

3.4 自定义类型参数封装POJO

        由ServletModelAttributeMethodProcessor 参数处理器处理,它支持自定义参数绑定,在底层使用了WebDataBinder数据绑定器,而数据绑定器中有一个conversionServiceconversionService注册了很多convertersconverters会帮助我们进行类型转换

3.5 目标方法执行完成

        将所有的数据都放在 ModelAndViewContainer;包含要去的页面地址View,还包含Model数据。接下来还是封装成 ModelAndView,最后处理派发结果数据放到请求域

你可能感兴趣的:(SpringBoot,spring,boot,java)