原文 : Best Practices in a Nutshell. By Philipp Hauer


How should project resource urls be designed? Plural or singular? How many urls does a resource need? Which HTTP method is used to create a new resource? Where do optional parameters go? What about urls that don’t involve resource operations? What is the best way to implement paging and versioning? Designing RESTful apis can be tricky because there are so many questions. In this article, we take a look at RESTful API design and present a best practice.

Use two urls per resource

A URL is used for a resource set and a URL is used for a specific resource:



/employees         The URL of the resource collection
/employees/56      The URL of a specific resourceCopy the code

Use nouns instead of verbs to represent resources

This gives you a cleaner API with fewer urls. Don’t design like this:



/getAllEmployees
/getAllExternalEmployees
/createEmployee
/updateEmployeeCopy the code

Better design:



GET /employees
GET /employees?state=external
POST /employees
PUT /employees/56Copy the code

Manipulate resources using HTTP methods

Use the URL to specify the resource you want to use. Use HTTP methods to specify what to do with the resource. Use the four HTTP methods POST, GET, PUT, and DELETE to provide CRUD functionality (create, GET, update, and DELETE).

  • GET: Use the GET method to GET resources. GET requests never change the state of a resource. No side effects. The GET method is idempotent. The GET method is read-only. Therefore, you can use the cache perfectly.
  • Create: Use POST to create a new resource.
  • Update: Update an existing resource using PUT.
  • DELETE: Use DELETE to DELETE an existing resource.

Two urls multiplied by four HTTP methods is a nice set of features. Take a look at this chart:

POST (Create) GET (read) PUT (update) DELETE (DELETE)
/employees Create a new employee List all employees Update employee information in batches Delete all employees
/employees/56 (error) Get information on employee 56 Update employee number 56 Delete employee 56

Create a new resource using the POST method on the URL of the resource collection

How do the client and server interact when creating a new resource?

Create a new resource using POST on the resource collection URL:

  1. The client sends the resource collection URL/employeesSend a POST request. The HTTP Body contains the property “Albert Stark” for the new resource.
  2. A RESTful Web server generates ids for new employees, creates employees in its internal model, and sends responses to clients. The HTTP header of the response contains a Location field that indicates the creation of a URL accessible to the resource.

Use the PUT method on the URL of a specific resource to update the resource

Update an existing resource with PUT:

  1. The client sends a PUT request to the URL of a specific resource/employee/21. The HTTP body of the request contains the value of the property to be updated (the new name of employee number 21, “Bruce Wayne”).
  2. The REST server updates the employee name with ID 21 and uses the HTTP status code 200 to indicate that the change is successful.

Plural nouns are recommended

Recommendation:



/employees
/employees/21Copy the code

Is not recommended:



/employee
/employee/21Copy the code

Actually, it’s a matter of personal preference, but plural forms are more common. In addition, it is more intuitive to use the GET method on the resource collection URL, especially GET /employees? State =external, POST /employees, PUT /employees/56. But most importantly: avoid mixing plural and singular nouns, which can be confusing and error-prone.

For optional, complex parameters, use the query string (?) .

Not recommended:



GET /employees
GET /externalEmployees
GET /internalEmployees
GET /internalAndSeniorEmployeesCopy the code

To make your URLS smaller and more concise. Set up a base URL for the resource, representing optional, complex parameters as a query string.



GET /employees?state=internal&maturity=seniorCopy the code

Use the HTTP status code

RESTful Web services should respond to client requests with appropriate HTTP status codes

  • 2XX – Success – All is well
  • 4XX – Client error – If there is an error on the client (such as the client sending an invalid request or not being authorized)
  • 5xx – Server error – If the server has an error (for example, an error while trying to process a request) refer to the HTTP status code on Wikipedia. However, most of these HTTP status codes are not used, and only a fraction of them are used. The following are usually used:
2 xx: success 3xx: redirection 4xx: Client error 5xx: Server error
200 success 301 permanent redirect 400 Error request 500 Internal server error
201 to create 304 Resources are not modified 401 unauthorized
403 ban
404 not found

Returns a useful error message

In addition to appropriate status codes, useful error messages and detailed descriptions should be provided in the HTTP response body. Here’s an example. Request:



GET /employees?state=superCopy the code

Response:



// 400 Bad Request
{
    "message": "You submitted an invalid state. Valid state values are 'internal' or 'external'","errorCode": 352,"additionalInformation":"http://www.domain.com/rest/errorcode/352"
}Copy the code

Use small hump nomenclature

Use the small hump nomenclature as the attribute identifier.



{ "yearOfBirth": 1982 }Copy the code

Do not use underlining (year_of_birth) or large camel name (YearOfBirth). Typically, RESTful Web services will be consumed by clients written in JavaScript. The client converts the JSON response into a JavaScript object (by calling var person = json.parse (response)), and then calls its properties. Therefore, it is best to follow the common specification for JavaScript code. Contrast:



person.year_of_birth // Not recommended, violating the general specification for JavaScript code
person.YearOfBirth // JavaScript constructor names are not recommended
person.yearOfBirth / / recommendCopy the code

Force a version number in the URL

From start to finish, publish your RESTful apis using version numbers. Putting the version number in the URL is required. If you have incompatible and disruptive changes, the version number makes it easier to publish the API. When you release a new API, you simply increment the number in the version number. That way, clients can migrate to the new API without getting bogged down in calling a completely different new API. Use the intuitive “V” prefix to indicate that the following number is the version number.



/v1/employeesCopy the code

You don’t need to use a minor version number (” V1.2 “) because you shouldn’t release API versions too often.

Provide paging information

It is not a good idea to return all the database resources at once. Therefore, a paging mechanism needs to be provided. You usually use the parameters offset and limit that are well-known in the database.



/employees? offset=30&limit=15# returns3045The employees'Copy the code

If the client does not pass these parameters, the default values should be used. The usual defaults are offset = 0 and limit = 10. If database retrieval is slow, you should reduce the limit value.



# / employees to return010The employees'Copy the code

Also, if you use paging, the client needs to know the total number of resources. Example: Request:



GET /employeesCopy the code

Response:



{
  "offset": 0."limit": 10."total": 3465."employees": / /... }Copy the code

Use verbs for non-resource requests

Sometimes API calls do not involve resources (such as computation, translation, or transformation). Ex. :



GET /translate?from=de_DE&to=en_US&text=Hallo
GET/calculate? para2=23&para2=432Copy the code

In this case, the API response does not return any resources. Instead, an operation is performed and the result returned to the client. Therefore, you should use verbs rather than nouns in urls to clearly distinguish resource requests from non-resource requests.

Consider specific resource searches and cross-resource searches

Providing search for specific resources is easy. Simply use the appropriate resource collection URL and append the search string to the query parameter.



GET /employees?query=PaulCopy the code

If you want to provide a global search for all resources, you need another approach. As mentioned earlier, use verbs instead of nouns for non-resource request urls. Therefore, your search url might look like this:



GET /search?query=Paul   // Return employees, customers, Suppliers, etc.Copy the code

Add links to browse other apis in the response parameters

Ideally, clients are not asked to construct urls that use REST apis themselves. Let’s consider an example. The client wants to access the payroll of the employee. To do this, he must know that he can pass in the URL (for example/employees / 21 / salaryStatements) additional strings “salaryStatements” to access the compensation table. This string concatenation is error-prone and difficult to maintain. If you change the way you access the REST API for payroll (for example, to /employees/21/salary-statement or /employees/21/paySlips), all clients will stop. A better solution would be to add a links field to the response parameter so that the client can change it automatically. Request:



GET /employees/Copy the code

Response:



/ /... {"id":1."name":"Paul"."links": [{"rel": "salary"."href": "/employees/1/salaryStatements"}}], / /...Copy the code

If the client relies entirely on the fields in links to get the payroll table, and you change the API, the client will always get a valid URL (as long as you change the link field, the requested URL will automatically change) without interruption. Another benefit is that your API becomes self-describing and requires less documentation. When paging, you can also add examples of links to get to the next or previous page. Simply provide examples of links with appropriate offsets and restrictions.



GET /employees? offset=20&limit=10Copy the code


{
  "offset": 20."limit": 10."total": 3465."employees": / /... ."links": [{"rel": "nextPage"."href": "/employees? offset=30&limit=10"
     },
     {
        "rel": "previousPage"."href": "/employees? offset=10&limit=10"}}]Copy the code

Related reading:

  • An article by the author on best practices for testing RESTful services in Java.
  • The author strongly recommends a book, Brain Mulloy’s Nice Paper, as the basis for this article.