Best Practice for Errors in RESTful API

2020-02-28 05:16发布

What are the best practices for returning HTTP status codes in RESTful API? I am using Laravel 4 for my PHP framework.

In the case of an error, should I use

return Response::json('User Exists', 401);

or

include flag for success

return Response::json([
    'success' => false,
    'data' => 'User Exists'],
    401
);

or

use 200 instead of 4xx, relying on success to determine if there is an error

return Response::json([
    'success' => false,
    'data' => 'User Exists'],
    200
);

And in the case of success and there is no need to return any data, do you still return anything?

PHP API Code

public function getCheckUniqueEmail() {
    // Check if Email already exist in table 'users'
    $uniqueEmail = checkIfEmailExists();

    // Return JSON Response
    if($uniqueEmail) {
        // Validation fail (user exists)
        return Response::json('User Exists', 401);
    } else {
        // Validation success
        // - Return anything?
    }
}

3条回答
地球回转人心会变
2楼-- · 2020-02-28 05:47

When you look at the list of available HTTP status codes, you will at some point realize that there are plenty of them, but used alone they cannot really explain an error by itself.

So to answer your question, there are two parts. One is: How can your API communicate the reasons for an error and add useful information that the user of the API (which in most cases is another developer) can read and act upon. You should add as much information as possible, both machine readable and human readable.

The other part: How can HTTP status codes help in distinguish certain error (and success) states?

This latter part is actually harder than one might thing. There are the obvious cases where 404 is used to tell "not found". And 500 for any errors that are server-side.

I wouldn't use status 401, unless I really want to allow the operation to succeed if there are HTTP authentication credentials present. 401 usually triggers a dialog box in the browser, which is bad.

In case of a ressource being unique and already existing, status "409 Conflict" seems appropriate. And if creating a user succeeds, status "201 Created" sounds like a good idea, too.

Note that there are a lot more status codes, some of them related to extensions of the HTTP protocol (like DAV), some completely unstandardized (like status "420 Enhance your calm" from the Twitter API). Have a look at http://en.wikipedia.org/wiki/List_of_HTTP_status_codes to see what has been used so far, and decide whether you want to use something appropriate for your error cases.

From my experience, it is easy to simply pick a status code and use it, but it is hard to do so consistently and in accordance with existing standards.

I wouldn't however stop here just because others might complain. :) Doing RESTful interfaces right is a hard task in itself, but the more interfaces exists, the more experience has been gathered.

Edit:

Regarding versioning: It is considered bad practice to put a version tag into the URL, like so: example.com/api/v1/stuff It will work, but it isn't nice.

But the first thing is: How does your client specify which kind of representation he wants to get, i.e. how can he decide to either get JSON or XML? Answer: With the Accept header. He could send Accept: application/json for JSON and Accept: application/xml for XML. He might even accept multiple types, and it is for the server to decide then what to return.

Unless the server is designed to answer with more than one representation of the resource (JSON or XML, client-selected), there really isn't much choice for the client. But it still is a good thing to have the client send at least "application/json" as his only choice and then return a header Content-type: application/json in response. This way, both sides make themselves clear about what they do expect the other side to see the content like.

Now for the versions. If you put the version into the URL, you effectively create different resources (v1 and v2), but in reality you have only one resource (= URL) with different methods to access it. Creating a new version of an API must take place when there is a breaking change in the parameters of a request and/or the representation in the response which is incompatible with the current version.

So when you create an API that uses JSON, you do not deal with generic JSON. You deal with a concrete JSON structure that is somehow unique to your API. You can and probably should indicate this in the Content-type sent by the server. The "vendor" extension is there for this: Content-type: application/vnd.IAMVENDOR.MYAPI+json will tell the world that the basic data structure is application/json, but it is your company and your API that really tells which structure to expect. And that's exactly where the version for the API request fits in: application/vnd.IAMVENDOR.MYAPI-v1+json.

So instead of putting the version in the URL, you expect the client to send an Accept: application/vnd.IAMVENDOR.MYAPI-v1+json header, and you respond with Content-type: application/vnd.IAMVENDOR.MYAPI-v1+json as well. This really changes nothing for the first version, but let's see how things develop when version 2 comes into play.

The URL approach will create a completely unrelated set of new resources. The client will wonder if example.com/api/v2/stuff is the same resource as example.com/api/v1/stuff. The client might have created some resources with the v1 API and he stored the URLs for this stuff. How is he supposed to upgrade all these resources to v2? The resources really have not changed, they are the same, the only thing that changed is that they look different in v2.

Yes, the server might notify the client by sending a redirect to the v2 URL. But a redirect does not signal that the client also has to upgrade the client part of the API.

When using an accept header with the version, the URL of the resource is the same for all versions. The client decides to request the resource with either version 1 or 2, and the server might be so kind and still answers version 1 requests with version 1 responses, but all version 2 requests with the new and shiny version 2 responses.

If the server is unable to answer to a version 1 request, he can tell the client by sending HTTP status "406 Not Acceptable" (The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request.)

The client can send the accept header with both versions included, which enabled the server to respond with the one he likes most, i.e. a smart client might implement versions 1 and 2 and now sends both as accept header, and waits for the server to upgrade from version 1 to 2. The server will tell in every response whether it is version 1 or 2, and the client can act accordingly - he does not need to know the exact date of the server version upgrade.

To sum it up: For a very basic API with limited, maybe internal, usage even having a version might be overkill. But you never know if this will be true a year from now. It is always a very good idea to include a version number into an API. And the best spot for this is inside the mime type that your API is about to use. Checking for the single existing version should be trivial, but you have the option of transparently upgrading later, without confusing existing clients.

查看更多
我命由我不由天
3楼-- · 2020-02-28 05:56

There are some list of HTTP Status Code at Illuminate\Http\Response that extend to Symfony\Component\HttpFoundation\Response. You can use that in your class.

For example:

use Illuminate\Http\Response as LaravelResponse;
...
return Response::json('User Exists', LaravelResponse::HTTP_CONFLICT);

It much more readable.

查看更多
The star\"
4楼-- · 2020-02-28 06:03

I wouldn't use 200 status for everything. That just would be confusing.

Jquery allows you to process different response codes differently, there are built in methods for that already so take advantage of using them in your apps and when your app grows you can provide that API for others to use too.

Edit: Also I highly recommend watching this talk about Laravel and API development:

http://kuzemchak.net/blog/entry/laracon-slides-and-video

查看更多
登录 后发表回答