请求处理与响应处理

请求处理与响应处理

声明:本文章属于学习笔记,根据尚硅谷雷丰阳老师的SpringBoot编写
Spring官方文档

一丶Rest映射及源码解析

1丶Rest映射

我们都知道rest风格在我们进行野页面问的时候风格是这个样子的:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户

但是现在我们要是访问方式是这样的:
现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户

所以我们在前台表单是这样的:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/user" method="get">
<input value="REST-GET 提交" type="submit"/>
</form>
<form action="/user" method="post">
  <input value="REST-POST 提交" type="submit"/>
</form>
<form action="/user" method="delete">
    <input value="REST-DELETE 提交" type="submit"/>
</form>
<form action="/user" method="put">
    <input value="REST-PUT 提交" type="submit"/>
</form>
</body>
</html>

我们以前在使用rest风格的时候只要Controller提供相应的映射就可以访问的到:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
但我我们改变了之后我们要怎么才可以访问到呢?
我们来进行测试一下:

package com.kdy.boot05web01.controller;

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

/**
 * @author kdy
 * @create 2021-07-28 14:10
 */
@RestController
public class HelloController {

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public String getUser() {
        return "GET-张三";
    }

    @RequestMapping(value = "/user", method = RequestMethod.POST)
    public String saveUser() {
        return "POST-张三";
    }


    @RequestMapping(value = "/user", method = RequestMethod.PUT)
    public String putUser() {
        return "PUT-张三";
    }

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

    }

}

在这里插入图片描述

我们分别点击这个四个按钮:
我们发现我们点击DELETE和 PUT都出现了一个情况:
在这里插入图片描述
所以在这种情况下我们还要加以改进。

我们看底层源码:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

所以说我们获取前台的表单 this.methodParam之后这个指其实就是_method。
前端界面修改:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/user" method="get">
<input value="REST-GET 提交" type="submit"/>
</form>
<form action="/user" method="post">
  <input value="REST-POST 提交" type="submit"/>
</form>
<form action="/user" method="post">
//必须在这里添加隐藏页
    <input name="_method" value="DELETE" type="hidden"/>
    <input value="REST-DELETE 提交" type="submit"/>
</form>
<form action="/user" method="post">
    <input name="_method" value="PUT" type="hidden"/>
    <input value="REST-PUT 提交" type="submit"/>
</form>
</body>
</html>

ymal配置:

spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true

运行结果:
在这里插入图片描述
我们点击DELETE是显示出这个来了。
但是为什么我们要这么写?我们必须从源码进行解读。

2丶源码解析


		if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
			String paramValue = request.getParameter(this.methodParam);
			if (StringUtils.hasLength(paramValue)) {
				String method = paramValue.toUpperCase(Locale.ENGLISH);
				if (ALLOWED_METHODS.contains(method)) {
					requestToUse = new HttpMethodRequestWrapper(request, method);
				}
			}
		}

我们进行debug:
首先我们可以看见:
在这里插入图片描述
这里就是程序能否继续向下执行的条件。这也照应了上面的表单请求,必须为post请求才可以。
在这里插入图片描述
这个参数再上面也看见过他其实就是_method。所以说,我们首先要看看上面的条件语句也就是请求是否为post,之后才可以执行下面的操作。获取_method的值。在这里也就是DELETE。
在这里插入图片描述

之后就是进行转换,我在前台表单中无论写的value值是大还是小,都是一样转化成大写的DELETE。之后进行判断这个值中是否含有DELETE这个值,我们点进去之后会发现:
在这里插入图片描述

只包括DELETE,PUT,PATCH这三个值。

在这里插入图片描述
之后转到这个请求转发里,我们进入源码其实可以看出,他就是原生的HttpServletRequest

之后调用重写的getMrthod方法,把method传入到HttpMethodRequestWrapper构造方法中,返回给requestToUse。之后通过doFileter方法拦截请求:
在这里插入图片描述

二丶常用参数注解使用

在这里插入图片描述

首先我们看这样的一个案例:

1丶@PathVariable(路径变量)

package com.kdy.boot05web01.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/**
 * @author kdy
 * @create 2021-08-15 20:40
 */
@RestController
public class ParameterController {

    @GetMapping("car/{id}/owner/{username}")
    public Map tset(@PathVariable("id") Integer id,@PathVariable("username") String name,
                    @PathVariable Map<String,String> map){


        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("id",id);
        hashMap.put("name",name);
        hashMap.put("map",map);
        return hashMap;
    }
}

之后我们看这个前台请求的链接:

<ul>
<a href="car/4/owner/kdy">/car/{id}/owner/{username}</a>
</ul>

在这里插入图片描述

我们看见这个是可以获取到的。

2丶@RequestHeader (获取响应头):

在这里插入图片描述

3丶@RequestParam (获取请求参数):

当我们在路径变量中没有写入某个变量的时候,我们可以用@RequestParam获取请求参数

<a href="car/4/owner/kdy?age=18&inters=basketball">/car/{id}/owner/{username}</a>
package com.kdy.boot05web01.controller;

import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author kdy
 * @create 2021-08-15 20:40
 */
@RestController
public class ParameterController {

    @GetMapping("/car/{id}/owner/{username}")
    public Map<String,Object> getCar(@PathVariable("id") Integer id,
                                     @PathVariable("username") String name,
                                     @PathVariable Map<String,String> pv,
                                     @RequestHeader("User-Agent") String userAgent,
                                     @RequestHeader Map<String,String> header,
                                     @RequestParam("age") Integer age,
                                     @RequestParam("inters") List<String> inters,
                                     @RequestParam Map<String,String> params){


        Map<String,Object> map = new HashMap<>();

//        map.put("id",id);
//        map.put("name",name);
//        map.put("pv",pv);
//        map.put("userAgent",userAgent);
//        map.put("headers",header);
        map.put("age",age);
        map.put("inters",inters);
        map.put("params",params);
        return map;
    }
}

在这里插入图片描述

4丶@CookieValue(获取cooki的值):

package com.kdy.boot05web01.controller;

import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author kdy
 * @create 2021-08-15 20:40
 */
@RestController
public class ParameterController {

    @GetMapping("/car/{id}/owner/{username}")
    public Map<String,Object> getCar(@PathVariable("id") Integer id,
                                     @PathVariable("username") String name,
                                     @PathVariable Map<String,String> pv,
                                     @RequestHeader("User-Agent") String userAgent,
                                     @RequestHeader Map<String,String> header,
                                     @RequestParam("age") Integer age,
                                     @RequestParam("inters") List<String> inters,
                                     @RequestParam Map<String,String> params,
                                     @CookieValue("ga") String ga,
                                     @CookieValue("ga") Cookie cookie){


        Map<String,Object> map = new HashMap<>();

//        map.put("id",id);
//        map.put("name",name);
//        map.put("pv",pv);
//        map.put("userAgent",userAgent);
//        map.put("headers",header);
        map.put("age",age);
        map.put("inters",inters);
        map.put("params",params);
        map.put("ga",ga);
        System.out.println(cookie.getName()+"===>"+cookie.getValue());
        return map;
    }
}

在这里插入图片描述

5丶@RequestAttribute(获取request域的属性)

我们可以通过跳转的方式来获取request域的属性:

package com.kdy.boot05web01.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @author kdy
 * @create 2021-08-16 13:33
 */
@Controller
public class RequestAttritubeController {

    @GetMapping("/goto")
    public String into(HttpServletRequest request){
        request.setAttribute("msg","kdy");
        request.setAttribute("code","adada");
        return "forward:/success";
    }

    @ResponseBody
    @GetMapping("/success")
    public Map<String, Object> success(@RequestAttribute("msg") String msg, @RequestAttribute("code") String code ,
                                       HttpServletRequest request){
        Object msg1 = request.getAttribute("msg");
        HashMap<String, Object> map = new HashMap<>();
        map.put("msg1",msg1);
        map.put("msg",msg);
        return map;
    }
}

在这里插入图片描述
运行结果:
在这里插入图片描述

6丶@MatrixVariable(矩阵变量)

首先我们要明白这个三种请求方式:

queryString请求方式
/request?username=admin&password=123456&age=20
rest风格请求
/request/admin/12/20
MatrixVariable矩阵变量
/request;username=admin;password=1236;age=20

由于SpringBoot中默认并没有开启矩阵变量的支持,直接关闭了矩阵变量。因此在使用的时候我们需要对SpringBoot自动装配的Bean对象进行手动的配置更改:

package com.kdy.boot05web01.config;

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

/**
 * @author kdy
 * @create 2021-08-16 19:29
 */
@Configuration(proxyBeanMethods = false)
public class WebConfig  implements WebMvcConfigurer {
    //方法二:
/*    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper pathHelper = new UrlPathHelper();
                //不移除。默认的方法才不生效
                pathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(pathHelper);

            }
        };
    }*/


    //方法一:
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper pathHelper = new UrlPathHelper();
        //不移除。默认的方法才不生效
        pathHelper.setRemoveSemicolonContent(false);
        configurer.setUrlPathHelper(pathHelper);

    }
}

由于MatrixVariable矩阵变量是根据分号进行区分的,而底层默认是移除的也就是说不生效,所以我们才需要进行配置。

前台:
在这里插入图片描述
测试:

    @GetMapping("/cars/{path}")
    public Map carsSell(@MatrixVariable("kk") Integer kk,
                        @MatrixVariable("brand") List<String> brand,
                        @PathVariable("path") String path){
        Map<String,Object> map = new HashMap<>();

        map.put("kk",kk);
        map.put("brand",brand);
        map.put("path",path);
        return map;
    }

运行结果:
在这里插入图片描述

三丶数据响应与内容协商

1丶响应JSON

首先我们是怎么样给前端响应json的,我们还要结合案例和源码一起探究。

导入依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        json依赖
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>

我们必须要导入json依赖才可以进行案例分析。

@Controller
public class ResponsePersonController {
    @ResponseBody
    @GetMapping("/test/person")
    public Person person(){
        Person person = new Person();
        person.setName("kdy");
        person.setAge(21);
        return person;
    }
}

所以这段代码是怎们运行的?
我们一起看一看
debug进入源码到返回值解析器(我们在探究json的时候看的是返回值解析器,其实就像我们在探究各种参数注解时候时候用的参数解析器一样,是一个道理。):
在这里插入图片描述

在这里插入图片描述

	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}

在这里插入图片描述

首先要看对象可以让那个返回值处理器处理,之后再交给返回值处理器进行处理。
在这里插入图片描述在这里要看这个对象是不是异步的也即是不是json,当然现在还没有处理肯定就不是,所以返回false。之后就会交给返回值解析器进行处理,看这个对象类型是否支持处理:
在这里插入图片描述
之后会依次判断那个可以处理这个Person对象:

ModelAndView
Model
View
ResponseEntity 
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
RequestResponseBodyMethodProcessor;

到最后只有RequestResponseBodyMethodProcessor支持:
在这里插入图片描述他是要判断是这个类中有@ResponseBody注解,才可以处理返回值。最后时候消息转换器进行写出操作转换成json串:MessageConverters.

我们看这样的一张图片:
在这里插入图片描述浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型。其中Accept就是浏览器想要接收的类型。也就是内容协商。之后服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据

在这里插入图片描述
一旦匹配上就开始执行。之后SpringMVC会挨个遍历所有容器底层HttpMessageConverter 看谁能处理:
在这里插入图片描述

最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的),这就是person转换成json串的底层原理。

我们也可以发现返回值解析器原理:

1、返回值处理器判断是否支持这种类型返回值 supportsReturnType
2、返回值处理器调用 handleReturnValue 进行处理
3、RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。

  1. 利用 MessageConverters 进行处理 将数据写为json
    1、内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)
    2、服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,
    3、SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理:
    (1)、得到MappingJackson2HttpMessageConverter可以将对象写为json
    (2)、利用MappingJackson2HttpMessageConverter将对象转为json再写出去。

2丶内容协商

上面已经简单的介绍过内容协商了。现在我们要进一步的了解探究一下。
首先我们看这样的一个案例:
我们现在看到的是这样的格式:
在这里插入图片描述
在这里插入图片描述

我们可以看见请求头接收的先后顺序,他是比json要早的。我们可以用postman进行测试一下:
在这里插入图片描述

我们可以看见Accept接收的是* / * 所以返回的是什么形式都行,但是当我们修改的时候,我们可以看见:
在这里插入图片描述
在这里插入图片描述

不同的Accept会产生不同的文件形式,这也就是SpringMvc在底层提供的内容协商功能。他会根据客户端的能力的不同来处理数据。
原理:
HeaderContentNegotiationStrategy 确定客户端可以接收的内容类型
在这里插入图片描述
之后遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象(Person)
找到支持操作Person的converter,把converter支持的媒体类型统计出来。客户端需要【application/xml】。服务端能力【10种、json、xml】,进行内容协商的最佳匹配媒体类型
用 支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。

在这里插入图片描述

四丶视图解析

1丶 模板引擎-Thymeleaf

首先我们用Thymeleaf来看一个小案例:
我们前端首先用一下这个Thymeleaf的模板:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 th:text="${msg}">哈哈</h1>
<h2>
    <a href="www.atguigu.com" th:href="${link}">去小破站</a>  <br/>
    <a href="www.atguigu.com" th:href="@{/link}">去小破站2</a>
</h2>
</body>
</html>

controller:

package com.kdy.boot05web01.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * @author kdy
 * @create 2021-08-21 11:34
 */
@Controller
public class SuccessController {
    @GetMapping("/su")
    public String success(Model model){

        model.addAttribute("msg","kdy");
        model.addAttribute("link","https://www.bilibili.com/");
        return "success";
    }
}

在这里插入图片描述

我们看下面的一张图:
在这里插入图片描述

${link}也就是你要去的地址,@{/link}也就是你要去的当前项目的根路径下的link,也就是我们本项目的的link,这也就是 $ 和 @的区别。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值