Web API Standards

Tổng quan

API (Application Programing Interface) là một giao tiếp phần mềm được dùng bởi các ứng dụng khác nhau. RESTful API là một chuẩn API phổ biến hiện nay. Vậy làm sao có thể viết được một API theo chuẩn RESTful? Sau đây là một số gợi ý. Để bài viết dễ hiểu mình sẽ dùng một số thuật ngữ bằng tiếng Anh.

RESTful URLs

Hướng dẫn chung cho RESTful URLs

  • Một URL định danh một tài nguyên hệ thống (resource)
  • URL nên bao gồm danh từ, không dùng động từ.
  • Sử dụng danh từ số nhiều cho thống nhất, không dùng danh từ số ít.
  • Sử dụng HTTP verbs (GET, POST, PUT, DELETE) để thực thi trên các collections và elements.
  • Không cần tạo url sâu hơn resource/identifier/resource.
  • Để phiên bản (version number) tại base URL. Ví dụ: http://example.com/v1/path/to/resource.
  • URL v. header:
    • Nếu nó thay đổi logic bạn viết để tổ chức trả về (response), đặt nó trong URL.
    • Nếu nó không thay đổi logic cho mỗi response như thông tin Oauth, đặt nó trên header.
  • Chỉ định các trường tùy chọn (optional fields) trong danh sách ngăn cách bởi dấu phẩy. Ví dụ: http://www.example.gov/api/v1/magazines.json ?fields=username,age
  • Formats nên có dạng api/v2/resource/{id}.json

Các ví dụ về good URL

Ví dụ về bad URL

HTTP Verbs

HTTP verbs hoặc methods nên sử dụng dưới chuẩn HTTP/1.1. Dưới đây là ví dụ map HTTP verbs với các hành động tạo, đọc, chỉnh sửa, xóa:

HTTP METHOD POST GET PUT DELETE
CRUD OP CREATE READ UPDATE DELETE
/dogs Create new dogs List dogs Bulk update Delete all dogs
/dogs/1234 Error Show Bo If exists, update Bo; If not, error Delete Bo

(Example from Web API Design, by Brian Mulloy, Apigee.)

Responses

  • Không để giá trị (values) trong khóa (keys)
  • Thông tin mở rộng (metadata) nên chỉ chứa những thuộc tính của các gói dữ liệu trả về, chứ không phải thuộc tính của những thành phần có trong gói dữ liệu đó. Ví dụ không trả về thông tin của type của magazine trong tập results.
  {
        "metadata": {
            "resultset": {
                "count": 123,
                "offset": 0,
                "limit": 10
            }
        },
        "results": [
            {
                "id": "1234",
                "type": "magazine",
                "title": "Public Water Systems",
                "tags": [
                    {"id": "125", "name": "Environment"},
                    {"id": "834", "name": "Water Quality"}
                ],
                "created": "1231621302"
            },
            {
                "id": 2351,
                "type": "magazine",
                "title": "Public Schools",
                "tags": [
                    {"id": "125", "name": "Elementary"},
                    {"id": "834", "name": "Charter Schools"}
                ],
                "created": "126251302"
            }
        ]
    }

Good

Không để giá trị nằm trong khóa:

    "tags": [
      {"id": "125", "name": "Environment"},
      {"id": "834", "name": "Water Quality"}
    ],

Bad

Giá trị nằm trong khóa:

    "tags": [
      {"125": "Environment"},
      {"834": "Water Quality"}
    ],

Tổ chức lỗi (Error handling)

Các phản hồi lỗi nên bao gồm một HTTP status code chung, tin nhắn cho developer, tin nhắn cho người dùng cuối (khi phù hợp) và mã lỗi trong (internal error code) , đường dẫn tới nơi developer có thể tìm thêm thông tin. Ví dụ:

    {
      "status" : 400,
      "developerMessage" : "Verbose, plain language description of the problem. Provide developers
       suggestions about how to solve their problems here",
      "userMessage" : "This is a message that can be passed along to end-users, if needed.",
      "errorCode" : "444444",
      "moreInfo" : "http://www.example.gov/developer/path/to/help/for/444444,
       http://drupal.org/node/444444",
    }

Sử dụng 3 mã lỗi phản hồi đơn giản sau: (1) thành công, (2) thất bại vì có vấn đề với client-side, (3) thất bại vì có vấn đề với server-side :

  • 200 - OK
  • 400 - Bad Request
  • 500 - Internal Server Error

Phiên bản (Versions)

  • Không bao giờ release một api mà không có một số phiên bản (version number).
  • Phiên bản nên là số tự nhiên, không dùng số thập phân, với prefix là v. Ví dụ:
    • Good: v1, v2, v3
    • Bad: v-1.1, v1.2, 1.3
  • Bảo trì APIs ít nhất một phiên bản trở lại (phiên bản trước đó).

Giới hạn số lượng bản ghi (limit) trả về

  • Nếu không chỉ định giới hạn thì kết quả trả về với một giá trị giới hạn mặc định (limit = 20, 50)
  • Để lấy bản ghi từ 51 – 75 thì làm như sau:

Thông tin về giới hạn bản ghi (limit) và tổng số bản ghi (total) cũng nên trả về trong response. Ví dụ:

    {
        "metadata": {
            "resultset": {
                "count": 227,
                "offset": 25,
                "limit": 25
            }
        },
        "results": []
    }

Ví dụ về Request và Response

API Resources

GET /magazines

Ví dụ: http://example.gov/api/v1/magazines.json

Response body:

    {
        "metadata": {
            "resultset": {
                "count": 123,
                "offset": 0,
                "limit": 10
            }
        },
        "results": [
            {
                "id": "1234",
                "type": "magazine",
                "title": "Public Water Systems",
                "tags": [
                    {"id": "125", "name": "Environment"},
                    {"id": "834", "name": "Water Quality"}
                ],
                "created": "1231621302"
            },
            {
                "id": 2351,
                "type": "magazine",
                "title": "Public Schools",
                "tags": [
                    {"id": "125", "name": "Elementary"},
                    {"id": "834", "name": "Charter Schools"}
                ],
                "created": "126251302"
            }
            {
                "id": 2351,
                "type": "magazine",
                "title": "Public Schools",
                "tags": [
                    {"id": "125", "name": "Pre-school"},
                ],
                "created": "126251302"
            }
        ]
    }

GET /magazines/[id]

Ví dụ: http://example.gov/api/v1/magazines/[id].json

Response body:

    {
        "id": "1234",
        "type": "magazine",
        "title": "Public Water Systems",
        "tags": [
            {"id": "125", "name": "Environment"},
            {"id": "834", "name": "Water Quality"}
        ],
        "created": "1231621302"
    }

POST /magazines/[id]/articles

Ví dụ: Tạo mới – POST http://example.gov/api/v1/magazines/[id]/articles

Request body:

    [
        {
            "title": "Raising Revenue",
            "author_first_name": "Jane",
            "author_last_name": "Smith",
            "author_email": "[email protected]",
            "year": "2012",
            "month": "August",
            "day": "18",
            "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eget ante ut augue scelerisque ornare. Aliquam tempus rhoncus quam vel luctus. Sed scelerisque fermentum fringilla. Suspendisse tincidunt nisl a metus feugiat vitae vestibulum enim vulputate. Quisque vehicula dictum elit, vitae cursus libero auctor sed. Vestibulum fermentum elementum nunc. Proin aliquam erat in turpis vehicula sit amet tristique lorem blandit. Nam augue est, bibendum et ultrices non, interdum in est. Quisque gravida orci lobortis... "
        }
    ]

Giả lập trả về (Mock Responses)

Nên gợi ý mỗi resource chấp nhận một tham số 'giả lập' trên server test. Khi có tham số này thì nên trả về một dữ liệu giả lập ( dữ liệu mẫu).

Thực thi tính năng này sớm ở môi trường phát triển (development) đảm bảo rằng API sẽ được mô phỏng response như trên production và hỗ trợ cho việc test.

Chú ý: nếu tham số giả lập được truyền trong request trên môi trường production thì phải trả về lỗi.

Tham khảo