Nancy transforms a (404) JsonResponse to Html one - nancy

I have a Nancy Fx application that acts as a pure API endpoint (application/json only, no text/html or browser etc access intended) and a module that returns e.g. the following:
return
userExists
? Negotiate.WithStatusCode(HttpStatusCode.OK)
: Negotiate.WithStatusCode(HttpStatusCode.NotFound);
However, I noticed one particularity - client's that have an Accept-Header set to 'application/json' and perform a GET request here, do get a text/html response back, even worse - in the .NotFound case a Nancy-specific/own 404 error is returned, in case of .OK an exception occurs due to missing Views.
What makes it even stranger for me is that inside my custom IStatusCodeHandler I "see" that the context.Response is a JsonResponse, somewhere down the pipeline this gets handled and (attempted to be) transformed further to text/html somehow though, and I wonder why.
Is there any way I can prevent the transformation to text/html?

This is because Nancy has a DefaultStatusCodeHandler that handles 500 and 404 responses. It's the last thing that runs in the Nancy pipeline before the host takes over the response.
What you're seeing is because the handler gets a 404 response (albeit a JsonResponse), and it can't know whether it's a hard (a route simply didn't exist) or a soft (a route existed but returned 404) status code, so it transforms it to the default 404 page. You might argue that it should check the accept header before doing so, but right now it isn't.
If you don't want this behavior, you can remove the default status code handler by overriding the InternalConfiguration property in your bootstrapper:
protected override NancyInternalConfiguration InternalConfiguration
{
get
{
return NancyInternalConfiguration
.WithOverrides(config => config.StatusCodeHandlers.Clear());
}
}

Related

Optional response body for rest client using RESTEasy

I'm writing a POC for Quarkus. I'm using this quick start guide to build a REST client. The REST service I'll be integrating with is third party. Here is a simple example of my current implementation:
#Path("/v1")
#RegisterRestClient
public class EmployeeApi {
#POST
#Path("/employees")
ApiResponse createEmployee(#RequestBody Employee employee)
}
This works fine. The issue I'm having is that the third party API will, depending on success / failure, return a response body. In the scenario it does fail, it provides details in the response body (ApiResponse) on why it was unsuccessful. When it succeeds, it returns nothing. This causes Quarkus to throw the following exception:
javax.ws.rs.ProcessingException: RESTEASY003145: Unable to find a MessageBodyReader of content-type application/octet-stream and type com.test.app.ApiResponse
I've tried to wrap ApiResponse in an Optional type but does not solve the problem. I see absolutely nothing in Quarkus / RESTEasy documentation that would indicate a work-around.
I'm wondering if I should be using javax.ws.rs.core.Response instead.
The problem is JaxRS tries to fit ApiResponse to a default return type being application/octet-stream
You should make sure to specify explicitly that you're returning application/json
This is possible using #Produces(APPLICATION_JSON) on top of your service.
Here is the correct code snippet
#Path("/v1")
#RegisterRestClient
public class EmployeeApi {
#POST
#Path("/employees")
#Produces(APPLICATION_JSON)
ApiResponse createEmployee(#RequestBody Employee employee)
}

REST request fails with URI encoded path parameter

I use AngularJS (client) and a REST interface in my project (server, javax.ws.rs.*). I'm passing data in a path parameter. It may contain special characters, so I call encodeURIComponent() to encode the arguments prior to sending a request.
Client-side:
$http.put('/foo/data/' + encodeURIComponent(data) + '/bar');
The controller will process the request and send a response.
Server-side:
#PUT
#Path("/data/{data}/bar")
public ResultObject handleFooRequest(#PathParam("data") String data) throws Exception {
return handleRequest(data);
}
This works fine on localhost, however, the request fails when I do a request on our production server (Error 400: Bad request). What am I doing wrong and why is it working on one server and fails on the other? In general, is my approach correct? Do I need to tell RESTEasy to decode the arguments? To my understanding (I read the documentation), it does that on default.

Spring + Angular / IE gets 403 on PUT (others don't)

I have a spring webapp with spring security(3.2.3, so no CSRF protection) and angular.
In a controller i have a method like this one to update the users pw:
#RequestMapping("/accountinfo/password", method = arrayOf(RequestMethod.PUT))
#ResponseBody
#Secured("ROLE_USER")
open fun updateOwnPassword(user: User, #RequestBody password: String) {
val editedUser = user
editedUser.password = encoder.encode(password)
userRepository.save(editedUser)
}
The request is done via angular Service:
function changeOwnPassword(newPassword) {
return $http
.put('accountinfo/password', newPassword)
.then(function (response) {
return response.data
});
}
This works fine in every browser i tested with. Except if using IE 11.0.35 in a Citrix environment (Works outside of it,but can't see any specific configuration).
In that case i get 403 on the Request. When i change the method to POST it works fine again. I could do that for every function where i got this problem of course, but that doesn't seem like a clean solution.
As far as my research goes, i think it's something wrong with the way the browser writes the Request, but that's were i can't find out what to do.
EDIT:
I compared the request headers of both IE 11.0.35 inside and outside of Citrix and they seem exactly the same. The only difference is that the working version uses DNT=1 and the non-working version as WOW64 in the User-Agent attributes?
UPDATE:
I found out that it happens with DELETE too
Found the problem: The client sends the Requests through an additional Proxy that doesn't like PUT and DELETE and just cuts the session cookies off of it. We are adressing that problem with putting the tokens in the header in the future.

What is the use of 'Route' in Restangular functions oneUrl() and allUrl()

That's the signature for oneUrl function: oneUrl(route, url)
And from the documentation:
oneUrl(route, url): This will create a new Restangular object that is
just a pointer to one element with the specified URL.
To me, it seems useless to set Route when you are giving a url for the resource. Why does it exist in the argument list? Why is it mandatory? And how can it be used?
In my use of oneUrl I've found the route name is used to build the URL for subsequent PUT and DELETE operations. For example (pseudo code):
// "GET /api/v2/users/3/ HTTP/1.1" 200 184
var user = Restangular.oneUrl('myuser', 'http://localhost:8000/api/v2/users/3').get();
user.name = 'Fred';
// the following uses the route name and not the URL:
// "PUT /api/v2/myuser HTTP/1.1 404 100
user.put();
I was surprised by this behavior. I expected put() to use the same URL as get(); which would be helpful in my case.
My API uses absolute URLs within the JSON payloads to navigate to all related resources and I wanted to use oneUrl() to GET/PUT instances without recreating the routes in the JS code. But I'm pretty new to Restangular so I might not have the mental model correct.

303 redirection not working with Angular HTTP POST

I am calling an authentication service where I do a $http.post which returns a 303 resonse, redirecting to a get call returning the response.
When I make the post call using Postman, I get the desired response but when I do an angular $http.post call, it returns me a 401 error (which is user not authorized)
Am I missing something while making the angular call? The backend service seems to work fine as it works fine on Postman.
This is how the $http call looks:
$http.post(url, userData).success(function(data, status) {
//handle success
}.error(function(data, status) {
//handle error
});
The url and the user data is constructed absolutely fine in this case.
The reason that you get a GET call is that the browser handle the 303 response before the angular can reach that. And the handling sequence is first go to the browser and then go to the angular framework.
So briefly what happens is : you make call to the server --> the server return the 303 response -> your browser handle the 303 and make a request to some url (should be 'location' in the response header) --> the server receive the request and return the 401 authorized response --> again the browser receive the 401 response first but this time the browser redirect the response to the angular --> at last you can receive the data and status inside the error().
The solution for this could be switching to other response status code like 2xx, and you can get the location from the body. Then you can do the redirection manually. If you HAVE to use 303 or other 3xx as the response code I don't think there's any effective solution at this moment because you can't do much to the browser. As far as I know there might be a solution at browser level but don't know when that will happen.
Hope this can help anyone has the similar issue like this although it has been nearly one year since this issue raised.
Some other ref: https://groups.google.com/forum/#!topic/angular/GKkdipdMbdo
There's similar solution you can see from the link above.
I faced this issue and I found a redirect url in error object after lots of hours struggle.
loginWithLinkedIn() {
let data = {
// some kind of information here
}
return this.http.get(`https://www.someurl.com/oauth/v2/authorization`).subscribe(res => {
console.log(res)
}, err => {
console.log(err.url) // here is the redirect url
window.location.href = err.url
})
}
Note: when you make a request and you get 303 response which is considered as error, that's why we think we are getting error but error contains useful info.

Resources