Mark successful siesta response as error - ios

I'm working with a really strange (and nasty) API that I have no control over, and unfortunately when an invalid request is made, instead of responding with a 4xx status, it responds with a 200 status instead.
With this response, it also changes the response body from the usual XML response to plain text, but does not change the content type header. You can imagine how annoying this is!
I've got Siesta working with the API and the fact that it is no actually RESTful in the slightest, but I'm unsure how to get the next part working - handling the unsuccessful requests.
How do I go about transforming a technically valid and successful 200 response, into an error response? Right now I have the following setup:
configure("/endpoint") {
$0.mutateRequests { req in
... perform some mutation to request ...
}
$0.pipeline[.parsing].add(self.XMLTransformer)
}
configureTransformer("/endpoint") {
($0.content as APIResponse)
.data()
.map(Resource.init)
}
This is working just fine when the response actually is XML, however in the scenario where the response is an error, I receive the following:
bad api request: invalid api key
or something similar to this. The XMLParser class is already handling this, and in turn marks itself as having come across an error, however I don't know how to make Siesta realise that there is an error, and to not call my transformer but instead mark the request as failed to I can handle the error elsewhere.
How can I achieve what I'm after?

configureTransformer is just a common-case shortcut for the full-featured (but more verbose) arbitrary transformers Siesta’s pipeline supports. Full transformers can arbitrarily convert any response to any other, including success → failure and failure → success. The user guide discusses this a bit.
You can see this in action in the example project, which has a customer transformer that does something very similar to what you want, turning a 404 failure into a success with the content false. It is configured here and defined here. That example does a failure → success transformation, but you should find the code adaptable for your success → failure purposes.

Related

Swashbuckle refuses to show response example for HTTP 500

I'm using Swashbuckle for a web api app in .Net Core 3.1. I want response examples for various response codes. I can get all of them working except HTTP 500. These are the attributes on the a particular method:
[SwaggerRequestExample(typeof(GroupInfoRequest), typeof(GroupInfoRequestExample))]
[SwaggerResponseExample(Status200OK, typeof(GroupInfo200Example))]
[SwaggerResponseExample(Status400BadRequest, typeof(GroupInfo400Example))]
[SwaggerResponseExample(Status403Forbidden, typeof(GroupInfo403Example))]
[SwaggerResponseExample(Status404NotFound, typeof(GroupInfo404Example))]
[SwaggerResponseExample(Status500InternalServerError, typeof(GroupInfo500Example))]
[ProducesResponseType(Status200OK)]
[ProducesResponseType(Status400BadRequest)]
[ProducesResponseType(Status403Forbidden)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status500InternalServerError)]
I can get all of them to render except the GroupInfo500Example. The application only returns an HTTP 500 to indicate an internal exception that isn't caught by other exception handlers. It is intended to return a body that contains, among other things, a GUID that can be passed in to our support organization to help them look up the exception in the application logs. I can not get the example to render for any 5xx error. If I change it to another status code, it renders, so it's specifically the 5xx result that doesn't render. I've checked the openapi json produced and it's not produced as part of the generated JSON. Is there a filter in place that keeps 5xx response docs from showing response examples?
Finally figured it out. I was missing part of the 'ProducesResponseType' attribute. It needs to have the return type as well as the HTTP status code. This works:
[ProducesResponseType(typeof(ADServiceOperationMultipleResult<GroupActionRequestForUsers, UserQuery>), Status200OK)]
[ProducesResponseType(typeof(ADServiceOperationMultipleResult<GroupActionRequestForUsers, UserQuery>), Status400BadRequest)]
[ProducesResponseType(typeof(ADServiceOperationMultipleResult<GroupActionRequestForUsers, UserQuery>), Status403Forbidden)]
[ProducesResponseType(typeof(ADServiceOperationMultipleResult<GroupActionRequestForUsers, UserQuery>), Status404NotFound)]
[ProducesResponseType(typeof(ADServiceOperationMultipleResult<GroupActionRequestForUsers, UserQuery>), Status422UnprocessableEntity)]
[ProducesResponseType(typeof(ADServiceOperationMultipleResult<GroupActionRequestForUsers, UserQuery>), Status500InternalServerError)]
Oddly enough, some status codes were including the example without it, but now the examples appear consistently as long as I include the method return type in the attribute.

Debugging an API request

I'm trying to post a user status update to the Goodreads API.
Most of the time my request returns 200 OK and does nothing. Every now and then, though, it returns 201 Created and the status is updated. When it works it's always the first time I try to make the call after running the app in iOS simulator. Subsequent calls never work.
I don't think the problem is the API itself, since the official Goodreads iOS app uses the same call and it always works.
Their API is famous for having problems with calls that include brackets in the parameters, but I can make other calls that contain brackets and they work fine, the problem is just this one.
I'm using OAuthSwift and this is my code:
oAuth.client.post(
"http://www.goodreads.com/user_status",//.xml",//?user_status[book_id]=6366035&user_status[page]=168",
parameters: ["user_status[page]" : 168, "user_status[book_id]" : 6366035, "format" : "xml"],
//headers: ["Content-Type" : "application/x-www-form-urlencoded"],
success: {
data, response in
print("")
print(response)
},
failure: {
error in
print("")
print(error)
}
)
(The commented out parts are alternatives I have tried unsuccessfully.)
I'm printing the base string that gets signed and it looks the same for the calls that work and the ones that don't, except for the nonce and the timestamp, obviously.
In the headers is also included the oauth_signature, which changes every time and sometimes contains characters that are encoded by OAuthSwift, so that could account for the call working just some of the time (it could work only when the signature doesn't contain a certain character)… but I'm printing out the headers too and I don't see any patterns or any discernible difference between the headers of the calls that work and those of the calls that don't.
So now I don't know what to test anymore… I'm checking the base string and the headers for calls that work and for calls that don't and they look the same… Could anybody think of something else that changes between calls and I should check? I have no idea what could be causing this and I don't know how to debug it.
Thanks in advance,
Daniel
Edit: Very weird… I tried my request with Paw, a Mac REST client, and with Chrome's Postman extension. If I use https I get 404 on my first call, then 201 on the second, then 404 on the third, 201 on the forth and so on. It works every other time. The time it works it doesn't matter if I use http or https, it works as long as there was a failed https request just before.
So I tried doing the same in my app: I added two https calls one after the other… in my app they always return 404.
So it seems like Postman, Paw and OAuthSwift are handling the requests differently. I don't know what could be the difference between those clients… the signature base string seems to be the same for all three, the headers too… so what else could change between them?
In the newer versions of Xcode you can only communicate with a HTTPS server. I expect Google support that so you can change the URL. Or you can edit your Info.plist file.
App Transport Security Settings > Allow Arbitrary Loads > YES

iOS / Obj-c - Get HTTP status code response message (reason phrase)

I'm working with an API that sends back HTTP 406's for many different errors, along with a custom message (reason phrase). It may look something like:
406 Not Acceptable: User is already logged in
406 Not Acceptable: Missing password field
406 Not Acceptable: Node does not exist.
I can get the 406 status code and the standard "Not Acceptable" string using:
NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)response;
NSInteger statusCode = [HTTPResponse statusCode];
[NSHTTPURLResponse localizedStringForStatusCode:HTTPResponse.statusCode];
However I really require the reason phrase message to know how to handle the response. How can I get it, preferably using the standard iOS SDK?
I really require the reason phrase message to know how to handle the response.
Then the API is broken. The reason phrase is a debugging aid only. It's not meant to inform client behaviour.
From RFC 2616 § 6.1.1:
The Status-Code is intended for use by automata and the Reason-Phrase is intended for the human user. The client is not required to examine or display the Reason- Phrase.
If there is information about the response that cannot be conveyed by the status code alone, the proper place for it is as a header or in the response body. The reason phrase is not a correct place to put information necessary for a client to use.
Status code 406 means that the server cannot respond with the accept-header specified in the request.
406 Not Acceptable The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request.
These error codes are not iOS-specific. If you want to display different messages based on different occurrence reasons, I suppose you should check it with your API/web server, and use conditions in your code to display your custom messages for each of them.
Ultimately, you can get the reason phrase using the ASIHTTPRequest library.
http://allseeing-i.com/ASIHTTPRequest/
It was just as simple to use in my case as AFNetworking and NSURLSession.

How to use REST assured?

I have never used JUnit or other testing frameworks. All i know is how to develop rest service. I recently saw REST assured framework to test REST api. But all the articles that i found looks like below. But i don't know how to pass request xml and how will i get response and when should i call this method.?
Do i need to use some other tool before this REST assured.? I am completely beginner in this kind of testing frameworks. Please show me some light in this world. All i know is how to send request and check values in the response in SOAPUI. I have never tried this.
expect().
statusCode(200).
body(
"user.email", equalTo("test#hascode.com"),
"user.firstName", equalTo("Tim"),
"user.lastName", equalTo("Testerman"),
"user.id", equalTo("1")).
when().
get("/service/single-user/xml");
expect() /* what u expect after sending a request to REST Service */
statusCode(200) /*you are expecting 200 as statuscode which tells request handled successfully at server */
body()
/* the conditions given in body are compare the value with expected values. "equalTo" hamcrest matcher condition (you need to have hamcrest jar in java classpath).*/
when(). /* as is name says above all will be done after sending get/post/put/delete request right so before you put these get,post,put,delete you will have this method as prefix */
get("/service/single-user/xml")
/* the actual REST API request url goes here. can be GET/POST/PUT/DELETE. the confusion for you is its only showing half part which is base path.you can give entire request url in get() method.*/
more on:
http://rest-assured.googlecode.com/svn/tags/1.8.1/apidocs/com/jayway/restassured/RestAssured.html
I hope this helps.

Does every successful HTTP request always return status code 200?

In Delphi, I'm using Indy's TIdHTTPWebBrokerBridge coupled with TIdHTTP to send/receive data via HTTP. On the Server, I don't have any fancy handling, I always just respond with a simple content stream. If there's any issues, I only return information about that issue in the response content (such as authentication failed, invalid request, etc.). So, on the client side, can I assume that every successful request I make to this server will always have a response code of 200 (OK)?
I'm wondering because on the client, the requests are wrapped inside functions which return just a boolean for the success of the request.
Inside this function:
IdHTTP.Get(SomeURL, AStream);
Result:= IdHTTP.ResponseCode = 200;
This function handles any and every request which could possibly fetch data. If there were any issues in the request, This function should return False. In my scenario, since I always return some sort of content on the server, would the client always receive a response code of 200 in this function?
I guess the real question is, if I always return some sort of content and handle all exceptions on the server, then will the server always return status code of 200 to each request?
"Does every successful HTTP request always return status code 200?"
See w3.org: HTTP/1.1 Status Code Definitions (RFC 2616)
The answer is No. All 2xx are considered successful.
That may depend on the HTTP method used.
Should your web-server application always return 200 upon success? That may as well depend on the request method and the signal it intends for the client . e.g.
for PUT method (emphasis is mine):
If an existing resource is modified, either the 200 (OK) or 204 (No
Content) response codes SHOULD be sent to indicate successful
completion of the request.
for POST method:
The action performed by the POST method might not result in a resource
that can be identified by a URI. In this case, either 200 (OK) or 204
(No Content) is the appropriate response status, depending on whether
or not the response includes an entity that describes the result.
If a resource has been created on the origin server, the response
SHOULD be 201 (Created) and contain an entity which describes the
status of the request and refers to the new resource, and a Location
header (see section 14.30). Responses to this method are not
cacheable, unless the response includes appropriate Cache-Control or
Expires header fields. However, the 303 (See Other) response can be
used to direct the user agent to retrieve a cacheable resource.
As you can learn from the RCF, every method SHOULD have it's own success status codes, depending on the implementation.
Your other question:
"can I assume that every successful request I make to this server will always have a response code of 200 (OK)?"
You can always expect Status code 200, if your web server always responds with Status 200. Your web server application controls what response it returns to the client.
That said, Status code 200 is the Standard response for successful HTTP requests (The actual response will depend on the request method used), and in the real world of web servers, SHOULD be set as default upon successful request, unless told otherwise (As explained in Remy's answer).
To answer your specific question:
can I assume that every successful request I make to this server will always have a response code of 200 (OK)?
The answer is Yes, because TIdHTTPWebBrokerBridge wraps TIdHTTPServer, which always sets the default response code to 200 for every request, unless you overwrite it with a different value yourself, or have your server do something that implicitly replies with a different response code (like Redirect() which uses 302, or SmartServeFile() which uses 304), or encounter an error that causes TIdHTTPServer to assign a 4xx or 5xx error response code.
However, in general, what others have told you is true. On the client side, you should handle any possible HTTP success response code, not just 200 by itself. Don't make any assumptions about the server implementation.
In fact, TIdHTTP already handles that for you. If TIdHTTP encounters a response code that it considers to be an error code, it will raise an EIdHTTPProtocolException exception into your code. So if you don't get an exception, assume the response is successful. You don't need to check the response code manually.
If there is a particular response code that normally raises an exception but you do not want it to, you can specify that value in the optional AIgnoreReplies parameter of TIdHTTP.Get() or TIdHTTP.DoRequest(). Or, if you are are using an up-to-date Indy 10 SVN revision, a new hoNoProtocolErrorException flag was recently added to the TIdHTTP.HTTPOptions property so the EIdHTTPProtocolException exception is not raised for any response code.
Successful resposes are 2xx List_of_HTTP_status_codes
i did the following. Process straight all 200`s and LOG exceptions. worked, not a single non 200 - except unauthorized and timeouts (password or sometimes unavaliable server). but many/all responses will be considered for a wide range of mainstream apps.
while (iRedo < 3) do begin
s := Self.HTTPComponent.Get( sUrl );
if self.HTTPComponent.ResponseCode = 200 then begin
break;
end;
// IDEIA - log what happend if not 200
logWhatHappend( s, HTTPComponent ); // then log content, headers, etc
inc( iRedo ); sleep( 5 );
end;

Resources