Ignoring web.xml error-page with Jersey's ExceptionMapper - java

#Provider
public class JerseyExceptionMapper implements ExceptionMapper<JerseyException> {
#Override
public Response toResponse(JerseyException jerseyException) {
return Response.status(jerseyException.getErrorCode()).
entity(jerseyException.getJsonResponseObj()).
type(MediaType.APPLICATION_JSON).
build();
}
}
The code above has unwanted results when you're using an <error-page> component in the web.xml. For example, if my Response.status is set to 400 and my error-page component defines an <error-code> of 400, the web server will redirect the request to the location defined in the web.xml.
This is obviously not what I want for REST requests. I read another post on StackOverflow that said the reason a request gets diverted to the error-page is because HttpServletResponse.sendError(400) is set. That post said if you set HttpServletResponse.setStatus(400) instead, the error-page will be ignored.
If that is true, I don't see how it's helpful since I did not implement the Jersey code. The option I'm seeing is to investigate the Response class source code and possibly re-implement the status method or perhaps other Jersey code. Is there a simple option here or something I'm missing?
Essentially, my question is: Given that I'm using Jersey for REST and I'm using error-page in my web.xml, how can I use the above code while ignoring the error-page for Jersey code only? Any other code that causes HTTP errors should go to the error-page. Or is there another solution that doesn't involve error-page but will work identical to what I want?

I can reproduce the problem, but only if I pass a null as entity content.
Example:
return Response.status(400)
.entity(null)
.type(MediaType.APPLICATION_JSON)
.build();
or even
return Response.status(400)
.type(MediaType.APPLICATION_JSON)
.build();
Both will result in a redirect to the error-page which is set up for HTTP status code 400 because the container will use the sendError() method if no message content is specified.
But if you do it like this:
return Response.status(400)
.entity("An error occured.")
.type(MediaType.APPLICATION_JSON)
.build();
or this:
return Response.status(400)
.entity("")
.type(MediaType.APPLICATION_JSON)
.build();
the container/jersey will only use the setStatus() method and won't redirect to the error-page. As stated in the docs:
This method is used to set the return status code when there is no
error (for example, for the SC_OK or SC_MOVED_TEMPORARILY status
codes).
If this method is used to set an error code, then the container's
error page mechanism will not be triggered. If there is an error and
the caller wishes to invoke an error page defined in the web
application, then sendError(int, java.lang.String) must be used
instead.
So in your case the problem seems to be that jerseyException.getJsonResponseObj() returns null. You should implement a null-check to avoid this.
There is bug JERSEY-1557 for Jersey 1.x which describes the problem, it was closed without a fix but with the advice to use an empty entity string as a workaround.
There is also a similar bug open for Jersey 2.x: JERSEY-2673
See also:
JAX-RS — How to return JSON and HTTP Status code together?
how can i return response status 405 with empty entity?
Bean Validation 400 errors are returning default error page (html) instead of Response entity (json)

Related

Spring HttpClientErrorException provides no details from response body

I'm updating legacy code that uses the exchange method of Spring 3.1 Framework's RestTemplate class. I came across what appears to be a major omission of detail. When the rest client with which I am attempting to communicate returns a 400 status code, a HttpClientErrorException is thrown but there is no response body to provide detail as to why the server rejected the request. There looks like there is no way to retrieve the response body which would provide very helpful information.
I do not need to figure out what is wrong in my calling code as I have already done that. I just would like to know if there is some way I could access and log the body of the HTTP response if an error occurs on the call.
The response body is actually a property on HttpClientErrorException. It can be accessed via the following two accessors which it inherits from its parent class HttpStatusCodeException:
public byte[] getResponseBodyAsByteArray()
public String getResponseBodyAsString()
Cast your HttpClientErrorException e to HttpStatusCodeException:
((org.springframework.web.client.HttpStatusCodeException) e).getResponseBodyAsString()

Spring Boot Returning 405 instead of 404 for POST to unknown URL

Disclaimer: I'm new to Java, Spring, and Spring Boot.
I'd like to have Spring Boot return a 404 when trying to POST to a URL that doesn't exist. However, right now it's returning a 405, with an Allow header that only includes GET and HEAD. Is there a way to customize which HTTP methods are allowed so that I get a 404? I've tried implementing a custom ErrorController, but that doesn't seem to work.
To be clear: this is when I'm POSTing to a URL that shouldn't be matched by any of my defined endpoints, e.g http://example.com/some-bogus-thing
If any more information is needed to diagnose this, I'd be happy to provide it. Given my unfamiliarity with the platform, I'm not sure what's relevant.
HTTP 405 (Method not found) is returned, when URL exists and you try to use an HTTP Method that is not allowed on that particular URL mapping.
if you invoke a POST on below .../test then it will return HTTP 405 and vice versa.
#RequestMapping(value = "/test", method = RequestMethod.GET)
if there is no URL mapping for any of the HTTP methods, then it will return HTTP 404.
To know all the current mappings on that particular boot instance, just to go browser
http://localhost:8080/mappings
Check here:
https://github.com/spring-projects/spring-boot/issues/4876
The solution is to have a setup like this:
spring:
mvc:
static-path-pattern: /static/**
In case anyone still encounter this issue:
This is a bug on spring framework 4.2.4, please refer to https://github.com/spring-projects/spring-framework/issues/18516
And as it said, this issue has been fixed since 4.3 RC1.

How to determine a correct request method is executed in RESTful webservice?

I want to understand how a RESTful web service identifies if a correct request method is called.
For example,
I have a REST service it exposes one operation which is of type GET.
Assume a REST client has invoked the operation using a wrong request method(PUT).
In this scenario, how the service/framework identifies a correct request method is invoked?
I have gone through various posts to understand the scenario but I don't find any information.
Please let me know your comments.
The first line sent in an HTTP request looks like this:
GET /index.html HTTP/1.1
The HTTP request thus contains the HTTP method (POST, PUT, GET, etc.). The framework reads this method, and invokes the Java method that is mapped (thanks to annotations, or XML configuration, or whatever) to the URL (also contained in the HTTP request, as shown above) and the HTTP method. If none is found, then an error response is sent back (405 Method Not Allowed, if the resource is found, but with another method, or 404 if the resource is not found).
It's the http protocol not REST that checks headers, and reports back with an error code.
REST is sort of a strategy, not an implementation.
Hope this helps.

Send redirect from a JAX-RS service

Is it possible to have a JAX-RS web service redirect to another web page?
Like as you would do with Servlet response.sendRedirect("http://test/test.html").
The JAX-RS web service should itself redirect. I'm using RESTEasy if that's relevant.
Yes, you can do this in Jersey or any JAX-RS implementation (including RestEasy) if your return type is a Response (or HttpServletResponse)
https://eclipse-ee4j.github.io/jersey.github.io/apidocs/1.19.1/jersey/javax/ws/rs/core/Response.html
You can use either of the following:
Response.temporaryRedirect(URI)
Response.seeOther(URI)
"Temporary Redirect" returns a 307 status code while "See Other" returns 303.
For those like me looking for 302 that fall on this answer.
By looking the code of
Response.temporaryRedirect(URI)
You can customize your response code like this :
Response.status(int).location(URI).build()
Note that status code are define in enum
Response.Status
And for example 302 is Response.Status.FOUND
Extending smcg# answer above,
You can achieve this by altering the request context in a ContainerRequestFilter by using ContainerRequestContext.setRequestUri(URI). If you see the JAX-RS specification (Section 6.2) here, there is a mention of #PreMatching request filters. According to the documentation;
A ContainerRequestFilter that is annotated with #PreMatching is executed upon
receiving a client request but before a resource method is matched. Thus, this type of filter has the ability
to modify the input to the matching algorithm (see Section 3.7.2) and, consequently, alter its outcome.
A very naive filter can be like this;
#PreMatching
class RedirectFilter: ContainerRequestFilter {
override fun filter(requestContext: ContainerRequestContext?) {
requestContext!!.setRequestUri(URI.create("<redirect_uri>"))
}
}

Error Page Return a Status Code 200 default response

We would like to implement a "fault barrier" strategy for managing exceptions in our applications. One thing our applications have is the concept of a "passback" response, basically a no-op, which we'd like to return in preference to throwing 500, 400, etc. HTTP status codes - e.g. our external facing applications should always return a valid response, even if an underlying exception was thrown - we'd like to handle that internal to the application, and still return a valid noop response.
Our first implementation was a Servlet Filter which would wrap all requests in a try/catch block, and return the default return from the catch, e.g.:
try{
chain.doFilter()
} catch (Throwable t) {
generatePassbackResponse(HttpServletRequest req, HttpServletResponse res)
}
While this mostly works, and feels nice and clean (we can return nice text, set the content/type appropriately, etc.) the one problem seems to be that when an Exception is thrown the response still comes through with Status-Code: 500.
HttpServletResponse.setStatus(200) doesn't have an effect, and the javadoc does say it only applies on normal requests.
Our second implementation thought is we may have to forward to another page, or plug an errorPage into web.xml and manually sendError to that page - though we're interested in whether anyone has a specific recommendation.
There are two methods form setting the HTTP status of a response:
setStatus() will just set the status
sendError() will set the status and trigger the <error-page> mechanism
Javadoc for sendError says the response should be considered to be committed after calling sendError (this could explain the behavior of your appserver).
Implementing a custom HttpServletResponseWrapper would allow you to enforce the behavior you
need for sendError (and maybe buffer the whole request in memory, so that you can send "passbacks" for exceptions occurring after the point the request would be usually committed).
If I remember correctly, you should not be calling chain.doFilter() if you do not want anything else to process the request. The filter will get executed in every case, but chain.doFilter() will ensure that all other filters are called. In order to properly block an exception from getting to the user, you need to stop the request/response handling.
You could take a different route as well by using a framework like Spring and its Interceptors (just like a Filter). Spring gives you a lot of control over the Interceptors and how responses get handled. Granted, this is a bit heavy of a solution to your question.
In response to the comment, according to http://java.sun.com/products/servlet/Filters.html:
The most important method in the
Filter interface is the doFilter
method...This method usually performs
some of the following actions:
If the current filter is the last
filter in the chain that ends with the
target servlet, the next entity is the
resource at the end of the chain;
otherwise, it is the next filter that
was configured in the WAR. It invokes
the next entity by calling the
doFilter method on the chain object
(passing in the request and response
it was called with, or the wrapped
versions it may have created).
Alternatively, it can choose to block
the request by not making the call to
invoke the next entity. In the latter
case, the filter is responsible for
filling out the response.
The idea being that this "fault barrier" needs to stop all other filters from executing, and just handle the request/response in the manner it deems necessary.
Can you not just use the standard web.xml configuration:
<error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
<error-page>
<location>/error.jsp</location>
<exception-type>java.lang.Exception</exception-type>
</error-page>
I cant see what else you're trying to do that this doesn't already cater for? If it's just the error code, then I think that you can set this using the response object.
The RESTEasy framework allows you to select what your response code will be using ExceptionMappers. It may be that you're reluctant to use it, but I've found it to be very quick and efficient.
The JBoss documentation covering ExceptionMappers
http://docs.jboss.org/resteasy/docs/2.0.0.GA/userguide/html_single/index.html#ExceptionMappers
My blog article showing a snippet of general purpose RESTEasy code
http://gary-rowe.com/agilestack/2010/08/22/my-current-development-stack/

Categories

Resources