I playing with my simple REST API prototype written in Spring 3.2 MVC / Tomcat 7.4 / PostgreSQL.
Now I am thinking what is the best way to solve these issues:
Somebody make a request to a resources that doesn't exist
Somebody make a request to a resource but is using unsupported HTTP method
Somebody make a request to a resource but provide incorrect or incomplete data
Issue 3 I can probably solve with checking data and response proper HTTP headers (HTTP 400 or HTTP 404 because data could not be found because some input data is missing), but I don't know how to solve (in Spring) issue 1 and 2.
What is the best practice for handling exceptions such these?
For issue 1 just return a 404 error.
Create a 404 exception:
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
}
Now throw that from any handler and the client will get a 404 error.
For issue 2 the framework will automatically generate and return an error page for you if you set up your filters correctly, or if not generate and send a 405 not allowed code.
For issue 3 return a 400 Bad Syntax error in the same way as for issue 1 - or generate a more complex error return detailing the problem.
Return 404 (Not found)
Return 405 (Method not allowed)
Return 400 (Bad Request)
A word of advice, the body/headers of 404 that you return when someone request a URL path that doesn't exist should be different than if a resource doesn't exist. This will allow your clients to figure out if they are calling the wrong url or if the resource doesn't exist.
For the case #1, it's a clear 404 Not Found.
For the case #2, it's 405 Method Not Allowed.
For the case #3, it's a 400 Bad Request.
See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html for the description of these cases and the reasoning behind it.
Related
I'm trying to fix 403 forbidden error in case the user refresh the page, and in order to do it I added a redirection to index.html for all errors:
#RequestMapping(value="/error)
public ModeelAndView redirect() {
return new ModelAndView("forward:/");
}
The issue is.. all spring boot errors are overridden by this behavior - for example, if the user will do a POST to /API/someApi with the wrong parameters -he will get error 405 (Method not allowed) like it tried to do POTS on index.html.
How I can implement my redirection logic only in the UI path and keep the error handling as it was for all requests prefixed with /API/**?
Using the Spring way of doing things would be easier and better. For handling your errors, create a #ControllerAdvice class and use #ExceptionHandler to catch the specific errors you want to handle.
We had an error recently where were were trying to disassociate two entities in a Spring Boot application with a DELETE, but we were hitting the wrong endpoint e.g. /api/school/student rather than /api/student/school/. As Student is the owning entity here, hitting the first endpoint did nothing, but still returned a 204 status code, which tricked us into thinking it was ok. Is there a way we could make it return a 4xx code instead?
You can create a controller method for that endpoint which simply returns 4xx.
Or you can modify your schema in a way that a many-to-many relationship could be managed from both side.
It seems that have a controller which takes all unresolved url and returns a result with no content. Something in your code like:
#GetMapping("/**") or #GetMapping("/api/**")
remove it and you will get the 404 error message.
Suppose I have REST endpoint which gives me properties of given media ID using GET. If user calls the endpoint with wrong HTTP verb, like POST or DELETE, I want to give descriptive error message saying "Method is not supported". I am using JAX-RS for REST mapping. JAX-RS just throws 404 with no descriptive error message.
What is the proper way of handling this scenario? Are there more descriptive ways of returning an error?
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.
#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)