RESTful API URI 設計的一些總結

jopen 9年前發布 | 86K 次閱讀 RESTful WEB服務/RPC/SOA

RESTful API URI 設計的一些總結

非常贊的三篇文章:

  • Resource Naming
  • Best Practices for Designing a Pragmatic RESTful API
  • 撰寫合格的 REST API
  • </ul>

    本篇閱讀目錄:

    • 1. HTTP Methods
    • 2. JSON API 命名
    • 3. API URI design
    • </ul>

      1. HTTP Methods

      HTTP 常用方法:

      • GET: 獲取某個資源。
      • POST: 創建一個新的資源。
      • PUT: 替換某個已有的資源。
      • PATCH: 修改某個已有的資源。
      • DELETE:刪除某個資源。
      • </ul>

        我原先以為修改某一個資源,也是用 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 的用戶
        • </ul>

          上面是對某一種資源進行操作的 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 的產品
          • </ul>

            還有一種情況,我們一般在設計 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
 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!