curl
终端命令curl用于在终端内向网站发送请求并获取响应
适合访问、测试API
匿名调用:
1
curl 'http://example.com'非匿名调用:
如调用大模型API,需要获取API key,根据官方文档的样例调用API
API
API即应用程序编程接口,用于将自己的能力通过一个固定的入口持续对外提供,让别的程序来调用
Interface(接口)
接口用于抽象实现,是后端设计的一套供给第三方使用的方法
定义一个接口时,需要写好接口路径和接口方法名的映射,前端通过接口路径来调用方法,进而和后端通信
注意:像HTTP请求这种通信方式是单向的,比如只能前端主动发起请求,而后端不可以主动和前端通信;若后端想主动和前端通信,需要通过双向通信协议如websocket
例:
一个获取商品列表的接口,路径设置为/api/getItemList,接口方法名为:getItemList,这样前端就可以通过请求上述路径来调用上述方法名的方法,进而后端会做相关逻辑处理
接口组成
- 接口路径
- 接口描述
- 请求类型:协议的请求方法,如HTTP协议常用GET/POST
- 请求参数:后台规定好前端需要传的参数结构
- 返回结构:后台规定好返回给前端的数据结构,一般返回结构包括三个部分:返回码、错误信息和正确数据
定义接口
以Golang为例,interface定义在service块的前面,在service块中通过@service注解来引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义 interface
interface 接口名 {
// 这里可以放一些公共的注解,如 @doc、@handler 的前缀等
}
// 在 service 中应用
service 服务名 {
@server(
// 引用 interface,通常写 interface: 接口名
interface: 接口名
)
// 这个@server下面的所有handler都会受该interface的控制
@handler 处理器名
post /api/user/login (LoginReq) returns (LoginResp)
}REST API
REST,即Representational State Transfer,是一种设计网络程序API的风格,而REST API是基于REST原则构建的Web服务接口,又称RESTful
RESTful
如何满足RESTful?
例如现在需要从client端发送请求到server端,我们常用HTTP协议发送请求,而HTTP是无状态的,如果你想要维护某些状态,你需要使用Header将这些状态和每个请求一起发送,这就是REST约束之一:无状态性(Stateless)
所有的数据都会被视作资源(resource),满足CRUD,可以通过URI标识,向服务器发送一个GET请求,服务器返回响应,Body部分一般用JSON表示,这被称为资源导向(Resource-based)
REST API延续使用HTTP方法(GET, POST, PUT, PATCH, DELETE)来明确说明请求意图,即统一接口(Uniform Interface)
另外,我们可以用HTTP缓存机制,如最后修改和电子标签,我们可以标记响应为可缓存或不可缓存,由用户决定,避免提出不必要的请求,这是可缓存性(Cacheable)
设计原则
URI设计原则:
- 使用名词而非动词表示资源
- 使用小写字母和连字符
- 避免文件扩展名
- 分层次表示关系,如
/users/{id}/orders
HTTP方法使用:
- 正确使用HTTP标准方法
- 注意GET、PUT、DELETE是幂等操作,多次调用产生相同结果
- 对于批量操作,考虑使用POST而非PUT,PUT通常期望客户端明确资源标识符
版本控制:
在URL或请求头中包含API版本信息
- URL路径版本:
/v1/users - 查询参数版本:
/api/users?version=1 - 请求头:
Accept: application/vnd.myapi.v1+json - URL路径版本直观但URI不稳定,请求头版本保持URI稳定,但对客户端来说不够直观
- 版本迭代中遵循语义化版本控制原则
- 向后兼容的变更使用次要版本号
- 不兼容的变更使用主要版本号
- 新旧版本之间提供迁移期,允许客户端平滑过渡
- 文档中明确标注每个API版本的生命周期状态
有效分页:
- 使用limit,offset或page,size参数
- 在响应中包含分页元数据
- 在处理大型数据或频繁更新的数据时考虑使用基于游标的分页
- 设置合理分页值
- 在分页响应中提供HATEOAS链接,如下例中的next和prev
例如:
1
2
GET /products?limit=20&offset=40
GET /products?page=3&size=201
2
3
4
5
6
7
8
9
10
11
{
"data": [...],
"pagination": {
"total": 523,
"pages": 27,
"current_page": 3,
"per_page": 20,
"next": "/products?page=4&size=20",
"prev": "/products?page=2&size=20"
}
}游标分页是基于游标的分页方式,使用上一页最后一条记录的标识来确定下一页的起始数据
如靠自增主键id来确定起始位置:
1select * from table_name where id>100 order by id limit 10;这样可以实现从上一页最后一条记录的主键id=100之后开始查询10条记录
过滤、排序与搜索:
对集合资源提供查询参数:
- 过滤:
/user?role=admin - 排序:
/user?order=-created_at - 搜索:
/user?search=john - 支持部分匹配和模糊搜索:
?name=like:john - 支持字段选择:
?fields=id,name,email - 提供预定义的过滤器:
?filter=recent
设计一致的相应结构:
- 使用包装对象,区分数据与元数据
- 保持错误相应格式一致
实现HATEOAS原则:
在响应中包含相关资源链接,使API具有自描述性
- 使用标准化的链接关系名称
- 包含链接上下文信息,如HTTP方法
- 根据用户权限动态生成链接,只显示当前用户可用操作
1
2
3
4
5
6
7
8
9
10
11
12
{
"data": {
"id": 123,
"name": "John Doe"
},
"links": {
"self": "/users/123",
"orders": "/users/123/orders",
"update": {"href": "/users/123", "method": "PUT"},
"delete": {"href": "/users/123", "method": "DELETE"}
}
}安全性:
- API Key:服务器间通信
- OAuth2.0:第三方授权
- JWT:无状态认证
- 需要为不同API的使用场景选择合适的授权流程
- C to S:授权码流程
- S to S:客户端凭证流程
- 实现细粒度的权限控制,遵循最小权限原则
最小权限原则:每个程序和系统用户都应该具有完成任务所必须的最小权限集合,即赋予每一个合法动作最小的权限,来保护数据和功能
即分配他能满足需求所需要的最小的权限
实现速率限制和节流:
- 使用请求速率限制保护API
- 在响应头中提供限制信息
1
2
3
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1623760800- 实现多层级限制:按IP地址、用户/API、资源端点
- 使用令牌桶或漏桶算法处理突发流量
- 实现自适应节流,根据系统负载动态调整限制
适当使用缓存:
- 使用ETags和If-None-Match头
- 设置适当的Cache-Control指令
1
2
Cache-Control: max-age=3600, must-revalidate
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"- 根据资源类型设置不同缓存策略:
- 静态内容:较长的
max-age - 个人资料:较短的
max-age或private指令 - 频繁变化的数据:使用ETag
- 静态内容:较长的
- 实现条件请求减少带宽使用(If-Modified-Since, If-None-Match)
- 考虑在API网关或CDN层面实现缓存
- 提供缓存失效机制
- 使用缓存标签进行细粒度缓存管理