RESTful API URI 设计的一些总结

非常赞的四篇文章:
本篇阅读目录:
- 1. HTTP Methods
- 2. JSON API 命名规则
- 3. API URI design
- 4. WebAPI 使用 snake_case 还是 camelCasing?
1. HTTP Methods
HTTP 常用方法:
- GET: 获取某个资源。
- POST: 创建一个新的资源。
- PUT: 替换某个已有的资源。
- PATCH: 修改某个已有的资源。
- DELETE:删除某个资源。
我原先以为修改某一个资源,也是用 POST,后来发现还有一个 PATCH,但发现 HttpClient 并没有提供此调用方法,需要我们进行扩展:
public static class HttpClientExtensions
{
public static async Task<HttpResponseMessage> PatchAsync(this HttpClient client, Uri requestUri, HttpContent iContent)
{
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, requestUri)
{
Content = iContent
};
HttpResponseMessage response = new HttpResponseMessage();
try
{
response = await client.SendAsync(request);
}
catch (TaskCanceledException e)
{
Debug.WriteLine("ERROR: " + e.ToString());
}
return response;
}
}
调用代码:
HttpContent httpContent = new StringContent("Your JSON-String", Encoding.UTF8, "application/json");
var responseMessage = await httpClient.PatchAsync(new Uri("testUri"), httpContent);
2. JSON API 命名规则
相关阅读:You should use camelCase with JSON, but snake_case is 20% easier to read
camelCase(骆驼命名)我们都非常熟悉,因为 C# 就是使用的这个命名法,snake_case(蛇形命名)适用于 python 和 ruby,比如商品 ID,camelCase 会命名为 productId,snake_case 则会命名为 product_id。
需要注意的是,snake_case 只限于 JSON API 命名,并不限于 URI,URI 中一般也不会使用下划线,为什么要对 JSON API 进行规范命名?因为 RESTful 是无状态风格,也就是说 RESTful API 并不限于某一种客户端进行调用,所以 JSON API 的命名必须要规范,如果只是 C# 调用的话,那么命名采用 camelCase 命名就可以了,但显然并不是这样,最后得出的结论是使用 snake_case 命名会比较好,以后在设计的时候,需要注意了。
3. API URI design
API URI 设计最重要的一个原则:nouns (not verbs!),名词(而不是动词)。
CRUD 简单 URI:
GET /users- 获取用户列表GET /users/1- 获取 Id 为 1 的用户POST /users- 创建一个用户PUT /users/1- 替换 Id 为 1 的用户PATCH /users/1- 修改 Id 为 1 的用户DELETE /users/1- 删除 Id 为 1 的用户
上面是对某一种资源进行操作的 URI,那如果是有关联的资源,或者称为级联的资源,该如何设计 URI 呢?比如某一用户下的产品:
GET /users/1/products- 获取 Id 为 1 用户下的产品列表GET /users/1/products/2- 获取 Id 为 1 用户下 Id 为 2 的产品POST /users/1/products- 在 Id 为 1 用户下,创建一个产品PUT /users/1/products/2- 在 Id 为 1 用户下,替换 Id 为 2 的产品PATCH /users/1/products/2- 修改 Id 为 1 的用户下 Id 为 2 的产品DELETE /users/1/products/2- 删除 Id 为 1 的用户下 Id 为 2 的产品
还有一种情况,我们一般在设计 API 的时候,会进行一些查询操作,比如分页和排序等,API 方法参数设计可能很容易,那重要的 URI 该如何设计呢?我们先看这样的一个设计:
[HttpGet]
[Route("api/wzlinks/users-{spaceUserId}/{pageIndex=1}/{pageSize=20}")]
public async Task<IEnumerable<WzLinkDTO>> GetPagedList(int spaceUserId, int pageIndex, int pageSize)
{
.....
}
首先,这个 URI 想要表示的意思是:获取某一用户下,分页查询的网摘列表,这个 API 设计好不好呢?我们看下 GitHub 中的一个 API:
"current_user_repositories_url": "https://api.github.com/user/repos{?type,page,per_page,sort}"
差别是不是很大?而且我们设计的 URI 表达也比较混乱,查询应该是参数,并且是对 URI 进行的查询,所以放在 URI 中会不太合适,我们完善下:
[HttpGet]
[Route("api/users/{space_user_id}/wzlinks")]
public async Task<IEnumerable<WzLinkDTO>> GetPagedList(int space_user_id, int page, int per_page)
{
.....
}
URI 表达为:获取 space_user_id 为 1 用户下的网摘分页列表,上面设计会不会更好些呢?调用示例:
api.cnblogs.com/api/users/1/wzlinks?page=1&per_page=20
GitHub API(规范参考):https://api.github.com
{
"current_user_url": "https://api.github.com/user",
"current_user_authorizations_html_url": "https://github.com/settings/connections/applications{/client_id}",
"authorizations_url": "https://api.github.com/authorizations",
"code_search_url": "https://api.github.com/search/code?q={query}{&page,per_page,sort,order}",
"emails_url": "https://api.github.com/user/emails",
"emojis_url": "https://api.github.com/emojis",
"events_url": "https://api.github.com/events",
"feeds_url": "https://api.github.com/feeds",
"following_url": "https://api.github.com/user/following{/target}",
"gists_url": "https://api.github.com/gists{/gist_id}",
"hub_url": "https://api.github.com/hub",
"issue_search_url": "https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}",
"issues_url": "https://api.github.com/issues",
"keys_url": "https://api.github.com/user/keys",
"notifications_url": "https://api.github.com/notifications",
"organization_repositories_url": "https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}",
"organization_url": "https://api.github.com/orgs/{org}",
"public_gists_url": "https://api.github.com/gists/public",
"rate_limit_url": "https://api.github.com/rate_limit",
"repository_url": "https://api.github.com/repos/{owner}/{repo}",
"repository_search_url": "https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}",
"current_user_repositories_url": "https://api.github.com/user/repos{?type,page,per_page,sort}",
"starred_url": "https://api.github.com/user/starred{/owner}{/repo}",
"starred_gists_url": "https://api.github.com/gists/starred",
"team_url": "https://api.github.com/teams",
"user_url": "https://api.github.com/users/{user}",
"user_organizations_url": "https://api.github.com/user/orgs",
"user_repositories_url": "https://api.github.com/users/{user}/repos{?type,page,per_page,sort}",
"user_search_url": "https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"
}
4. WebAPI 使用 snake_case 还是 camelCasing?
补充:因为 space_user_id 违反 C# 的命名规则,Google 搜索“asp net web api snake_case”,却搜多到大量的“camelCasing”关键字,而且在微软大部分 WebAPI 示例中,Route 的参数命名设计规范都是 camelCasing,所以。。。。没办法,只能使用 camelCasing 命名规则吧,谁让用的是 .NET 呢,不过,有人还搞了个 SnakeCaseFormUrlEncodedMediaTypeFormatter 扩展,但好像是在过程中进行了转化,并不是解决定义问题。
网摘的 API 我们再修改下:
[HttpGet]
[Route("api/users/{spaceUserId}/wzlinks")]
public async Task<IEnumerable<WzLinkDTO>> GetPagedList(int spaceUserId, int page, int perPage)
{
.....
}
不经意间,还发现 ASP.NET WebAPI Help 一个有意思的地方,比如上面的 API 设计,得到的是这样的 Help 说明:

如果我们把 API 代码修改成:
[HttpGet]
[Route("api/users/{space_user_id}/wzlinks")]
public async Task<IEnumerable<WzLinkDTO>> GetPagedList(int spaceUserId, int page, int perPage)
{
.....
}
得到的却是这样的 Help 说明:

发现有什么不同了吗?看来 ASP.NET WebAPI Help 还是蛮智能的呢。
RESTful API URI 设计的一些总结的更多相关文章
- RESTful API URI 设计: 查询(Query)和标识(Identify)
相关文章:RESTful API URI 设计的一些总结. 问题场景:删除一个资源(Resources),URI 该如何设计? 应用示例:删除名称为 iPhone 6 的产品. 是不是感觉很简单呢?根 ...
- RESTful API URI 设计: 判断资源是否存在?
相关的一篇文章:RESTful API URI 设计的一些总结. 问题场景:判断一个资源(Resources)是否存在,URI 该如何设计? 应用示例:判断 id 为 1 用户下,名称为 window ...
- 好RESTful API的设计原则
说在前面,这篇文章是无意中发现的,因为感觉写的很好,所以翻译了一下.由于英文水平有限,难免有出错的地方,请看官理解一下.翻译和校正文章花了我大约2周的业余时间,如有人愿意转载请注明出处,谢谢^_^ P ...
- RESTful API的设计原则
好RESTful API的设计原则 说在前面,这篇文章是无意中发现的,因为感觉写的很好,所以翻译了一下.由于英文水平有限,难免有出错的地方,请看官理解一下.翻译和校正文章花了我大约2周的业余时间, ...
- 好的RESTful API的设计原则
转载自一位大佬 英文原版 Principles of good RESTful API Design Good API design is hard! An API represents a cont ...
- [Medium翻译]RESTful API权威设计指南-设计更好的API
本文为授权译文.希望查看原文的同学请戳链接:https://hackernoon.com/restful-api-design-step-by-step-guide-2f2c9f9fcdbf 对于我们 ...
- Restful API的设计与实践
Restful这个名称应该很多人都不陌生,但是我发现不少人对Restful存在或多或少的理解偏差,其中不泛比较厉害的程序员,所以有必要为Restful来“正名”. Restful是一种软件架构风格,设 ...
- [转]10个有关RESTful API良好设计的最佳实践
Web API已经在最近几年变成重要的话题,一个干净的API设计对于后端系统是非常重要的. 通常我们为Web API使用RESTful设计,REST概念分离了API结构和逻辑资源,通过Http方法GE ...
- 10个有关RESTful API良好设计的最佳实践
Web API已经在最近几年变成重要的话题,一个干净的API设计对于后端系统是非常重要的. 通常我们为Web API使用RESTful设计,REST概念分离了API结构和逻辑资源,通过Http方法GE ...
随机推荐
- Java并发之原子变量和原子引用与volatile
我们知道在并发编程中,多个线程共享某个变量或者对象时,必须要进行同步.同步的包含两层作用:1)互斥访问(原子性):2)可见性:也就是多个线程对共享的变量互斥地访问,同时线程对共享变量的修改必须对其他线 ...
- linux 安装mysql数据库——tar.gz包解压安装法
mysql数据库有多种安装方式,本文只介绍在Linux服务器上的tar.gz包解压安装法, 先通过mysql官网或者网络资源下载 mysql-5.7.3-m13-linux-glibc2.5-x86_ ...
- Python Socket 网络编程
Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ ...
- spriing boot 实战
一.spring基础 1. 依赖注入 我们常说的控制翻转(Inversion of Control -IOC)和依赖注入(dependency injection-DI)在Spring环境下是等同的概 ...
- Fiddler抓包工具使用基础
官网下载Fiddler Fiddler的官方网站: www.fiddler2.com Fiddler是最强大最好用的Web调试工具之一,它能记录所有客户端和服务器的http和https请求,允许你监 ...
- java script 基础知识
方法:提供信息 函数去处理 给出一个结果 字符类型. 字符提前赋类型 int i=1; money x; float;datetime; decimal;var 万用类型 var a = 1; 1赋值 ...
- 关于delphi7的四舍五入
round 函数是银行用的 采用了 四舍六入5留偶 网上找到了个实现方法 先乘1000,用Trunc取整,除10取余,余数再取整,如果大于5,进位,小于5不进位. 函数就好写了 现在只写一个保留两 ...
- Java中判断字符串是否为数字的五种方法
//方法一:用JAVA自带的函数 public static boolean isNumeric(String str){ for (int i = str.length();--i>=0;){ ...
- BZOJ 2002: [Hnoi2010]Bounce 弹飞绵羊
2002: [Hnoi2010]Bounce 弹飞绵羊 Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 9071 Solved: 4652[Submi ...
- Leetcode 75. Sort Colors
Given an array with n objects colored red, white or blue, sort them so that objects of the same colo ...