My current project, a restful API, validates a POST request to create a new user and multiple errors could occur (with HTTP status):
username not set (400 BadRequest)
username is taken (409 Conflict)
server can't establish db connection (500 Internal Server Error)
...
Should I immediatly send back a JSON response like this
{
"status": 400,
"Message": "No username is set"
}
if an error was detected or should is it better if I accumulate all errors like here:
{
"status": <HTTP STATUS CODE>,
"errors": [
{"message": "Username is not set."},
{"message": "Can't access the database."}
]
}
The last approach would not require multiple request to send a valid payload. But which status code should be used, if the username is not set (400 Bad Request) or the server can't access the database (500 Internal Server Error)?
I think if you foresee the need for multiple errors in one request, then the second JSON (with the multiple errors) is preferred. Another benefit of doing the multiple errors response is that as a user of your service, if i get back multiple errors, I can address them all at one shot, instead of addressing them one at a time as I get them.
After some research, the best (most standard) way to respond is a JSON structure of this form:
{
"error": {
"code": "400",
"message": "main error message here",
"target": "approx what the error came from",
"details": [
{
"code": "23-098a",
"message": "Disk drive has frozen up again. It needs to be replaced",
"target": "not sure what the target is"
}
],
"innererror": {
"trace": [ ... ],
"context": [ ... ]
}
}
}
The multiple errors you want to place would be individual elements in the "details" array. Using this structure you would still need some overall summary of the error, however the details would contain as many individual error messages as you want.
This is the format proposed by the OASIS data standard OASIS OData and seems to be the most standard option out there, however there does not seem to be high adoption rates of any standard at this point.
This also conforms to the JSON RPC 2.0 Spec as it requires that the error be an object in a "error" member, and that you have a code and message.
You can find the complete open source library that implements this at: Mendocino JSON Utilities. This library supports the JSON Objects as well as the exceptions.
The details are discussed in my blog post on Error Handling in JSON REST API
Related
Which one is best for Rest API response ?
In here I return some meta information with actual data. Although I am not sure they need to use those meta information or not.
{
"version": "1.0.0",
"isError": false,
"statusCode": 200,
"message": "Permission Object",
"data": {
"id": 1,
"name": "user create",
"created_at": "2022-11-30T10:18:20.000000Z"
}
}
In second example I am returning only the relevant data.
{
"id": 1,
"name": "user create",
"created_at": "2022-11-30T10:18:20.000000Z"
}
Give me suggestion if there are other better way.
I noticed you've used the tag REST, so I assume you are thinking about a RESTful API implementation and have some knowledge about RESTful API design.
If you need some best practices, a couple of them I think are useful. here and here.
Looking at your examples, I would prefer the second option, the reasons are:
IsError can be determined by the HTTP response, e.g. 400, 500, 200, 201, so it's redundant.
Status and Message are also redundant when the response is successful but could be useful in an error state, like in ASP.NET, you can use the ProblemDetails response (you can customize the way you want).
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "Unable to create a new user due to missing name",
"status": 400,
"traceId": "00-0aa7d64ad154a1e1853c413a0def982d-195d3558c90f7876-00"
}
version is an interesting one. Usually, it can be included in the request header or URL. If the API cannot handle the version requested, then it should return an error in the problem details.
Thus, I prefer the second option and send a problem details response when there is an error.
An open source CRM with more than 18K start on github uses Laravel-default api resource
Project Link
Code Example link
Pay attention to status codes
Reference
This one is super important. If there's one thing you need to remember from this article, this is probably it:
The worst thing your API could do is return an error response with a
200 OK status code.
It's simply bad semantics. Instead, return a meaningful status code that correctly describes the type of error.
Still, you might wonder, "But I'm sending error details in the response body as you recommended, so what's wrong with that?"
Let me tell you a story.
I once had to use an API that returned 200 OK for every response and indicated whether the request had succeeded via a status field:
{
"status": "success",
"data": {}
}
So even though the status was 200 OK, I could not be absolutely sure it didn't fail to process my request.
This kind of design is a real no-no because it breaks the trust between the API and their users. You come to fear that the API could be lying to you.
All of this is extremely un-RESTful. What should you do instead?
Make use of the status code and only use the response body to provide error details.
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "Expected at least two items in list."
}
JSON API is a format that works with HTTP. It delineates how clients should request or edit data from a server, and how the server should respond to said requests.
I am attempting to use the GoCardless API sandbox to create a customer but only get a 403 forbidden error
{"error": {
"message": "Forbidden request",
"errors": [ {
"reason": "forbidden",
"message": "Forbidden request"
}],
"documentation_url": "https://developer.gocardless.com/api-reference#forbidden",
"type": "invalid_api_usage",
"request_id": "8b870491-e8ca-436c-a602-bb613bba7d96",
"code": 403
}}
I have used OAuth to get a bearer token which I am using for my post to the sandbox url
https://api-sandbox.gocardless.com/customers
Using this url and the bearer token i can get customers and update a customer that i created manually via the sandbox client portal. If i purposefully make the token an invalid value i get a 401 error instead so I am sure the token is correct.
The documentation for the error type "invalid_api_usage" is as follows
This is an error with the request you made. It could be an invalid
URL, the authentication header could be missing, invalid, or grant
insufficient permissions, you may have reached your rate limit, or the
syntax of your request could be incorrect. The errors will give more
detail of the specific issue.
The fact i can update a customer i believe crosses most of those potential issues of that list.
I am now using the exact post body content of the GoCardless API example for creating customer to verify the syntax should be correct.
{
"customers": {
"email": "user#example.com",
"given_name": "Frank",
"family_name": "Osborne",
"address_line1": "27 Acer Road",
"address_line2": "Apt 2",
"city": "London",
"postal_code": "E8 3GX",
"country_code": "GB",
"metadata": {
"salesforce_id": "ABCD1234"
}
}
}
I am running these requests from SoapUI instead of my application so I can be sure I am sending exactly what i am expecting to send.
From the API documentation I understand my application must have approved payment pages to create customers in the live environment however this is prefixed by a statement that this is not restricted in the sandbox environment.
Restrictions
Whilst the entire GoCardless API is accessible in the sandbox
environment, the following restrictions exist in live.
Payment page restrictions
Unless your payment pages have been approved as scheme rules compliant
by our sponsor bank you must use the Redirect Flow API to create
customers, bank accounts and mandates.
The following endpoints are therefore restricted:
Customers: Create
Customer bank accounts: Create
Mandate: Create, Reinstate
Javascript flow: All endpoints
It seems to me the 403 forbidden response is exactly what i would be getting if Create Customer was restricted in the sandbox environment however the API documentation explicitly states that this is not the case.
My question is basically what could I be doing wrong? Maybe somebody can spot something I have forgotten or am misunderstanding.
The answer is that the sandbox is restricted in the same way as the live environment.
The API documentation appears to be worded in a way that suggest the sandbox is unrestricted however this is not the case.
To remove the restriction in the sandbox environment you need to contact GoCardless support and request it.
I'm writing a Restful API and I have to return error message but I'm not sure on which route to go.
Route 1 - HTTP Status
Use HTTP error status when the client sends bad data
Ex: 401 - Not authorized, 410 - Model does not exist, 412 - Model Validaiton Error, etc
Route 2 - JSON Success or Error Failure
The API returns json and I am considering returning everything with the http header 200, but then in my JSON handle errors and success
Ex:
{ "status" : "error", "message" : "Model validation error", "data" : ["user name required", "user email required"] }
Which route should I go and why? Advantages and disadvantages.
I'm writing a Restful API and I have to return error message but I'm
not sure on which route to go.
Route 1 - HTTP Status
Use HTTP error status when the client sends bad data
HTTP status codes should absolutely be used in any web service implementation claiming to be RESTful. The core principle of the specification is leveraging and extending the Web to fully support transfer of representational state. To allow for interoperation with existing Web infrastructure a REST implementation should indicate status of requests via appropriate HTTP status codes. For example:
200 - Ok
201 - Content Created
401 - Unauthorized
403 - Forbidden
500 - Server Error
501 - Not Implemented
When responding with many of these statuses, its also allowed by the HTTP specification to include an entity representation in the response body. In the case of 'normal', non-error responses, this representation will normally be of the entity that is being 'operated' on by the HTTP request. In the case of error responses the representation, if included, should provide more information on the error that occurred. This is where we segue to your option 2.
Route 2 - JSON Success or Error Failure
The API returns json and I am considering returning everything with
the http header 200, but then in my JSON handle errors and success
You should absolutely not return a 200 OK for all responses. Many well implemented HTTP clients depend on the status code in the response to determine wether it succeeded or not. Always responding with a 200 OK can cause third party client libraries to incorrectly process the incoming data, and also puts a requirement on your client to parse the response body in order to determine if an error actually happened or not.
Having said that, adding additional information about the error that occurred can be very helpful, so definitely do consider adding it to the response body. Your proposed format looks just fine, though to be honest the status element is redundant, assuming you use HTTP status codes appropriately. Something more like:
{
"message": "Model validation error",
"data": [
"user name required",
"user email required"
]
}
Out of curiosity, are there any popular standards for returning server side exceptions (and corresponding details) in an API that returns JSON? The exceptions could be anything from uncaught exceptions in the code or thrown exceptions put in place by the developer.
Currently I'm setting the HTTP Status Code according to their Wiki definitions'
My JSON response would look something like this:
HTTP Code: 401
{'error': 'Authentication required.', 'message': 'You must be authenticated to perform that action.'}
You can take a look at JSON-RPC (http://en.wikipedia.org/wiki/JSON-RPC).
In case of success, the response is like this:
{
"result": ...(returned data)...
...
}
and in case of error, the response is like that:
{
"error": ...(error details)...
...
}
When the error occured, "error" property must be present and "result" property must not be returned. In case there was no error, "error" property must not be set. This way you can distinguish errors from successful responses based solely on the content of the response, without the need to check the headers (like response codes).
The standard structure of native ECMAScript error objects is:
{
name: ...,
message: ...
}
where "name" is the type of the error (the name of the corresponding constructor, like "Error", "SyntaxError", etc.). In your example, you could use the name "AuthentificationError", for instance.
Yes you can do the same.
As we know that Web servers allow us to create custom error pages for HTTP errors, in those custom error pages you have to serve the JSON reply
Example:
Add the following to your WEB-INF/web.xml:
<error-page>
<error-code>401</error-code>
<location>/autherror.jsp</location>
</error-page>
In the autherror.jsp file code the required logic to accomplish your needed JSON reply.
Try being a bit more verbose regarding the error you are sending. This structure has been suggested in some places to give sufficient information both for users and developer using your API
"error":{
"code":"410",
"long":"410001",
"message":"cannot connect to DB",
"developer":"Connection to the DB was unsuccessful due to ...",
"documentation": "mysite.com/help/errors/..."
}
Return the correct HTTP code for error. It is bad practice to return code 200 with error.
Extend the code with your own error IDs
Provide short message that roughly outlines the cause of the error. Consider adding more detailed message for the developers. Have in mind that API error messages are 99.9% of the time read by developers so make them happy by providing technical details (within reason) so that they can resolve ASAP.
In case you keep public documentation about your api and error codes, simply add the url.
I am using Box 2.0 API to delete a file. Using Postman, i enter the following:
https://api.box.com/2.0/files/2605672883
specifying the DELETE action. I've set the authorization header appropriately. When I submit the request, I get the following error:
{
"type": "error",
"status": 400,
"code": "bad_request",
"help_url": "http://developers.box.com/docs/#errors",
"message": "Could not parse JSON",
"request_id": "4934716064fff2a0a70988"
}
This makes me think that i need a JSON object as part of the message body, the documentation specifies that the File ID is the only thing necessary as part of the request. Is this a bug or am I doing something wrong ?
thanks
DELETE /files/{id} requests require (as of last week) an If-Match header with the sha1 of the file in order to prevent race conditions. You can see the updated documentation here: http://developers.box.com/docs/#files-delete-a-file
EDIT: There is a bug in POSTMAN in how it sends DELETE requests. If you send an empty JSON object {} as the body, it should work successfully.