RESTful API URI 设计的一些总结

jopen 9年前

RESTful API URI 设计的一些总结

非常赞的三篇文章:

本篇阅读目录:

  • 1. HTTP Methods
  • 2. JSON API 命名
  • 3. API URI design

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 /users1/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/{page=1}/{per_page=20}")]  public async Task<IEnumerable<WzLinkDTO>> GetPagedList(int spaceUserId, 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}"  }

原文  http://www.cnblogs.com/xishuai/p/restful-webapi-uri-design.html