HTTP协议和RESTful API

TCP/IP五层网络架构

  • 应用层(HTTP,RPC等)——直接和应用程序接口并提供常见的网络应用服务
  • 传输层(TCP)——同一个网络中链接的两个节点,如何完成端对端的连接
  • 网络层(IP)——同一个网络的机器如何去协作和传递数据,建立一条路径,保证传输数据的一致
  • 数据链路层——网线两端的设备如何传递数据
  • 物理层——一条网线如何去传输数据

数据链路层和物理层是设备工程师考虑的问题,而应用层、传输层、网络层是Web工程师该考虑的问题

HTTP协议

  • HTTP协议是互联网的基础协议
  • 本质上是Web前端程序和Web后端程序的通信的协议
  • 定义了前端给后端发送请求的格式
  • 同时也定义了后端解析前端发来的请求(HTTP请求)的方式
  • 使用程序语言来描述,HTTP协议给后端程序定义了一个接口
  • 每个请求都是独立的,每个请求都需要显示的附带状态信息(无状态)
  • 互联网上的所有内容都是资源,每个资源有唯一的URL
  • 交互就是对互联网资源的操作(GET,PUT,POST,DELETE)
  • 资源的操作结果都有状态返回码

HTTP请求格式

  • 请求路径——URL,例如:https://pandorawanz.github.io
  • 请求操作命令(动词):GET,POST,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATCH
  • 客户端标记User-Agent,值的离子:Mozilla.5.0(火狐的内核) (Macintosh; Intel Mac OS X 10_10_5)
  • 接收类型Content-Type,值的例子:text/plain,text/html,image/jepg
  • Cookie:用于传送和状态相关的信息的键值对

HTTP回应格式

  • HTTP协议版本:HTTP/1.1
  • HTTPStatus:200,201,404
  • 时间,例如:Sat,29 Sep 2018 07:24:53 GMT
  • Content-Type:text/plain,text/html,image/jpeg
    以上是HTTP协议在程序语义层面的定义,同时也抽象了整个互联网内容的组织和交互方式

常见的Content-Type字段值

  • text/plain
  • text/html
  • text/css
  • image/jpeg
  • image/png
  • image/svg+xml
  • audio/mp4
  • video/mp4
  • application/javascript
  • application/pdf
  • application/zip
  • application/atpm+xml

HTTPStatus

  • 1xx消息——请求已被服务器接收,继续处理
  • 2xx成功——请求已成功被服务器接收、理解、并接受
  • 3xx重定向——需要后续操作才能完成这一请求
  • 4xx请求错误——请求含有词法错误或者无法被执行(客户端责任)
  • 5xx服务器错误——服务器在处理某个正确请求时发生错误(服务端责任)

总结

一个HTTP请求在语义上表达对一个互联网资源(URL)的操作(GET,POST,PUT,Delete),然后Web后端返回资源操作的结果和相关信息

RESTful API

RESTful API就是基于HTTP协议对互联网的内容定义的方式提出的一套互联网应用的架构体系,其中信息以JSON的形式进行存储,对对象的资源、操作、状态
进行定义,RESTful是一种设计模式规范的指南,不是强制性的要求,所以要靠开发者自己遵守

URL基本要求

1.协议:HTTP或HTTPS
2.域名和地址一般格式:http://<域名>/api/
3.域名和地址之后接资源名字统一用复数

写RESTful API步骤

1.想清楚我们的应用里可以抽象出什么样的资源,和他们的层次结构
2.想清楚对对象的基本操作:GET,POST,PUT,DELETE的含义是什么
3.组织接口代码。Spring如何去定义这些路由(URL):URL->Java类方法
4.开始实现

HTTP相关Web编程

采用了Spring框架,在Maven中配置好pom.xml即可,pom.xml相关配置在https://pandorawanz.github.io/2018/10/14/SpringBootStarted/

操作相关注解

  • @RequestMapping
  • @PostMapping
  • @GetMapping
  • @PutMapping
  • @Deletemapping

参数相关注解

  • @RequestBody
  • @PathVariable

User

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class User {
private String name;
private String content;

public User() {
// default constructor for spring mapping
// Spring中不可以省略空构造方法
}

public User(String name, String content) {
this.name = name;
this.content = content;
}

public String getName() {
return name;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

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

UserController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

// 告诉Spring这是个Controller
// 资源的控制,资源的接口合集
@RestController
public class UserController {

// user -> User对象
private final HashMap<String, User> users = new HashMap<>();

// Spring 通过HTTP请求中的操作(GET, POST, PUT, DELETE) + URL确定调用方法来处理请求

/**
* 响应 GET /users 这样的请求
* 查询用户列表
* @return 所有用户列表
*/
@GetMapping("/users")
List<User> listUsers() {
return new ArrayList<>(users.values());
}

/**
* 响应 GET /users/{name}
* 通过User的name查询具体User对象
* @param name
* @return name确定User对象
*/
@GetMapping("/users/{name}")
// 从path中提取出name
ResponseEntity<User> getUser(@PathVariable String name) {
if (users.containsKey(name)) {
return new ResponseEntity<>(users.get(name),HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}

/**
* 响应 POST /users 这样的请求
* 添加一个用户到我们用户列表里
* @param user
* @return 返回创建成功的User对象
*/

/*
@PostMapping("/users")
User newUser(@RequestBody User user) {
users.put(user.getName(),user);
return users.get(user.getName());
}
*/

/**
* 响应 POST /users 这样的请求
* 添加一个用户到我们用户列表里
* @param user
* @return 返回创建成功的User对象
*/
@PostMapping("/users")
ResponseEntity<User> newUser(@RequestBody User user) {
users.put(user.getName(),user);
// 创建成功后返回User对象,以及自定义的状态值201
return new ResponseEntity<>(users.get(user.getName()), HttpStatus.CREATED);
}

/**
* 响应 PUT /users/{name} 这样的请求
* @param name
* @param updatedUser
* @return 修改之后的User对象
*/
@PutMapping("/users/{name}")
ResponseEntity<User> updateUser(@PathVariable String name,@RequestBody User updatedUser) {
if (users.containsKey(name)) {
User user = users.get(name);
user.setContent(updatedUser.getContent());
return new ResponseEntity<>(user,HttpStatus.OK);
} else return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}

/**
* 响应 DELETE /users/{name} 这样的请求
* 删除 name 确定的User对象
* @param name
*/
@DeleteMapping("/users/{name}")
ResponseEntity<Void> deleteUser(@PathVariable String name) {
if (users.containsKey(name)) {
users.remove(name);
return new ResponseEntity<>(HttpStatus.OK);
} else return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}

Application

1
2
3
4
5
6
7
8
9
10
11
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// 告诉Spring从这里启动
@SpringBootApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}