浏览 AI使用教程
API 接口对接流程:先核对认证、请求、响应和错误格式
API 对接不要从复制成功响应开始。认证方式、请求头、字段类型、错误结构和最小可运行请求,才是双方能否联通的基础。
沿完整工作流推进
前一阶段的可检查结果,是下一阶段的输入;中间证据不足时停在当前阶段。
先遇到 401、再遇到 422,问题其实出在两层
教学场景:阅读一个订单创建契约,先在本机模拟接口上复现201、401、422与幂等重试,再把相同检查方法用于已获授权的供应商沙盒。
完整本机模拟仅服务教学;下面预期结果不标为供应商实测。
- 请求方法与地址全篇一致
- 状态码、响应体与请求标识一起保存
- 区分本地模拟与真实供应商验证
先确认这次任务的起点、边界和交付
| 需要确认 | 本例内容 |
|---|---|
| 现有材料 | Node.js 20+、PowerShell 7+;本机接口固定为 POST http://127.0.0.1:3210/v1/orders,Bearer lesson 为公开演示标记;body含sku与正整数quantity。 |
| 不能越过的边界 | 模拟接口只监听127.0.0.1,不发送真实订单、不用真实凭证。模拟响应说明契约判断方法,不证明任何供应商API兼容或可用。 |
| 要交付的结果 | API 契约核对表与最小请求 |
从“认证与环境”走到“测试分页、超时和重试”
| 当前阶段 | 实际处理 |
|---|---|
| 认证与环境 | 区分测试和生产端点、密钥类型、scope、请求头和轮换方式,不把密钥写进源码。 |
| 最小成功请求 | 只带必填字段运行一次,保存脱敏请求、响应、状态码、请求 ID 和时间。 |
| 建立请求响应模型 | 逐字段记录类型、必填、枚举、默认、空值和版本兼容,未知字段保持向前兼容。 |
| 按状态码处理错误 | 区分用户输入、认证、权限、资源、限流和服务端错误,保留上游原始信息供诊断。 |
| 测试分页、超时和重试 | 验证终止条件、幂等性、退避和取消,不对所有失败无限重试。 |
只处理创建订单,不切换成订单查询
| 项 | 约定 | 检查 |
|---|---|---|
| 认证 | Authorization: Bearer lesson | 缺失或不符返回401 |
| body | sku为非空字符串;quantity为正整数 | quantity=0返回422 |
| 幂等 | Idempotency-Key必填;相同键绑定相同body | 同键同体返回原订单;同键异体409 |
| 输出 | id、sku、quantity | 首次201;重复200;x-request-id用于串联 |
| 分页 | 本端点无分页 | 不要把列表游标要求套在创建端点 |
保存为 order-demo.mjs 并在一个终端运行
Node.js 20+
node order-demo.mjs
import http from 'node:http';
import { randomUUID } from 'node:crypto';
const records = new Map();
const server = http.createServer(async (req, res) => {
const send = (status, body) => {
res.writeHead(status, {'content-type':'application/json', 'x-request-id':randomUUID()});
res.end(JSON.stringify(body));
};
if (req.method !== 'POST' || req.url !== '/v1/orders') return send(404, {error:'route_not_found'});
if (req.headers.authorization !== 'Bearer lesson') return send(401, {error:'unauthorized'});
const key = req.headers['idempotency-key'];
if (!key) return send(400, {error:'missing_idempotency_key'});
let body;
try { let text = ''; for await (const part of req) text += part; body = JSON.parse(text); }
catch { return send(400, {error:'invalid_json'}); }
if (!body || typeof body.sku !== 'string' || !body.sku.trim() || !Number.isInteger(body.quantity) || body.quantity < 1)
return send(422, {error:'invalid_order'});
const fingerprint = JSON.stringify({sku:body.sku, quantity:body.quantity});
const previous = records.get(key);
if (previous) return previous.fingerprint === fingerprint
? send(200, previous.order) : send(409, {error:'idempotency_conflict'});
const order = {id:randomUUID(), sku:body.sku, quantity:body.quantity};
records.set(key, {fingerprint, order}); send(201, order);
});
server.listen(3210, '127.0.0.1', () => console.log('POST http://127.0.0.1:3210/v1/orders'));
Ctrl+C停止模拟服务。记录仅在内存中,重启会清空;它不实现生产鉴权、持久化或配额。
在另一个 PowerShell 7 终端保留原始响应
PowerShell 7+
POST创建和重复请求
$headers = @{ Authorization = 'Bearer lesson'; 'Idempotency-Key' = 'lesson-order-1' }
$body = @{ sku = 'A-17'; quantity = 2 } | ConvertTo-Json -Compress
$response = Invoke-WebRequest -Uri 'http://127.0.0.1:3210/v1/orders' -Method Post -Headers $headers -ContentType 'application/json' -Body $body -SkipHttpErrorCheck
$response.StatusCode
$response.Headers['x-request-id']
$response.Content
# 原样再次执行 Invoke-WebRequest:预期200,订单id不变。
# 使用同一键,把quantity改为3:预期409。
# 删除Authorization:预期401;新键且quantity=0:预期422。
-SkipHttpErrorCheck是PowerShell 7参数;Windows PowerShell 5.1请换用pwsh运行本练习。真实接口应采用服务明确的认证、路径和幂等语义,不能直接替换域名猜测。
完成后的API 契约核对表与最小请求
预期首次合法请求201;同一Idempotency-Key和相同body重发为200且id不变;同键不同body为409;缺认证401;quantity为0时422。需实际保存本机响应后才将对应项标为通过。
为什么“最小请求成功,前端接入仍失败”还不能交付
最小请求成功,前端接入仍失败
- 原因
- 浏览器请求在 CORS、代理或序列化层改变
- 怎么改
- 对比实际网络请求与成功 curl,逐字段检查头、正文和环境
