HTTP service API error message best practise - rest

For a error case when calling some HTTP Rest service API, the response is as follows:
{
"statusCode": "400",
"error": "Bad Request",
"message": "Can not construct instance of java.math.BigDecimal from String value 'a': not a valid representation\n at [Source: org.apache.cxf.transport.http.AbstractHTTPDestination$1#2f650e17; line: 1, column: 2] (through reference chain: com.foo.services.dto.request.ItemToUpdate[\"quantity\"])",
"validation": {
"source": "PAYLOAD",
"keys": ["key"]
},
"errorIdentifiers": [],
}
I am wondering if the message field in the response is appropriate. It does reveal certain level of implementation to the end user. Is this considered as
no particular problem at all
just a bad cosmetic issue that won't cause serious problem, just not readable to end user
potential security risk that definitely needs to be fixed

I think that you should only log the stacktrace on the server side. IMO it's technical hints (in addition, perhaps the end user even doesn't use Java to interact with your API) and the only thing that interests the end user of your API is that there is here a validation error within the provided data.
Another remark is that you use the status code and statusmessage within your response payload. I think that you don't need to duplicate this since it's already present in the response.
I would suggest an error message like that:
{
"messages": {
"quantity": "this must be a valid number"
}
}
I use a JSON structure for the field messages since there could be several validation errors within the provided data. Note that it's only a suggestion and you could extend this to your exact needs.
Hope it helps.
Thierry

Related

Return 403 or 201 if a field is not writable

What is the best practice for a response if the user tries to write to a field he or she is not allowed?
Imagine the user wants to create a resource, for example a pizza: POST /pizza
The body:
{
"name": "Hawaii",
"base": "tomato sauce",
"firstMain": "ham",
"secondMain": "pineapple",
"cheese": true
}
Problem is, you can't put secondMain on a pizza (because its pineapple). What's better? Return a 403 error with the message that field secondMain is not writable or return a 201 with the created pizza but without the secondMain?
What's the best practice for this problem? Didn't found anything on Google.
Returning a 403 with a proper error response would work best. Saying 201 but creating something different then what they originally wanted would probably lead to a unhappy customer. I know I'd be annoyed if my pizza didn't come with pineapple and that's what I ordered.
Really guess it varies on a case by case basis depending on your expected result and what your client is expecting to happen. Just make sure to specify in your documentation that extra data sent up with requests that isn't writable will throw an error.
etc
403 response: {error: {"secondMain": "unknown field", "exampleField": "expected but missing"}}

How to handle multiple types of exception from REST

I have a REST resource that can throw many types of exceptions.
In order to retrieve a pretty response when exception happens I did the following:
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(Exception.class)
public ResponseEntity<String> handleError(Exception ae) {
return new ResponseEntity<String>(ae.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
But from clinet side I can't recognize what type of exception was thrown!
I have some Junits with expected exception, and test cases are failing because of that in different modes.
I can add a new class for Error that will contain the exception name: ae.getClass().getSimpleName(), and the message and return it in response.
Is there a cleaner way?
To handle multiple exception types, you could have a look at what Spring MVC does in the ResponseEntityExceptionHandler class. It handles a number of Exceptions and check their types using instanceof.
But from client side I can't recognize what type of exception was thrown!
I can add a new class for Error that will contain the exception name: ae.getClass().getSimpleName(), and the message and return it in response.
You should avoid leaking this information to the client. What if you refactor your code and rename the exception classes? You will break the client code.
Instead, you should translate the exception into something that can be parsed by client (and, of course, document it properly).
Consider, for example, an OutOfCreditException (made up for this example) to indicate that the client's account doesn't have enough credit for a purchase. It could be translated to the following payload, which could easily be parsed by the client:
{
"error": "out-of-credit",
"title": "You do not have enough credit",
"details": "Your current balance is €10, but the items cost €50",
"balance": 10,
"currency": "EUR"
}
For reporting problems in HTTP-based APIs, also consider having a look at the [RFC 7807][4]: It defines defines a simple JSON document format for represeting problems along with the application/problem+json media type.

"E_INVALID_NEW_RECORD",

{
"code": "E_INVALID_NEW_RECORD",
"details": "Missing value for required attribute EMP_ID. Expected a number, but instead, got: undefined",
"message": "The server could not fulfill this request (POST /employee/create) due to a problem with the parameters that were sent. See the details for more info. The following additional tip will not be shown in production: Tip: Check your client-side code to make sure that the request data it sends matches the expectations of the corresponding attribues in your model. Also check that your client-side code sends data for every required attribute."
}

Google Fit REST API "Unable to fetch DataSource for Dataset: xyz"

I'm testing out a few things in the OAuth 2.0 Playground and trying to get data in and out of Google Fit using their REST API
I have done this previously with success, I just didn't write down what I did.. now I've come back to make it a proper thing and can't get it working again.
I have access to Google Fit datasources via the dashboard. I can get a list of the dataSources that exist from:
https://www.googleapis.com/fitness/v1/users/me/dataSources
And that is successful. I have also created my own stream which has a single floating point weight value on it called
raw:com.google.weight:b6ac18c0:dten.sync
It already has data in it, I put it there last time I used it. I can select all that data by requesting a GET on the following
https://www.googleapis.com/fitness/v1/users/me/dataSources/raw:com.google.weight:b6ac18c0:dten.sync/datasets/0-1432193482000000000
It returns me all the data points I entered last time as JSON
I then try to PATCH the data adding my own data to the folliwng URL
https://www.googleapis.com/fitness/v1/users/me/dataSources/raw:com.google.weight:b6ac18c0:dten.sync/datasets/1432193482000000000-1432193482000000000
With this as a the request body
{
"minStartTimeNs": "1421912895000000000",
"maxEndTimeNs": "1432193482000000000",
"dataSourceId": "raw:com.google.weight:b6ac18c0:dten.sync",
"point": [
{
"startTimeNanos": "1421912895000000000",
"modifiedTimeMillis": "1421912895000",
"endTimeNanos": "1421912895000000000",
"value": [
{
"fPVal": 89.1
}
],
"dataTypeName": "com.google.weight"
}
]
}
But I get back
{
"error": {
"code": 400,
"message": "Unable to fetch DataSource for Dataset: raw:com.google.weight:b6ac18c0:dten.sync",
"errors": [
{
"domain": "global",
"message": "Unable to fetch DataSource for Dataset: raw:com.google.weight:b6ac18c0:dten.sync",
"reason": "invalidArgument"
}
]
}
}
I can't find any one referencing a similar anywhere soo I'm here
Also note if I miss spell my source it tells me off because they don't match the URL, if i include an empty list of data points I get the same error. I'm quite lost so I'm throwing it out there to see if anyone knows what that means
Thanks in advance
edit: i tried changing the hex code for my project's integer code and got an error about untrusted source. so i tried making a new test data source which works as expected. Slightly annoyed but guess I'll just start over..
OK I was stupid and didn't set up my own credentials in the OAuth settings in top right of the dashboard as it said to here. I forgot that bit -_- now I can access my own stream again and it shows my integer project id in the stream id not the hex one
https://developers.google.com/fit/rest/v1/get-started
Now I get invalid argument, but.. whatever >_<
edit 2:
invalid argument was because I have fPVal instead of fpVal and modifiedTimeMillis mills is not supposed to be submitted, obviously

REST: Update resource with unknown (server-generated) value

I have a resource foo with the following structure:
GET /foo/1 returns:
{
"id": 1,
"server-key": "abcdef",
"status": "expired"
}
Status can either be active or expired. If it is expired I want the server to generate a new one.
Normally I'd issue PUT/PATCH foo/1 with the new key, but client doesn't know the key-generation algorithm.
I could also do a POST foo/1/server-key with no body, but that feels strange (I know this isn't very scientific reason though).
Any good ideas/patterns?
In case when you've got expired entity just make POST call on /foo without any parameters and server should return new entity (and HTTP response code should be 201):
{
"id": 2,
"server-key": "xyz",
"status": "active"
}
If some resourece is expired it is unconvinient to make it active again by PUT/PATCH request.
The approach I would adopt is to set a null value to server-key and let the server deal with it, but I do that because it's a consistent behavior in my APIs for the server to fill missing values with defaults.
Other than that, a simple POST to the URI as suggested in the other answer is adequate.
I think that you should use a PUT/PATCH method in your case to ask for generate a token if expired. Generally it's not really RESTful to put an action name within the resource path ;-)
I would see something like that:
Get the element: GET /foo/1
If the status is expired, ask for a new server key to be generated: POST /foo/1. In this case, this method will be used to execute an action to reinitialize the key on the server side
Using the method PUT corresponds to update the complete representation with a new one provided by the client. With the method PATCH, you will do a partial update of the representation.
Here is a link that could give you some hints about the way to design a Web API (RESTful service): https://templth.wordpress.com/2014/12/15/designing-a-web-api/.
Hope it helps you,
Thierry