I use ResponseObjectSerializable like described here:
https://github.com/Alamofire/Alamofire#generic-response-object-serialization
And i want to validate if the status code is in a range
https://github.com/Alamofire/Alamofire#validation
My call looks like this:
Alamofire.request(Router.Something())
.validate(statusCode: 200..<300)
.responseObject { (request, response, object:Object?, error) in
println(object)
println(request)
println(response)
}
My Problem is if the validation fails responseObject anyhow get called and try to serialize the empty response.
How I can handle that without validate the response a second time in my ResponseObjectSerializable?
That's a really good question.
The long story short is that you can't. Your responseObject serializer is not notified about a validation error. It only receives the request, response and data objects and needs to attempt to construct the object from the data.
The ResponseObjectSerializable link you posted does exactly that. If the serialization succeeds, it will return a valid object. If it fails, it will return a nil object and a serialization error.
Where it gets interesting is if you return a serialization error, but the validation also failed. In that case, your completionHandler will actually be called with a nil object, and the validation error, not the serialization error. Alamofire prioritizes the validation error over the serialization error if the validation is run before the responseObject.
As a sidenote, your responseObject serializer should handle the data coming back from the server safely, regardless of the status code that was returned. If parsing the data fails, your serializer should return a serialization error. If it succeeds, then return the object.
Related
I am trying to return a custom error response when an HTTP 500 Internal Error is encountered. If I use c.JSON(http.StatusInternalServerError, CustomError{}) when a database write error occurs, Gin ignores my CustomError struct and substitutes a default "500 server Error" message.
How can I add information to the default response or return a custom response while still using the HTTP 500 Internal Server error code?
This is what I am trying to accomplish. Notifying users of a duplicate entry in the Mongo database. Gin ignores my DBErrorResponse struct and just returns the default 500 error json response.
_, err := handler.collection.InsertOne(handler.ctx, data)
if err != nil {
if mongo.IsDuplicateKeyError(err) {
dbErr := err.(mongo.WriteException)
c.JSON(
http.StatusInternalServerError,
models.DBErrorResponse{
Type: "Write Exception",
Code: dbErr.WriteErrors[0].Code,
Err: "similar record exists",
})
return
}
If the error is caused by a user providing a duplicate key, it's not an internal server error. You might want to use something like BadRequest(400), which suits duplicate value far more, if provided by the client. Thus, you should be able to return a custom error message with StatusCode 400.
Additionally, as far as I know, InternalServerError(500) is not designed to provide a 'server-side' problem feedback to the client, since, well, it's not public information. Although I'm not certainly sure if that's so and if is, why.
UPD: As Gavin mentioned, httpCode 409 is far better choice, here is the doc:
HTTP 409 error status: The HTTP 409 status code (Conflict) indicates that the request could not be processed because of conflict in the request, such as the requested resource is not in the expected state, or the result of processing the request would create a conflict within the resource.
I have the following HTTP handler function:
func (h *UptimeHttpHandler) CreateSchedule(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
dec := json.NewDecoder(r.Body)
var req ScheduleRequest
if err := dec.Decode(&req); err != nil {
// error handling omited
}
result, err := saveToDb(req)
if err != nil {
// error handling omited
}
// Responding with 201-Created instead of default 200-Ok
w.WriteHeader(http.StatusCreated)
enc := json.NewEncoder(w)
if err := enc.Encode(result); err != nil {
// this will have no effect as the status is already set before
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "%v", err)
}
}
Above code does the following:
Request comes as a JSON data. It is decoded into req
Request is persisted in the DB. This returns a result object which will be the response
Now here, as soon as the DB insert is successful, we set the status code to 201. Then use a JSON encoder to stream the JSON encoded value directly to ResponseWriter.
When encode returns an error, I need to change the status code to 500. But currently I can't do it as Go allows to set the status code only once.
I can handle this by keeping the encoded JSON in memory, and setting the status code only when it is success. But then this creates unwanted copies and not really nice.
Is there a better way to handle this?
Expanding my comment a bit:
There is no way to do this without buffering. And if you think about it, even if there was, how would that be implemented? The response header needs to be sent before the contents. If the code depends on encoding the response, you must first encode, check the result, set the header and flush the encoded buffer.
So even if the Go API supported a "delayed" status, you would be basically pushing the buffering problem one level down, to the http library :)
You should make the call - either it's important to get the response code right at all costs and you can afford to buffer, or you want to stream the response and you cannot change the result.
Theoretically Go could create an encoding validator that will make sure the object you are trying to encode will 100% pass before actually encoding.
BTW, this makes me think of another thing - the semantic meaning of the response code. You return HTTP Created, right? It's the correct code since the object has in fact been created. But if you return a 500 error due to encoding, has the object not been created? It still has! So the Created code is still valid, the error is just in the encoding stage. So maybe a better design would be not to return the object as a response?
The JSON marshalling may fail if some of your types (deeply) can not be serialized to JSON.
So you should ask yourself: what are the case where the encoding of the response can fail?
If you know the answer, just fix those cases. If you don't know, this is probably that those cases just don't exist.
If you are not sure, the only way to catch errors is to buffer the encoding. You have to balance the "not nice" between buffering and not returning a clean error.
Anyway, returning a 500 error if just the JSON encoding failed is really not nice. At least, you should revert the object creation (the work of saveToDb), so you have to wrap in a transaction that you will rollback in any failure cases.
I have a sightly java class(a java class included inside component) which calls an api and checks whether the response in null. If its null, I have to call the error component. When I am setting the response status as 404, it is not working because before executing that line, it is taking the response as 200. How to call the error component in this case.
Note: I have tried redirecting the URL to error.html, but thats not the proper solution.
It is generally bad practice to redirect or change the response status from a component/view as the response might already have some content that was committed.
You might want to redesign you application to either:
Call the remote API at the beginning of the request handling and, if the remote API returns null, return a 404 before anything else is committed to the response.
Handle the null response from the remote API by either:
Rendering an appropriate response, in case the null response is expected/permitted in some cases.
Throwing an exception, which will result in status 500, in case the null response is not expected.
We have a REST service where the response to a request may be an error message. A simple example is the request is a formula to calculate and the formula might have a divide by zero. In that case the response is an error code and error message.
So the communication with the REST service is all good. The service itself is responding to the request. But the response is an error message instead of the expected result.
In this case what is the best response code to use? 200 to say the entire communication process is good and we look in the returned JSON to determine if it’s an error? 500 to say it’s an error, but then look to see if we have the expected JSON to determine it was an error in the calculation? Some other code which says we are getting a response from the server but the response is an error message?
A simple example is the request is a formula to calculate and the formula might have a divide by zero. [...] In this case what is the best response code to use?
I would use 422 Unprocessable Entity
The 422 (Unprocessable Entity) status code means the server understands the content type of the request entity (hence a 415(Unsupported Media Type) status code is inappropriate), and the syntax of the request entity is correct (thus a 400 (Bad Request) status code is inappropriate) but was unable to process the contained instructions. For example, this error condition may occur if an XML request body contains well-formed (i.e., syntactically correct), but semantically erroneous, XML instructions.
Don't rely only on HTTP code anyway, always add a description of the error in the body. I believe it's common practice to have all your endpoints reply with a JSON with success (true or false) and something like error (with the error message) if success if false, or data (with the result) if success is true.
For error messages we can use 4XX Bad Request
Look at this post, for various status codes.
http://www.restapitutorial.com/httpstatuscodes.html
Depending on its mood and information we send in the request, our server might send back an empty-bodied 204 response with content type text/plain or something else like a 400-series response with an application/json body detailing the error. How should we handle this?
We thought something like this:
Alamofire.request(request)
.responseString({ (request, response, string, error) -> Void in
// Happy dance
})
.responseSwiftyJSON({ (request, response, json, error) -> Void in
// Parse the error out of the json response and inform the user
})
... but it seems all the chained response handlers are executed. Is there a way to say "only call this one for a specific error type" or "only call this one for a specific response code"?
Are we missing something about how Alamofire is meant to work?