非常赞的四篇文章:

本篇阅读目录:

  • 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 设计的一些总结的更多相关文章

  1. RESTful API URI 设计: 查询(Query)和标识(Identify)

    相关文章:RESTful API URI 设计的一些总结. 问题场景:删除一个资源(Resources),URI 该如何设计? 应用示例:删除名称为 iPhone 6 的产品. 是不是感觉很简单呢?根 ...

  2. RESTful API URI 设计: 判断资源是否存在?

    相关的一篇文章:RESTful API URI 设计的一些总结. 问题场景:判断一个资源(Resources)是否存在,URI 该如何设计? 应用示例:判断 id 为 1 用户下,名称为 window ...

  3. 好RESTful API的设计原则

    说在前面,这篇文章是无意中发现的,因为感觉写的很好,所以翻译了一下.由于英文水平有限,难免有出错的地方,请看官理解一下.翻译和校正文章花了我大约2周的业余时间,如有人愿意转载请注明出处,谢谢^_^ P ...

  4. RESTful API的设计原则

    好RESTful API的设计原则   说在前面,这篇文章是无意中发现的,因为感觉写的很好,所以翻译了一下.由于英文水平有限,难免有出错的地方,请看官理解一下.翻译和校正文章花了我大约2周的业余时间, ...

  5. 好的RESTful API的设计原则

    转载自一位大佬 英文原版 Principles of good RESTful API Design Good API design is hard! An API represents a cont ...

  6. [Medium翻译]RESTful API权威设计指南-设计更好的API

    本文为授权译文.希望查看原文的同学请戳链接:https://hackernoon.com/restful-api-design-step-by-step-guide-2f2c9f9fcdbf 对于我们 ...

  7. Restful API的设计与实践

    Restful这个名称应该很多人都不陌生,但是我发现不少人对Restful存在或多或少的理解偏差,其中不泛比较厉害的程序员,所以有必要为Restful来“正名”. Restful是一种软件架构风格,设 ...

  8. [转]10个有关RESTful API良好设计的最佳实践

    Web API已经在最近几年变成重要的话题,一个干净的API设计对于后端系统是非常重要的. 通常我们为Web API使用RESTful设计,REST概念分离了API结构和逻辑资源,通过Http方法GE ...

  9. 10个有关RESTful API良好设计的最佳实践

    Web API已经在最近几年变成重要的话题,一个干净的API设计对于后端系统是非常重要的. 通常我们为Web API使用RESTful设计,REST概念分离了API结构和逻辑资源,通过Http方法GE ...

随机推荐

  1. HttpsURLConnection 利用keepAlive特性进行优化一例

    最近项目中,遇到一个报错: java.lang.OutOfMemoryError: unable to create new native thread 报错的场景是:一个消息的群发,群里总共有50多 ...

  2. 常用MySQL图形化管理工具

    MySQL的管理维护工具非常多,除了系统自带的命令行管理工具之外,还有许多其他的图形化管理工具,这里我介绍几个经常使用的MySQL图形化管理工具,供大家参考. MySQL是一个非常流行的小型关系型数据 ...

  3. ERROR 2003 (HY000): Can't connect to MySQL server on 'ip address' (111)的处理办法

    远程连接mysql数据库时可以使用以下指令 mysql -h 192.168.1.104 -u root -p 如果是初次安装mysql,需要将所有/etc/mysql/内的所有配置文件的bind-a ...

  4. 如何利用报表工具FineReport实现报表列的动态展示

    相信动态列的实现困扰了很多人,大数据量,多字段的加载将会非常耗时,数据又做不到真正的动态灵活.现有的方式都是通过变向的隐藏等方式来实现. 那该如何解决呢?这里分享帆软报表设计器FineReport的实 ...

  5. Java实现office文档与pdf文档的在线预览功能

    最近项目有个需求要java实现office文档与pdf文档的在线预览功能,刚刚接到的时候就觉得有点难,以自己的水平难以在三四天做完.压力略大.后面查找百度资料.以及在同事与网友的帮助下,四天多把它做完 ...

  6. appscan 对api的手工检测

    AppScan 在 API 安全测试中的实例介绍 在本项目中,API 遵循标准的的 REST 架构和背端服务器进行通信.针对 API 的功能测试由两部分组成:一部分是用一个 Web 的测试页面直接实现 ...

  7. hdu----(1849)Rabbit and Grass(简单的尼姆博弈)

    Rabbit and Grass Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  8. ie6支持最小高度

    min-height:100px; height:auto !important; height:100px;

  9. [LeetCode] Valid Parentheses 验证括号

    Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the inpu ...

  10. Android开发之扫描二维码开发

    原贴地址:http://www.cnblogs.com/Fndroid/p/5540688.html 二维码其实有很多种,但是我们常见的微信使用的是一种叫做QRCode的二维码,像下面这样的,可以放心 ...