RESTful接口web服务
- HTTP协议
- 简介
- 工作原理
- HTTP请求过程
- HTTP请求方式
- HTTP报头
- RESTful接口
- 项目实例
- 项目创建
- 测试
- 总结
HTTP协议
简介
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。。
HTTP是一个基于TCP/IP通信协议来传递数据HTML 文件, 图片文件, 查询结果等
工作原理
HTTP协议工作于客户端-服务端架构上。浏览器作为HTTP客户端通过url向HTTP服务端即web服务器发送请求
- HTTP协议采用请求/相应模式。客户端发送请求,包含方法、url、协议版本等。
- 服务器以一状态行为作为相应,包括协议版本,成功或者失败信息,响应数据等。
web服务器有:Apache IIS
web服务器根据接受请求,向客户端发送相应信息
HTTP默认端口号为80
注意事项:
- HTTP是无连接
每次连接只处理一个请求。服务器处理完客户的请求,收到客户端应答后,断开连接,节省传输时间 - HTTP是媒体独立的
只要客户端和服务器知道如何处理数据内容,任何数据类型都可以通过HTTP发送 - HTTP是无状态的
无状态协议,对事物处理无记忆能力。后续处理需要前面的信息,需要重新进行传输,导致连接传输数据量加大
HTTP请求过程
- 通过DNS对域名进行解析:浏览器缓存->操作系统缓存->hosts文件->DNS服务器
- 通过TCP协议,建立浏览器与服务器端连接
- 浏览器发起HTTP协议请求
- 服务器响应HTTP请求
- 浏览器解析html代码,并请求Html资源
- 浏览器展示内容
- TCP断开连接
HTTP请求方式
请求方法 | 操作方式 |
GET | GET方法要求服务器将url定位的资源放在响应报文的数据部分,回送给客户端 |
POST | 向服务器提交数据,将数据交由服务器处理 |
HEAD | 获取响应头 |
PUT | 替换资源 |
DELETE | 删除资源 |
OPTIONS | 允许客户端查看服务器性能 |
TRACE | 回应服务器收到的请求,用于测试或诊断 |
HTTP报头
一. HTTP请求报文
请求报头由四部分构成,请求行、请求头、空行和请求数据
- 请求行
- 请求头
- ● User-Agent:产生请求的浏览器类型;
● Accept:客户端可识别的响应内容类型列表;星号 “ * ” 用于按范围将类型分组,用 “ / ” 指示可接受全部类型,用“ type/* ”指示可接受 type 类型的所有子类型;
● Accept-Language:客户端可接受的自然语言;
● Accept-Encoding:客户端可接受的编码压缩格式;
● Accept-Charset:可接受的应答的字符集;
● Host:请求的主机名,允许多个域名同处一个IP 地址,即虚拟主机;
● connection:连接方式(close 或 keepalive);
● Cookie:存储于客户端扩展字段,向同一域名的服务端发送属于该域的cookie; - 空行
- 请求数据
请求包体不在 GET 方法中使用,而是在POST 方法中使用。POST 方法适用于需要客户填写表单的场合
二. HTTP响应报文
HTTP响应报文由响应行,响应头,响应体三部分组成
响应行主要包括
响应协议,这个与请求协议对应,比如http,
状态码1xx
状态码的描述OK
响应头就是一些常见的响应名对应的响应值
响应参数就是我们真正需要的从数据库中取出的数据
- 状态码:三位数字组成:第一位数字表示响应类型
(1)1xx: 服务器接收客户请求
(2)2xx: 服务器接收请求并处理
(3)3xx: 服务器要求客户端重定向
(4)4xx: 客户端请求有非法内容
(5)5xx: 服务器未能正常处理出错
状态码实例
(1) 200 OK:表示客户端请求成功;
(2) 400 Bad Request:表示客户端请求有语法错误,不能被服务器所理解;
(3) 404 Not Found:请求的资源不存在,例如,输入了错误的URL;
(4) 500 Internal Server Error:表示服务器发生不可预期的错误,导致无法完成客户端的请求;
(5) 503 Service Unavailable:表示服务器当前不能够处理客户端的请求,在一段时间之后,服务器可能会恢复正常;
- 响应头
响应头包括Location,Server,Vary,Connection等
Location:Location响应报头域用于重定向接受者到一个新的位置
Server:Server 响应报头域包含了服务器用来处理请求的软件信息及其版本
Vary:指示不可缓存的请求头列表
Connection:连接方式; - 空行
- 响应体
服务器返回给客户端的文本信息
RESTful接口
API:一组编程接口规范,客户端与服务端通过请求响应进行数据通信
REST:表述性传递,决定了接口的形式与规则。RESTful是基于http方法的API设计风格
- 通过Url可以知道获取需要什么资源
- 通过http method获取针对资源做什么
- http status code知道结果
当然也不是所有的接口,都能用REST的形式来表述。在实际工作中,灵活运用,我们用RESTful风格的目的是为大家提供统一标准,避免不必要的沟通成本的浪费,形成一种通用的风格。
- RESTful是面向资源的
符合REST接口URI | 功能 |
GET /api/cats/{id} | 获取一个小猫 |
GET /api/cats | 获取所有小猫 |
POST /api/cats | 添加一个小猫 |
PUT /api/cats/{id} | 修改一个小猫 |
DELETE /api/cats/{id} | 删除一个小猫 |
- HTTP方法对资源进行操作
GET : 获取、读取资源
POST : 添加资源
PUT : 修改资源
DELETE : 删除资源 - HTTP状态码
所有事情都按预期正确执行完毕 - 成功
APP 发生了一些错误 – 客户端错误
API 发生了一些错误 – 服务器端错误 - Get方法和查询参数不应改变
修改数据是post,put,delete - 是用复数名词
- 复杂关系固定表达
GET /cars/711/drivers/ 返回 使用过编号711汽车的所有司机 - 高级用法:HATEOAS
返回结果中提供链接,连接其他API方法,用户不查询文档,也能熟知下一步操作可以通过link属性得知下一步用什么Api
{"link": {
"rel": "collection https://www.example.com/zoos",
"href": "https://api.example.com/zoos",
"title": "List of zoos",
"type": "application/vnd.yourformat+json"
}}
- 资源过滤,排序,选择和分页表述
- 版本化Api
项目实例
项目创建
默认操作next,可自行修改Group,Artifact
BaseRequestController
package com.jady.retrofitclientserver.controller;
import com.jady.retrofitclientserver.model.DeleteBody;
import com.jady.retrofitclientserver.model.ServerResult;
import com.jady.retrofitclientserver.model.User;
import com.jady.retrofitclientserver.model.UserForLogin;
import org.springframework.web.bind.annotation.*;
/**
* Created by lipingfa on 2017/6/16.
*/
@RestController
public class BaseRequestController {
/**
* get请求
*
* @return
*/
@GetMapping("/user/info")
public ServerResult getUser() {
ServerResult serverResult = new ServerResult<User>();
serverResult.setServer_time(System.currentTimeMillis());
serverResult.setSuccess(true);
User user = new User("jady", 12);
serverResult.setData(user);
return serverResult;
}
@PostMapping("/user/login")
public ServerResult login(@RequestParam String name, @RequestParam String password) {
ServerResult serverResult = new ServerResult<String>();
serverResult.setServer_time(System.currentTimeMillis());
if ("jady".equals(name) && "1234".equals(password)) {
serverResult.setSuccess(true);
serverResult.setData("addafeas_cdedhyuj_daledage_leiaefss");
} else {
serverResult.setSuccess(false);
serverResult.setErr_code("1000");
serverResult.setMessage("用户名或密码错误");
}
return serverResult;
}
@PostMapping(path = "/user/loginByBody")
public ServerResult login(@RequestBody UserForLogin userForLogin) {
ServerResult serverResult = new ServerResult<String>();
serverResult.setServer_time(System.currentTimeMillis());
if ("jady".equals(userForLogin.getName()) && "1234".equals(userForLogin.getPassword())) {
serverResult.setSuccess(true);
serverResult.setData("addafeas_cdedhyuj_daledage_leiaefss");
} else {
serverResult.setSuccess(false);
serverResult.setErr_code("1000");
serverResult.setMessage("用户名或密码错误");
}
return serverResult;
}
@PutMapping(path = "/user/update")
public ServerResult updateUserInfo(@RequestHeader("access_token") String accessToken, @RequestParam(required = false) String name, @RequestParam(required = false) String age) {
ServerResult serverResult = new ServerResult();
serverResult.setServer_time(System.currentTimeMillis());
if ("1234".equals(accessToken)) {
//更新用户信息
serverResult.setSuccess(true);
} else {
serverResult.setSuccess(false);
serverResult.setErr_code("10001");
serverResult.setMessage("更新接口token错误");
}
return serverResult;
}
@DeleteMapping(path = "/feed/delete")
public ServerResult deleteFeed(@RequestHeader("access_token") String accessToken, @RequestBody DeleteBody body) {
ServerResult serverResult = new ServerResult();
serverResult.setServer_time(System.currentTimeMillis());
if ("1234".equals(accessToken)) {
//删除feed
serverResult.setSuccess(true);
} else {
serverResult.setSuccess(false);
serverResult.setErr_code("10001");
serverResult.setMessage("删除接口token错误");
}
return serverResult;
}
}
FileUploadController
package com.jady.retrofitclientserver.controller;
import com.jady.retrofitclientserver.model.DeleteBody;
import com.jady.retrofitclientserver.model.ServerResult;
import com.jady.retrofitclientserver.model.User;
import com.jady.retrofitclientserver.model.UserForLogin;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.List;
import java.util.Map;
/**
* Created by lipingfa on 2017/6/16.
*/
@RestController
public class FileUploadController {
/**
* 单文件上传
*
* @return
*/
@PostMapping("/file/upload/single")
public ServerResult uploadFile(@RequestParam("file") MultipartFile file) {
ServerResult serverResult = new ServerResult<String>();
serverResult.setServer_time(System.currentTimeMillis());
if (!file.isEmpty()) {
try {
BufferedOutputStream out = new BufferedOutputStream(
new FileOutputStream(new File("files/" + file.getOriginalFilename())));
out.write(file.getBytes());
out.flush();
out.close();
serverResult.setSuccess(true);
} catch (Exception e) {
e.printStackTrace();
serverResult.setSuccess(false);
serverResult.setErr_code("10010");
serverResult.setMessage("上传失败:" + e.getMessage());
}
} else {
serverResult.setSuccess(false);
serverResult.setErr_code("10010");
serverResult.setMessage("上传失败,文件是空的");
}
return serverResult;
}
/**
* 多文件上传
*
* @return
*/
@PostMapping("/file/upload/multiple")
public ServerResult uploadFiles(HttpServletRequest request) {
ServerResult serverResult = new ServerResult<String>();
serverResult.setServer_time(System.currentTimeMillis());
Map<String, MultipartFile> files = ((MultipartHttpServletRequest) request).getFileMap();
BufferedOutputStream stream = null;
for (MultipartFile multipartFile : files.values()) {
if (!multipartFile.isEmpty()) {
try {
byte[] bytes = multipartFile.getBytes();
stream = new BufferedOutputStream(new FileOutputStream(new File("files/" + multipartFile.getOriginalFilename())));
stream.write(bytes);
stream.close();
} catch (Exception e) {
stream = null;
serverResult.setSuccess(false);
serverResult.setErr_code("10010");
serverResult.setMessage("上传失败:" + e.getMessage());
return serverResult;
}
} else {
serverResult.setSuccess(false);
serverResult.setErr_code("10010");
serverResult.setMessage("上传失败,文件是空的");
return serverResult;
}
}
serverResult.setSuccess(true);
return serverResult;
}
}
ResourceManager
package com.example.redis.resource.manager;
public class ResourceManager {
private int count =0;
private static ResourceManager instance = new ResourceManager();
private ResourceManager(){}
public static ResourceManager getInstance(){
return instance;
}
public synchronized void addCount(int i){
count =count+i;
}
public synchronized void minusCount(int i){
count =count+i;
}
public int getCount(){
return count;
}
public void initCount(int i){
count =i;
}
}
测试
POSTMan进行测试:
查询接口,服务启动,count默认值为0
初始化
post:
put:
总结
Postman工作原理
当在postman单机send按钮,服务器将会接受请求并在接口中显示响应
GET
HTTP GET请求方法是从服务器检索数据
数据由唯一的Uri表示
GET请求可以使用“Query String Parameters”将参数传递给服务器
用法:不输入id时,使用默认值