소스 정보
- 저장소
- huanglei288766/claude-code-zh
- 최근 소스 활동
- 2026년 3월 13일 19:22
- 감지된 SKILL.md 언어
- 중국어
- 스타
- 6
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/huanglei288766/claude-code-zh --skill api-design-cn명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | api-design-cn |
| description | RESTful API 设计规范(中文版),涵盖资源命名、统一响应、分页、错误码、版本控制、认证、OpenAPI 文档 |
| version | 1.0 |
本 Skill 定义 RESTful API 的设计规范,适用于 Java/Python/Go/Node.js 等后端项目,确保 API 风格统一、文档完善、易于对接。
适用场景:
# 资源用名词复数,不用动词
GET /api/v1/users # 获取用户列表
POST /api/v1/users # 创建用户
GET /api/v1/users/123 # 获取单个用户
PUT /api/v1/users/123 # 全量更新用户
PATCH /api/v1/users/123 # 部分更新用户
DELETE /api/v1/users/123 # 删除用户
# 子资源用嵌套路径表示归属关系
GET /api/v1/users/123/orders # 获取用户的订单列表
POST /api/v1/users/123/orders # 为用户创建订单
GET /api/v1/users/123/orders/456 # 获取用户的某个订单
# 嵌套不超过两层,超过时提升为顶级资源
GET /api/v1/orders/456/items # 可以
GET /api/v1/users/123/orders/456/items/789 # 太深,应拆分
GET /api/v1/order-items/789 # 提升为顶级资源
# URL 路径:全小写,连字符分隔(kebab-case)
/api/v1/order-items # 正确
/api/v1/orderItems # 错误 — 不用 camelCase
/api/v1/order_items # 错误 — 不用 snake_case
# 查询参数:snake_case
/api/v1/users?page_size=20&sort_by=created_at
# 请求/响应体字段:camelCase(前端友好)
{
"userId": 123,
"userName": "张三",
"createdAt": "2026-03-14T10:00:00Z"
}
方法 语义 成功状态码 幂等性
------- -------------- ----------- ------
GET 查询资源 200 是
POST 创建资源 201 否
PUT 全量替换 200 是
PATCH 部分更新 200 否
DELETE 删除资源 204 是
常用错误状态码:
400 Bad Request — 参数校验失败
401 Unauthorized — 未认证(未登录)
403 Forbidden — 已认证但无权限
404 Not Found — 资源不存在
409 Conflict — 资源冲突(如重复创建)
422 Unprocessable — 业务规则校验失败
429 Too Many Requests — 触发限流
500 Internal Server — 服务器内部错误
// 成功响应
{
"code": 0,
"message": "success",
"data": {
"id": 123,
"username": "zhangsan",
"email": "zhang@example.com"
}
}
// 失败响应
{
"code": 10001,
"message": "用户名已存在",
"data": null
}
// 校验失败响应(携带字段级错误明细)
{
"code": 10000,
"message": "参数校验失败",
"data": {
"errors": [
{"field"
// Java — 统一响应体
public class ApiResponse<T> {
private int code; // 业务状态码,0 表示成功
private String message; // 提示信息
private T data; // 响应数据
// 成功响应
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(0, "success", data);
}
public static ApiResponse<Void> success() {
return new ApiResponse<>(0, "success", null);
}
// 失败响应
public static <T> ApiResponse<T> fail(int code, String message) {
return new ApiResponse<>(code, message, null);
}
// 带错误码枚举的失败响应
public static <T> ApiResponse<T> fail(ErrorCode errorCode) {
return new ApiResponse<>(errorCode.getCode(), errorCode.getMessage(), null);
}
}
# Python (FastAPI) — 统一响应体
from pydantic import BaseModel, Generic
from typing import TypeVar
T = TypeVar("T")
class ApiResponse(BaseModel, Generic[T]):
"""统一 API 响应格式"""
code: int = 0
message: str = "success"
data: T | None = None
@classmethod
def success(cls, data: T = None) -> "ApiResponse[T]":
return cls(code=0, message="success", data=data)
@classmethod
def fail(cls, code: int, message: str) -> "ApiResponse":
return cls(code=code, message=message, data=None)
// Go (gin) — 统一响应体
type ApiResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
func Success(c *gin.Context, data interface{}) {
c.JSON(http.StatusOK, ApiResponse{
Code: 0, Message: "success", Data: data,
})
}
func Fail(c *gin.Context, httpStatus int, code int, message string) {
c.JSON(httpStatus, ApiResponse{
Code: code, Message: message, Data: nil,
})
}
# 标准分页参数
GET /api/v1/users?page=1&size=20&sort_by=created_at&sort_order=desc
参数说明:
- page — 页码,从 1 开始(非 0)
- size — 每页条数,默认 20,最大 100
- sort_by — 排序字段(可选)
- sort_order — 排序方向 asc/desc(可选,默认 desc)
{
"code": 0,
"message": "success",
"data": {
"items": [
{"id": 1, "username": "zhangsan"},
{"id": 2, "username": "lisi"}
],
"pagination": {
"page": 1,
"size": 20,
"total": 156,
"totalPages": 8
}
}
}
// Java — 分页请求与响应
public record PageQuery(
@Min(1) int page,
@Min(1) @Max(100) int size,
String sortBy,
String sortOrder
) {
public PageQuery {
// 默认值处理
if (page < 1) page = 1;
if (size < 1) size = 20;
if (size > 100) size = 100;
if (sortOrder == null) sortOrder = "desc";
}
public int getOffset() {
return (page - 1) * size;
}
}
public record PageResult<T>(
List<T> items,
Pagination pagination
) {
public record Pagination(int page, int size, long total, int totalPages) {}
public static <T> PageResult<T> of(List<T> items, long total, PageQuery query) {
int totalPages = (int) Math.ceil((double) total / query.size());
return <>(items,
(query.page(), query.size(), total, totalPages));
}
}
// 请求
// GET /api/v1/orders?cursor=eyJpZCI6MTAwfQ&size=20
// 响应
{
"code": 0,
"message": "success",
"data": {
"items": [...],
"cursor": {
"next": "eyJpZCI6MTIwfQ",
"hasMore": true
}
}
}
// Java — 游标分页(Base64 编码游标)
public record CursorPageResult<T>(
List<T> items,
CursorInfo cursor
) {
public record CursorInfo(String next, boolean hasMore) {}
public static <T> CursorPageResult<T> of(List<T> items, int size, String nextCursor) {
boolean hasMore = items.size() >= size;
return new CursorPageResult<>(items, new CursorInfo(nextCursor, hasMore));
}
}
错误码格式: 5 位整数
- 0 — 成功
- 10xxx — 通用错误(参数校验、认证、权限)
- 2xxxx — 用户模块错误
- 3xxxx — 订单模块错误
- 4xxxx — 支付模块错误
- 9xxxx — 系统级错误
每个模块预留 1000 个错误码空间
// Java — 错误码枚举
public enum ErrorCode {
// 通用错误 10xxx
VALIDATION_ERROR(10000, "参数校验失败"),
UNAUTHORIZED(10001, "未认证,请先登录"),
FORBIDDEN(10002, "无权限访问"),
NOT_FOUND(10003, "资源不存在"),
RATE_LIMITED(10004, "请求过于频繁,请稍后重试"),
DUPLICATE(10005, "资源已存在"),
// 用户模块 2xxxx
USER_NOT_FOUND(20001, "用户不存在"),
USER_DISABLED(20002, "用户已禁用"),
USERNAME_DUPLICATE(20003, "用户名已存在"),
EMAIL_DUPLICATE(20004, "邮箱已被注册"),
PASSWORD_WRONG(20005, "密码错误"),
// 订单模块 3xxxx
ORDER_NOT_FOUND(30001, "订单不存在"),
ORDER_STATUS_INVALID(30002, "订单状态不允许此操作"),
ORDER_EXPIRED(30003, "订单已过期"),
STOCK_INSUFFICIENT(30004, "库存不足"),
// 系统错误 9xxxx
INTERNAL_ERROR(99999, "服务器内部错误");
private final int code;
private final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
{ code; }
String { message; }
}
# Python — 错误码定义
from enum import IntEnum
class ErrorCode(IntEnum):
"""业务错误码"""
# 通用错误
VALIDATION_ERROR = 10000 # 参数校验失败
UNAUTHORIZED = 10001 # 未认证
FORBIDDEN = 10002 # 无权限
NOT_FOUND = 10003 # 资源不存在
RATE_LIMITED = 10004 # 限流
DUPLICATE = 10005 # 资源重复
# 用户模块
USER_NOT_FOUND = 20001 # 用户不存在
USER_DISABLED = 20002 # 用户已禁用
USERNAME_DUPLICATE = 20003 # 用户名已存在
PASSWORD_WRONG = 20005 # 密码错误
# 订单模块
ORDER_NOT_FOUND = 30001 # 订单不存在
ORDER_STATUS_INVALID = 30002 # 订单状态不允许操作
# 错误码 -> 消息映射
ERROR_MESSAGES: dict[ErrorCode, str] = {
ErrorCode.VALIDATION_ERROR: "参数校验失败",
ErrorCode.UNAUTHORIZED: "未认证,请先登录",
ErrorCode.USER_NOT_FOUND: "用户不存在",
# ...
}
# 推荐方式 — URL 路径中包含版本号
GET /api/v1/users
GET /api/v2/users
# 优点:直观、易调试、CDN 友好
# 缺点:版本升级时需要新路由
// Java — 多版本控制器
@RestController
@RequestMapping("/api/v1/users")
public class UserControllerV1 {
@GetMapping("/{id}")
public ApiResponse<UserVO> getUser(@PathVariable Long id) {
// v1 返回基础字段
return ApiResponse.success(userService.getUserBasic(id));
}
}
@RestController
@RequestMapping("/api/v2/users")
public class UserControllerV2 {
@GetMapping("/{id}")
public ApiResponse<UserDetailVO> getUser(@PathVariable Long id) {
// v2 返回完整字段,含新增的 profile 信息
return ApiResponse.success(userService.getUserDetail(id));
}
}
# 通过 Accept 头指定版本
GET /api/users
Accept: application/vnd.myapp.v2+json
# 或自定义头
GET /api/users
X-API-Version: 2
1. 新项目从 v1 开始
2. 非破坏性变更(新增字段)不升版本
3. 破坏性变更(删除/改名字段、修改语义)升大版本
4. 最多同时维护 2 个版本(当前版本 + 上一版本)
5. 旧版本至少保留 6 个月后下线,提前通知调用方
认证流程:
1. 用户登录 → 返回 accessToken(短期)+ refreshToken(长期)
2. 请求携带 accessToken → Authorization: Bearer <token>
3. accessToken 过期 → 用 refreshToken 换取新 accessToken
4. refreshToken 过期 → 重新登录
POST /api/v1/auth/login # 登录
POST /api/v1/auth/refresh # 刷新令牌
POST /api/v1/auth/logout # 登出(废弃当前令牌)
// Java — 登录响应
public record LoginResponse(
String accessToken, // 访问令牌,有效期 30 分钟
String refreshToken, // 刷新令牌,有效期 7 天
long expiresIn, // accessToken 过期时间(秒)
String tokenType // 固定 "Bearer"
) {}
// 登录接口
@PostMapping("/api/v1/auth/login")
public ApiResponse<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
// 1. 验证用户名密码
User user = authService.authenticate(request.getUsername(), request.getPassword());
// 2. 生成令牌对
String accessToken = jwtService.generateAccessToken(user);
String refreshToken = jwtService.generateRefreshToken(user);
// 3. 将 refreshToken 存入 Redis(支持主动废弃)
redisService.setRefreshToken(user.getId(), refreshToken, Duration.ofDays(7));
return ApiResponse.success(new LoginResponse(
accessToken, refreshToken, 1800, "Bearer"
));
}
// 刷新令牌接口
@PostMapping("/api/v1/auth/refresh")
public ApiResponse<LoginResponse> refresh(@RequestBody RefreshRequest request) {
// 1. 验证 refreshToken
Claims claims = jwtService.parseRefreshToken(request.getRefreshToken());
// 2. 检查 Redis 中是否存在(已登出的会被删除)
String redisService.getRefreshToken(claims.getUserId());
(!request.getRefreshToken().equals(stored)) {
(ErrorCode.UNAUTHORIZED);
}
userService.getById(claims.getUserId());
jwtService.generateAccessToken(user);
jwtService.generateRefreshToken(user);
redisService.setRefreshToken(user.getId(), newRefreshToken, Duration.ofDays());
ApiResponse.success( (
newAccessToken, newRefreshToken, ,
));
}
1. accessToken 有效期短(15-30 分钟)
2. refreshToken 有效期长(7-30 天),存 Redis 支持主动废弃
3. 每次 refresh 时轮换 refreshToken(防重放攻击)
4. 登出时删除 Redis 中的 refreshToken
5. accessToken 只放必要信息(userId、role),不放敏感数据
6. 使用 HTTPS,防止令牌被截获
7. 前端 accessToken 存内存,refreshToken 存 httpOnly cookie
1. 每个接口必须包含:summary(一句话描述)、description(详细说明)
2. 所有参数必须有 description 和示例值
3. 所有响应码必须有对应的 schema 定义
4. 使用 tags 对接口分组
5. 请求/响应示例必须是真实可用的数据
# api/openapi.yaml
openapi: 3.0.3
info:
title: 用户服务 API
description: 用户注册、登录、信息管理等接口
version: 1.0.0
servers:
- url: https://api.example.com
description: 生产环境
- url: https://api-staging.example.com
description: 预发布环境
tags:
- name: 用户管理
description: 用户 CRUD 操作
- name: 认证
description: 登录、登出、刷新令牌
paths:
/api/v1/users:
post:
tags: [用户管理]
summary: 创建用户
description: 注册新用户,用户名和邮箱不可重复
operationId: createUser
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
[]
[, , ]
[]
| 检查项 | 说明 |
|---|---|
| 资源命名 | 名词复数、kebab-case、嵌套不超过两层 |
| HTTP 方法 | GET 查询、POST 创建、PUT 全量更新、PATCH 部分更新、DELETE 删除 |
| 状态码 | 成功 200/201/204,客户端错误 4xx,服务端错误 5xx |
| 响应格式 | 统一 code/message/data 结构 |
| 分页 | page 从 1 开始,size 有上限,返回 total 和 totalPages |
| 错误码 | 5 位整数,按模块分段,message 面向用户 |
| 版本 | URL 路径版本 /api/v1/,最多维护两个版本 |
| 认证 | JWT + refreshToken,accessToken 短期有效 |
| 文档 | OpenAPI 3.0,每个接口有 summary/description/example |
| 安全 | HTTPS、输入校验、限流、错误信息不泄露内部细节 |
| 幂等性 | POST 用业务唯一键去重,PUT/DELETE 天然幂等 |
| 时间格式 | ISO 8601(2026-03-14T10:00:00Z),统一 UTC |