前端学习:HTTP协议、请求响应、分层解耦

HTTP协议

HTTP-概述

  HTTP:Hyper Text Transfer Protocol(超文本传输协议),规定了浏览器与服务器之间数据传输的规则。如果想知道http协议的数据传输格式有哪些,可以打开浏览器,点击 F12 打开开发者工具,点击Network 来查看。

  浏览器和服务器是按照HTTP协议进行数据通信的。

	HTTP协议又分为:请求协议和响应协议
		请求协议:浏览器将数据以请求格式发送到服务器
			包括:请求行、请求头 、请求体
		响应协议:服务器将数据以响应格式返回给浏览器
			包括:响应行 、响应头 、响应体

  浏览器向服务器进行请求时,服务器按照固定的格式进行解析:
前端学习:HTTP协议、请求响应、分层解耦_第1张图片

  服务器向浏览器进行响应时,浏览器按照固定的格式进行解析:
在这里插入图片描述
学习HTTP主要是学习请求和响应数据的具体格式内容。

HTTP的特点:
	1、基于TCP协议:面向连接,安全
	2、基于请求-响应模型: 一次请求对应一次响应(先请求后响应)
	3、HTTP协议是无状态协议: 对于数据没有记忆能力。每次请求-响应都是独立的

HTTP-请求协议

  在实际应用中常用的有 :GET、POST

请求方式 请求说明
GET 获取资源向特定的资源发出请求。
POST 传输实体主体,向指定资源提交数据进行处理请求(例:上传文件),数据被包含在请求体中。

GET方式的请求协议

前端学习:HTTP协议、请求响应、分层解耦_第2张图片

请求行 :HTTP请求中的第一行数据。由: 请求方式 、 资源路径 、 协议/版本 组成(之间使用空格分隔)
	请求方式:GET
	资源路径:/brand/findAll?name=OPPO&status=1
	请求路径:/brand/findAll
	请求参数:name=OPPO&status=1
		请求参数是以key=value形式出现
		多个请求参数之间使用 & 连接
		请求路径和请求参数之间使用 ? 连接
	协议/版本:HTTP/1.1

.
  http是个无状态的协议,所以需要在请求头设置浏览器的一些自身信息和想要响应的形式。这样服务器在收到信息后,就可以知道是谁,想干什么了。

请求头 :第二行开始,上图黄色部分内容就是请求头。格式为key: value形式
常见的HTTP请求头有:
	Host: 表示请求的主机名
	User-Agent: 浏览器版本。
	Accept:表示浏览器能接收的资源类型,如text/*,image/*或者*/*表示所有;
	Accept-Language:表示浏览器偏好的语言,服务器可以据此返回不同语言的网页;
	Accept-Encoding:表示浏览器可以支持的压缩类型,例如gzip, deflate等
	Content-Type:请求主体的数据类型
	Content-Length:数据主体的大小(单位:字节)

.

请求体 :存储请求参数
	GET请求的请求参数在请求行中,故不需要设置请求体

POST方式的请求协议

前端学习:HTTP协议、请求响应、分层解耦_第3张图片

请求行(以上图中红色部分):包含请求方式、资源路径、协议/版本
	请求方式:POST
	资源路径:/brand
	协议/版本:HTTP/1.1
请求头(以上图中黄色部分)
请求体(以上图中绿色部分) :存储请求参数
请求体和请求头之间是有一个空行隔开(作用:用于标记请求头结束)

GET请求和POST请求的区别

区别方式 GET请求 POST请求
请求参数 请求参数在请求行中。例:/brand/findAll?name=OPPO&status=1 请求参数在请求体中
请求参数长度 请求参数长度有限制(浏览器不同限制也不同) 请求参数长度没有限制
安全性 安全性低。原因:请求参数暴露在浏览器地址栏中。 安全性相对高

HTTP-响应协议

  http是个无状态的协议,所以可以在请求头和响应头中设置一些信息和想要执行的动作,这样,对方在收到信息后,就可以知道你是谁,你想干什么。
前端学习:HTTP协议、请求响应、分层解耦_第4张图片

响应行(以上图中红色部分):响应数据的第一行。响应行由 协议及版本 、 响应状态码 、 状态码描述 组成
	协议/版本:HTTP/1.1
	响应状态码:200
	状态码描述:OK
	
响应头(以上图中黄色部分):响应数据的第二行开始。格式为key:value形式
常见的HTTP响应头有:
	Content-Type:表示该响应内容的类型,例如text/html,image/jpeg ;
	Content-Length:表示该响应内容的长度(字节数);
	Content-Encoding:表示该响应压缩算法,例如gzip ;
	Cache-Control:指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒 ;
	Set-Cookie: 告诉浏览器为当前页面所在的域设置cookie ;
	
响应体(以上图中绿色部分): 响应数据的最后一部分。存储响应的数据
响应体和响应头之间有一个空行隔开(作用:用于标记响应头结束)

响应状态码

状态码 分类说明
1xx 响应中 ----> 临时状态码。表示请求已经接受,告诉客户端应该继续请求或者如果已经完成则忽略
2xx 成功 ----> 表示请求已经被成功接收,处理已完成
3xx 重定向 ----> 重定向到其它地方,让客户端再发起一个请求以完成整个处理
4xx 客户端错误 ----> 处理发生错误,责任在客户端,如:客户端的请求一个不存在的资源,客户端未被授权,禁止访问等
5xx 服务器端错误 ----> 处理发生错误,责任在服务端,如:服务端抛出异常,路由出错,HTTP版本不支持等

  关于响应状态码,先主要认识三个状态码,其余的等后期用到了再去掌握:

200    ok  ---->  客户端请求成功
404  Not Found  ---->  请求资源不存在
500  Internal Server Error  ----> 服务端发生不可预期的错误

请求响应简介

请求响应:
	请求(HttpServletRequest):获取请求数据
	响应(HttpServletResponse):设置响应数据

常用的架构有BS架构和CS架构。
  BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。常用的架构,网页访问。
  CS架构:Client/Server,客户端/服务器架构模式。需要下载APP

请求

postman

  当前最为主流的开发模式:前后端分离。其中,GET请求适合用于获取资源、查询数据,而POST请求适合用于提交表单、上传文件等操作。  浏览器通过网址发起的请求全部都是GET请求;而POST请求的数据对用户来说是不可见的,如果想要测试POST请求就会很不方便,Postman应运而生。Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件,常用于进行接口测试。

简单参数

   简单参数是指向服务器发起请求时,向服务器传递的是一些普通的请求数据。在 http://localhost:8080/simpleParam?name=Tom&age=10 中 name 和 age 就是简单参数。
   在后端程序中,通常使用SpringBoot方式接收传递过来的普通参数数据。在Springboot的环境中,对原始的API进行了封装,接收参数的形式更加简单。简单参数,参数名与形参变量名相同,定义同名的形参即可接收参数。

@RestController
public class RequestController {
// http://localhost:8080/simpleParam?name=Tom&age=10
// 第1个请求参数: name=Tom  参数名:name,参数值:Tom
// 第2个请求参数: age=10  参数名:age , 参数值:10

//springboot方式
@RequestMapping("/simpleParam")
	public String simpleParam(String name , Integer age ){ //形参名和请求参数名保持一致
		System.out.println(name+"  :  "+age);
		return "OK";
	}
}

  不论是GET请求还是POST请求,对于简单参数来讲,只要保证请求参数名和Controller
方法中的形参名保持一致 ,就可以获取到请求参数中的数据值。
  如果在开发中,遇到请求参数名和controller方法中的形参名不相同时,可以使用Spring提供的 @RequestParam 注解完成映射。在方法形参前面加上 @RequestParam 然后通过value属性执行请求参数名,从而完成映射。注意:@RequestParam中的required属性默认为true,代表该请求参数必须传递,如果不传递将报错。

@RequestMapping("/simpleParam")
   public String simpleParam(@RequestParam(name = "name", required =
           false) String username, Integer age){
       System.out.println(username+ ":" + age);
       return "OK";
   }

实体参数

  在使用简单参数做为数据传递方式时,前端传递了多少个请求参数,后端controller方法中的形参就要书写多少个。如果请求参数比较多,则需要一个参数一个参数的接收,比较繁琐。此时,可以将请求参数封装到一个实体类对象中。
  要想完成数据封装,需要遵守规则:请求参数名与实体类的属性名相同
前端学习:HTTP协议、请求响应、分层解耦_第5张图片

简单实体对象

  1、定义POJO实体类

package com.sprintbot1example.pojo;

public class User {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

  2、Controller方法:

@RequestMapping("/simplePojo")
    public String simplePojo(Userzzz sur){
        System.out.println(sur);
        return "ok";
    }
复杂实体对象

  复杂实体对象指的是,在实体类中有一个或多个属性,也是实体对象类型的。如:User类中有一个Address类型的属性(Address是一个实体类,有两个province和city属性)
  复杂实体对象的封装,需要遵守如下规则:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套实体类属性参数。
前端学习:HTTP协议、请求响应、分层解耦_第6张图片
  1、定义POJO实体类:
Address实体类

package com.sprintbot1example.pojo;

public class Adress {
    private String province;
    private String city;

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        return "Adress{" +
                "province='" + province + '\'' +
                ", city='" + city + '\'' +
                '}';
    }
}

User实体类

package com.sprintbot1example.pojo;

public class Userzzz {
    private String name;
    private Integer age;
    private Address address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Userzzz{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address=" + address +
                '}';
    }
}

  2、Controller方法:

@RequestMapping("/simplePojo")
    public String simplePojo(Userzzz sur){
        System.out.println(sur);
        return "ok";
    }

数组集合参数

  数组集合参数的使用场景:在HTML的表单中,有一个表单项是支持多选的(复选框),可以提交选择的多个值。多个值是怎么提交的呢?其实多个值也是一个一个的提交。
前端学习:HTTP协议、请求响应、分层解耦_第7张图片

后端程序接收上述多个值的方式有两种:数组集合

数组

  数组参数:请求参数名与形参数组名称相同且请求参数为多个,定义数组类型形参即可接收参数。
前端学习:HTTP协议、请求响应、分层解耦_第8张图片

@RequestMapping("/arrayParam")
    public String arrayParam(String[] hobby){
        System.out.println(Arrays.toString(hobby));
        return "ok";
    }
在前端请求时,有两种传递形式:
	1、xxxxxxxxxx?hobby=game&hobby=java
	2、xxxxxxxxxxxxx?hobby=game,java
集合

  集合参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam 绑定参数关系默认情况下,请求中参数名相同的多个值,是封装到数组。如果要封装到集合,要使用@RequestParam绑定参数关系
前端学习:HTTP协议、请求响应、分层解耦_第9张图片

@RequestMapping("/listParam")
    public String listParam(@RequestParam List<String> hobby){
        System.out.println(hobby);
        return "ok";
    }

日期参数

  在一些特殊的需求中,可能会涉及到日期类型数据的封装。因为日期的格式多种多样(如:2022-12-12 10:05:45 、2022/12/12 10:05:45),那么对于日期类型的参数在进行封装的时候,需要通过@DateTimeFormat注解,以及其pattern属性来设置日期的格式。
前端学习:HTTP协议、请求响应、分层解耦_第10张图片

Json参数

  JSON是开发中最常用的前后端数据交互方式。

学习JSON格式参数,主要从以下两个方面着手:
	1. Postman在发送请求时,如何传递json格式的请求参数
	2. 在服务端的controller方法中,如何接收json格式的请求参数
Postman发送JSON格式数据

前端学习:HTTP协议、请求响应、分层解耦_第11张图片

服务端Controller方法接收JSON格式数据

  传递json格式的参数,在Controller中会使用实体类进行封装。
  封装规则:JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数。需要使用@RequestBody标识。
前端学习:HTTP协议、请求响应、分层解耦_第12张图片

//josn 参数
    @RequestMapping("/JSONParam")
    public String JSONParam(@RequestBody Userzzz userzzz){
        System.out.println(userzzz);
        return "OK";
    }

路径参数

  传统的开发中请求参数是放在请求体(POST请求)传递或跟在URL后面通过?key=value的形式传递(GET请求)。而在现在开发中,经常会直接在请求的URL中传递参数。
  学习路径参数,主要掌握在后端的controller方法中,如何接收路径参数。

路径参数:
	前端:通过请求URL直接传递参数
	后端:使用{…}来标识该路径参数,需要使用@PathVariable获取路径参数
传递单个路径参数

前端学习:HTTP协议、请求响应、分层解耦_第13张图片

// 路径
    @RequestMapping("/path/{id}")
    public String pathParam(@PathVariable Integer id){
        System.out.println(id);
        return "OK";
    }
传递多个路径参数
// 路径
    @RequestMapping("/path/{id}/{name}")
    public String pathParam2(@PathVariable Integer id,
                             @PathVariable String name){
        System.out.println(id+":"+name);
        return "OK";
    }

响应

  Controller程序,除了接收请求外,还可以进行响应。controller方法中的return的结果,使用@ResponseBody注解就可以响应给浏览器。

@ResponseBody注解:
	类型:方法注解、类注解
	位置:书写在Controller方法上或类上
	作用:将方法返回值直接响应给浏览器
	如果返回值类型是实体对象/集合,将会转换为JSON格式后在响应给浏览器

  但是,在所书写的Controller中,只在类上添加了RestController注解、方法添加了@RequestMapping注解,并没有使用@ResponseBody注解,怎么给浏览器响应呢?原因是:在类上添加的@RestController注解,是一个组合注解。@RestController = @Controller + @ResponseBody

前端学习:HTTP协议、请求响应、分层解耦_第14张图片

  在上述所编写的Controller方法中,返回值各种各样,没有任何的规范。在实际开发中,controller方法将成千上万,若controller返回值没有规范将造成整个项目难以维护。在实际项目开发中,无论是哪种方法,都会定义一个统一的返回结果。方案如下:
前端学习:HTTP协议、请求响应、分层解耦_第15张图片

统一的返回结果使用类来描述,在这个结果中包含:
	响应状态码:当前请求是成功,还是失败
	状态码信息:给页面的提示信息
	返回的数据:给前端响应的数据(字符串、对象、集合)

.
  定义在一个实体类Result来包含以上信息。代码如下:

/**
 * 统一响应结果封装类
 */
public class Result {
    private Integer code ;//1 成功 , 0 失败
    private String msg; //提示信息
    private Object data; //数据 data

    public Result() {
    }
    public Result(Integer code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }

    public static Result success(Object data){
        return new Result(1, "success", data);
    }
    public static Result success(){
        return new Result(1, "success", null);
    }
    public static Result error(String msg){
        return new Result(0, msg, null);
    }

    @Override
    public String toString() {
        return "Result{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }
}
@RequestMapping("/listParam")
    public Result listParam(@RequestParam List<String> hobby){
        System.out.println(hobby);
        return Result.success(hobby);
    }

Springboot项目的静态资源(html,css,js等前端资源)默认存放目录为:classpath:/static
、 classpath:/public、 classpath:/resources

分层解耦

  在进行程序设计以及程序开发时,应尽可能遵循单一职责原则,即一个类或一个方法,就只做一件事情,只管一块功能。这样可以让类、接口、方法的复杂度更低,可读性更强,扩展性更好,也更利用后期的维护。

三层架构

在项目开发中按处理逻辑,从组成上看可以分为三个部分:
	数据访问:负责业务数据的维护操作,包括增、删、改、查等操作。
	逻辑处理:负责业务逻辑处理的代码。
	请求处理、响应数据:负责,接收页面的请求,给页面响应数据。

按照上述对应的三个组成部分,如下:
	Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
	Service:业务逻辑层。处理具体的业务逻辑。
	Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、删、改、查。

基于三层架构的程序执行流程:
	1、前端发起的请求,由Controller层接收(Controller响应数据给前端)
	2、Controller层调用Service层来进行逻辑处理(Service层处理完后,把处理结果返回给Controller层)
	3、Serivce层调用Dao层(逻辑处理过程中需要用到的一些数据要从Dao层获取)
	4、Dao层操作文件中的数据(Dao拿到的数据会返回给Service层)

前端学习:HTTP协议、请求响应、分层解耦_第16张图片

用三层架构改写如下:

前端学习:HTTP协议、请求响应、分层解耦_第17张图片

解耦

  解耦:即解除耦合。软件开发涉及到的两个概念:内聚和耦合。内聚:指软件中各个功能模块内部的功能联系。耦合:指衡量软件中各个层/模块之间的依赖、关联的程度。
  软件设计原则:高内聚低耦合。
  高内聚指的是:一个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即 “高内聚”。
  低耦合指的是:软件中各个层、模块之间的依赖关联程序越低越好。

  解耦思路是提供一个容器,容器中存储一些对象,controller程序从容器中获取EmpService类型的对象。要实现上述解耦操作,就涉及到Spring中的两个核心概念:
  控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为:IOC容器或Spring容器。OC容器中创建、管理的对象,称之为:bean对象
  依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。程序运行时需要某个资源,此时容器就为其提供这个资源。

步骤:			
	1.将Service层及Dao层的实现类,交给IOC容器管理
	  使用Spring提供的注解:@Component ,就可以实现类交给IOC容器管理
	  
	2.为Controller及Service注入运行时依赖的对象
	  使用Spring提供的注解:@Autowired ,就可以实现程序运行时IOC容器自动注入需要的依赖对象
		Controller程序中注入依赖的Service层对象
		Service程序中注入依赖的Dao层对象

IOC详解

  IOC容器创建的对象称为bean对象。要把某个对象交给1OC容器管理,需要在对应的类上加上如下注解之一:

注解 说明 位置
@Component 声明bean的基础注解 不属于以下三类时,用此注解
@Controller @Component的衍生注解 标注在控制器类上
@Service @Component的衍生注解 标注在业务类上
@Repository @Component的衍生注解 标注在数据访问类上(由于与mybatis整合,用的少)
注意事项:
	声明bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认为类名首字母小写。
	使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。

  bean想要生效,还需要被组件扫描注解@ComponentScan扫描。@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解@SpringBootApplication中,默认扫描的范围是启动类所在包及其子包。

DI详解

  依赖注入,是指IOC容器要为应用程序去提供运行时所依赖的资源,而资源指的就是对象。@Autowired注解,默认是按照类型进行自动装配的(去IOC容器中找某个类型的对象,然后完成注入操作)。如果在IOC容器中,存在多个相同类型的bean对象,则会报错。Spring提供了以下几种解决方案:
  使用@Primary注解:当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现。

前端学习:HTTP协议、请求响应、分层解耦_第18张图片

  使用@Qualifier注解:指定当前要注入的bean对象。 在@Qualifier的value属性中,指定注入的bean的名称。@Qualifier注解不能单独使用,必须配合@Autowired使用

在这里插入图片描述

  使用@Resource注解:是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。

前端学习:HTTP协议、请求响应、分层解耦_第19张图片

你可能感兴趣的:(前后端学习,前端,学习,http)