I am currently developing a webservice which will always return json as a response to any request(all good request are working already). I would like to return a json when the http status of the request is a bad request(status 400 to be exact). I used the #ControllerAdvice of spring mvc to do the job:
#ControllerAdvice
public class RestErrorHandler {
#ExceptionHandler(Exception.class)
#ResponseStatus(value=HttpStatus.BAD_REQUEST, reason="Something went wrong. Please check your JSON REQUEST!")
public #ResponseBody ErrorClass processValidationError() {
// some stuff in setting the error response
return new ErrorClass();
}
But what happen is that it returns the default Tomcat 7 error message for bad request and not the json I set. processValidationError() will return an ErrorClass instance which will automatically be in json format using jackson plugin. Did I missed something?
Related
I've been tasked to create a web service that fetches a file from an Azure storage account.
On sucess: return the file as the payload
On error: return an xml response.
The xml response will contain a copy of the request, error codes and messages etc as the user will need a helpful error message explaining what happened.
I can find instructions for how to return a single object/media type, but not multiple types dependent on condition.
Is this possible?
Solved with an exception handler thanks to Abhijeet's comment
- important to add the contentType or it returns JSON by default
#ControllerAdvice
public class MyExceptionHandler {
#ResponseStatus(HttpStatus.I_AM_A_TEAPOT)
#ExceptionHandler(MyException.class)
#ResponseBody
ResponseEntity<?> exceptionHandler(MyException e){
InvoiceArchiveResponse responseObject = e.getResponseObject();
return ResponseEntity.badRequest()
.contentType(MediaType.parseMediaType("application/xml"))
.body(responseObject);
}
}
Thanks!
I have a webservice which calls another WS and returns the response from the second WS. It looks like so:
// MyController
public ResponseEntity<Foo> requestFooController(#RequestBody #Valid Bar request) {
return this.myService.requestFooService(request);
}
//MyService
ResponseEntity<Foo> requestFooService(Bar request) {
Buzz improvedRequest = ...
return this.secondWS.secondRequestFoo(improvedRequest);
}
When I call the API through Postman, I receive a HTTP OK response with an empty body. Yet, when I'm in debug mode I can see that the service is returning a ResponseEntity with a body. The headers are not lost though.
I changed my code like so and it works fine:
// MyController
public ResponseEntity<Foo> requestFooController(#RequestBody #Valid Bar request) {
ResponseEntity<Foo> tmp = this.myService.requestFooService(request);
return ResponseEntity.status(tmp.getStatusCode()).body(tmp.getBody());
}
Now through Postman I do have the expected body. However, I don't understand the behaviour. I thought that maybe it's due to the fact that the body is some kind of stream that can be read once or something similar. But from reading the source code I don't see anything that could explain this behaviour.
I'm using the Netflix-stack (so HTTP calls between the two WS are made through a Feign client).
Any idea why I'm getting this result?
EDIT:
More details on my stask:
SpringBoot 1.5.3.RELEASE
Feign 2.0.5
There is a bug that causes the named body of an HTTP MultiPart POST to fail. The symptom of this is that you make a POST request with a body, and Spring-Boot can't match it up to an endoint. The exception I see is:
2019-01-23 15:22:45.046 DEBUG 1639 --- [io-8080-exec-10] .w.s.m.m.a.ServletInvocableHandlerMethod : Failed to resolve argument 3 of type 'org.springframework.web.multipart.MultipartFile'
org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present
Zuul is doing caching of the request in order to re-try multiple times. In this process, it fails to preserve the named field for the binary body. You may find it working if you preface the request with zuul. So instead of http://myserver.com/myservice/endpoint use zuul in the path: http://myserver.com/zuul/myservice/endpoint
That will effectively avoid the saving of the request and the retry mechanism.
More details are available on this issue in Zuul's GitHub Bug List.
I want to return a custom JSON as response body in case of errors. I tried doing this for 404 error code. I created a controller which looks like this.
#RequestMapping("/**")
#Controller
public class IOTExceptionController {
public void handleInvalidURL() throws ResourceNotFoundException {
throw new ResourceNotFoundException("Requested resource does not exist."
+ " Please check the URL.");
}
}
And the ControllerAdvice class I've setup will return the response as JSON.
So whatever URL which will not be found in other controllers will be handled here and 404 error is returned.
Unfortunately, the Tomcat error page for 404 is all I'm getting. Can anyone please let me know whether there is any flaw in this logic/code?
I am currently setting up a Spring MVC application (version 4.1.4.RELEASE) and I want the application to return a JSON string on a 404 error rather than the default html response. I am using Tomcat 8 as my server. I have what I think should be correct, however it isn't behaving in the manner that I expect. What I'm trying to do is based off of this answer.
public class SpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
...
#Override
protected void customizeRegistration(ServletRegistration.Dynamic registration){
registration.setInitParameter("throwExceptionIfNoHandlerFound","true");
}
}
and then I have an exception controller (which is different than the question I based my solution off of, however I don't believe that is an issue as I am under the impression that #ControllerAdvice is an acceptable way to manage this based off of the Spring Docs. It looks something like:
#ControllerAdvice
public class GlobalExceptionController{
#ResponseStatus(value=HttpStatus.NOT_FOUND)
#ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Message handleMethodNotSupported(HttpServletRequest request){
...
}
#ResponseStatus(value=HttpStatus.NOT_FOUND)
#ExceptionHandler(NoSuchRequestHandlingMethodException.class)
public Message handleBadRequest(HttpServletRequest request){
...
}
#ResponseStatus(value=HttpStatus.NOT_FOUND)
#ExceptionHandler(NoHandlerFoundException.class)
public Message requestHandlingNoHandlerFound(HttpServletRequest request){
...
}
...
}
It continues to send back the default response. I know for a fact that it is hitting my customizeRegistration() function because breakpoints stop it, however, any breakpoints that I have in my GlobalException class are not hit. Also, the GlobalException class is within a package that is hit by a #ComponentScan() annotation, so I am fairly confident that it is also being handled by spring.
I assume I'm missing something obvious, any help would be greatly appreciated.
I don't think the return type you're trying to use is supported. Have you tried changing your return value to ResponseEntity or adding a #ResponseBody annotation?
From the docs:
A ModelAndView object (Servlet MVC or Portlet MVC).
A Model object, with the view name implicitly determined through a RequestToViewNameTranslator.
A Map object for exposing a model, with the view name implicitly determined through a RequestToViewNameTranslator.
A View object.
A String value which is interpreted as view name.
#ResponseBody annotated methods (Servlet-only) to set the response content. The return value will be converted to the response stream
using message converters.
An HttpEntity or ResponseEntity object (Servlet-only) to set response headers and content. The ResponseEntity body will be
converted and written to the response stream using message converters.
void if the method handles the response itself (by writing the response content directly, declaring an argument of type
ServletResponse / HttpServletResponse / RenderResponse for that
purpose) or if the view name is supposed to be implicitly determined
through a RequestToViewNameTranslator (not declaring a response
argument in the handler method signature; only applicable in a Servlet
environment).
With my very simple JAX-RS service I'm using Tomcat with JDBC realm for authentication, therefore I'm working the the JSR 250 annotations.
The thing is that I want to return a custom message body in the HTTP status response. The status code (403) should stay the same. For example, my service looks like the following:
#RolesAllowed({ "ADMIN" })
#Path("/users")
public class UsersService {
#GET
#Produces(MediaType.TEXT_PLAIN)
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public String getUsers() {
// get users ...
return ...;
}
}
If a user with a different role than "ADMIN" access the service, I want to change the response message to something like that (depending on the media type [xml/json]):
<error id="100">
<message>Not allowed.</message>
</error>
At the moment Jersey returns the following body:
HTTP Status 403 - Forbidden
type Status report
message Forbidden
description Access to the specified resource (Forbidden) has been forbidden.
Apache Tomcat/7.0.12
How can I change the default message body? Is there a way to handle the (maybe thrown) exception to build my own HTTP status response?
The easiest way to handle this sort of thing is to throw an exception and to register an exception mapper to convert into the kind of message you want to send in that case. So, suppose you throw an AccessDeniedException, you would then have a handler like this (with full class names in places for clarity):
#javax.ws.rs.ext.Provider
public class AccessDeniedHandler
implements javax.ws.rs.ext.ExceptionMapper<AccessDeniedException> {
public javax.ws.rs.core.Response toResponse(AccessDeniedException exn) {
// Construct+return the response here...
return Response.status(403).type("text/plain")
.entity("get lost, loser!").build();
}
}
The way in which you register the exception mapper varies according to the framework you're using, but for Jersey you should be fine with just using #Provider. I'll let you figure out for yourself how you want to generate the kind of error documents that you want, but I do recommend handling failures as HTTP error codes of some kind (that's more RESTful...)
With creating an ExceptionMapper (mapping exceptions of WebApplicationException) it is possible to "catch" certain exceptions thrown by the application:
#Provider
public class MyExceptionMapper implements ExceptionMapper<WebApplicationException> {
#Override
public Response toResponse(WebApplicationException weException) {
// get initial response
Response response = weException.getResponse();
// create custom error
MyError error = ...;
// return the custom error
return Response.status(response.getStatus()).entity(error).build();
}
}
You also need to add the package to your application web.xml for registering the provider:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>
com.myapp.userservice; // semi-colon seperated
com.myapp.mappedexception
</param-value>
</init-param>
REST is build upon HTTP so you don't have to change the default behavior of an authentication failure. Having a 403 error when accessing a resource is enough for the client to clearly understand what appends.
The more your resources are HTTP compliant, the more others can understand it.