简体   繁体   中英

Is there a conventional way of returning error statuses from JSON web services?

I have a .NET .ashx handler, which receives a jQuery AJAX post, formats a web service request to a third-party service and consumes the result. On success, it instantiates an anonymous object with the relevant information and formats a JSON response string.

In the event of a web service error, I do the following:

context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.StatusDescription = wsResult.ErrorCode;

This makes both the status code and description easily accessible to the jQuery AJAX error callback; however, the way I have implemented this is quite arbitrary.

After doing some reading around, I can't find a conclusive answer to the following question: Is there an accepted, universal convention (or - even - specification ) for returning error states to JSON-based AJAX calls, which allows any consumer to know what to expect, or is this as arbitrary as a return type for any other function call?

So, is this a perfectly acceptable way of returning an error status to the AJAX caller, or is there a "proper" way of formatting a JSON error response?

As others have said, there's no universal convention. The REST "community" is still in the process of finding some consensus in matters like these - a consensus may never be found. Just to give a few examples:

Status code

By default ServiceStack.NET, a widely used C# REST library web service framework, returns the object (or empty response) with a status code, eg:

201 Created

Or:

200 OK

In the case of a validation error (eg an ArgumentException ), it may do eg:

400 Bad Request

And here's already the first point where things start to vary. Some people like the 400 status code for things like validation errors - others don't, since 400 really indicates malformed syntax in the request format itself .

Some prefer 422 Unprocessable Entity for validation errors, a WebDAV extension to the HTTP protocol, but still perfectly acceptable technically.

Others think you should simply take one of the error status codes unused in the HTTP protocol, eg 461 . Twitter have done that with (among others) 420 Enhance Your Calm to notify a client that they're now being rate limited - even if there's an (on the surface) acceptable (and recommended) status code 429 Too Many Requests for that purpose already.

Etc. It's all a matter of philosophy.

As for 500 Internal Server Error , the same applies - some think it's perfectly fine for all kinds of error responses, others think that the 5xx errors should only be returned on exceptions (in the real sense - ie, exceptional errors). If the error is truly exceptional, you mostly wouldn't want to take the chance and pass on any actual exception info, which may reveal too much about your server.

Leading us to what (if anything) to return in the JSON result? Same thing...

Response

200 OK may be quite enough for responding to eg a request to delete a resource, if no error occurred. In the same way, 404 Not Found is enough to tell the client that the requested deletion couldn't be performed because the entity to delete wasn't found. In other cases you may require more than that.

Some people think you should include as much of the needed info as possible in the response headers, often having an empty response with only headers. For example, on a creation, return 201 Created and put the created entity's ID (as a resource URI) in Content-Location . No response content needed.

I personally think that if you're making a public API, it's a good idea to return both appropriate headers and content, even if the content is somewhat redundant. Ie:

HTTP/1.1 404 Not found
Content-Type: application/json; charset=utf-8
...

{
   'Success': false,
   'Message': 'The user Mr. Gone wasn't found.'
}

(I don't actually include the Success property, but I might want to, depending on my frame of mind when designing the API).

When running in DEBUG mode, I also include a string representation of the internal service call - eg 'Request': 'GetUser { id: 5 }' , a timestamp, and the stack trace. It's all a matter of convenience, though. It's easy enough to code a client with proper user friendly error messages simply based on 404 Not found . Some other errors (eg validation) may need more context, though. For example:

HTTP/1.1 422 Validation Error
Content-Type: application/json; charset=utf-8
...

{
   'Success': false,
   'Message': 'The request had validation errors.',
   'Errors':
   {
       'UserName': 'The user name must be provided.',
       'Email': 'The email address is already in use.'
   }
}

ServiceStack.NET does something like this by default , but with slightly different properties and content. Microsoft's own Web API framework does something similar . The JSend spec linked in the related question is another variation.

And so on.

In short, no, there isn't any universal convention - not yet, at least. Lots of people (putting more thought into it than I) are working on it. But still, there may never be. And your method is perfectly acceptable.

(Yes, this was very lengthy - mostly because I've been searching around for the same kind of "universal convention" for a while).

For more on status codes, this is an excellent article (too long to quote here)

No, there is not any single major standard out there, although this might be nice. It might be useful if you are making yourself a standard to include success and details , but it depends on exactly how you are using it. I think the big advantage is when you implement a standard at least across all of your own code for consistency, eg http://ricardocovo.com/2012/03/08/asp-net-mvc-using-json-standard-responses-for-your-ajax-calls/

Your response is completely acceptable if it's fitting your needs. If I was working with an API, I would see that error response as 'standard', containing a response code and description, although I might like a boolean success for ease of use.

My 2 cents:

I would say that foremost the statuscode that you send back as the response is the best error indicator and gives a lot of options in the 4xx and 5xx range... (Even when you try to HttpGET some coffee from a teapot, you can use 418 :D)

As anything that is not 200 is some sort of error, and the http status codes are well documented in which case they should be used, any further error message is not really necessary (The error description is implied by the statuscode). Browsers are the ones doing the request, and they don't care about the errormessage, only the statuscode.

Any extra errormessages might also just away too much information to possible hack attempts. I mean, returning a 403 Forbidden is enough information on its own, you shouldn't also return a message saying "Not allowed, please use '1234' as the password instead" :)

Google JSON Style Guide uses data xor error objects ...

In order to maintain a consistent interface across APIs, JSON objects should follow the structure outlined below. This structure applies to both requests and responses made with JSON.
. . .
The JSON object has a few top-level properties, followed by either a data object or an error object, but not both.

An example ...

{
  "apiVersion": "2.0",
  "error": {
    "code": 404,
    "message": "File Not Found",
    "errors": [{
      "domain": "Calendar",
      "reason": "ResourceNotFoundException",
      "message": "File Not Found
    }]
  }
}

I generally adopt the names, structure and content of the server-side system as a matter of practice.

This practice ensures that front-end developers communicate to back-end developers using a vocabulary they already understand, and it doesn't set a standard/precedent whereby back-end developers are tasked with implementing new formats as front-end developers and designers invent new concepts (an error is an error, let's not split hairs about "type" and "meta", which are mere attributes of any given error.)

So, for example, if I'm returning error details to "a JSON client" and the service endpoint is implemented using C#, I would want to return a JSON document which looked like this:

{ 
  "Message": "An error has occurred.", 
  "ExceptionMessage": "Index was outside the bounds of the array.", 
  "ExceptionType": "System.IndexOutOfRangeException", 
  "StackTrace": "   at WebApiTest.TestController.Post(Uri uri) in c:\\Temp\\WebApiTest\\WebApiTest\\TestController.cs:line 18\r\n   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClassf.<GetExecutor>b__9(Object instance, Object[] methodParameters)\r\n   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)\r\n   at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)",
}

Not to parrot the accepted answer, of course, I only want to convey that the value in uniformity is enormous, especially if you're a polyglot (or "an entirely new programmer" who is simply not sure of one way or another.)

It may not matter to you now, but in 2, 3, or 5 years of maintenance you may start caring, and you may find yourself having to "retrain" in the long run as you run into other developers that embrace a similar practice of conformity and you're the only guy on the team still trying to invent new formats (when it's not necessary.)

NOTE : To be clear I am not suggesting you serialize exceptions down to the client. Although, that may be a perfectly valid option in many scenarios including debugging, secure private clouds, or when there is no sensitive data/IP, etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM