I have a REST service, where i need to send appropriate status codes with the custom json response, which explains the reason for the error.
I have #Controller class which exposes a service /test and while hitting the service, I am throwing a custom exception (extends Exception class)
I have a #ControllerAdvice class which handles the exception. However, whenever i get error i don't get the json response, instead the stack trace is printed.
Checked the blogs and tried the solutions. However could not get it resolved.
#ControllerAdvice(basePackages = "com.test")
public class ResponseExceptionHandler extends ResponseEntityExceptionHandler{
#ExceptionHandler(value= {MyException.class})
#ResponseStatus(code=HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
ResponseEntity<MyResponse> commonServiceException(MyException ex) {
MyResponse errors = new MyResponse();
errors.setTimeStamp(LocalDateTime.now());
errors.setErrorCode(ex.getErrorCode());
errors.setErrorMessage(ex.getMessage());
errors.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
errors.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
return new ResponseEntity<>(errors, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
SERVICE
#Controller
....
public class test{
..
#GET
#Path("/test")
//#ExceptionHandler(value= {MyException.class})
#Produces(MediaType.APPLICATION_JSON)
public String test() throws MyException{
throw new MyException("123", "message");
}
context.xml
<context:component-scan base-package=" com.test">
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
Expected : JSON Response
Actual : Type Exception Report
Message Service currently unavailable. Error Code : Application Error. Error
Description The server encountered an unexpected condition that prevented it from fulfilling the request.
Exception
com.test.MyException: Service currently unavailable. Error Code : Application Error..............................
This was because I was mixing up Jersey and Spring Framework. After doing necessary spring servlet configurations in web.xml and using #RequestMapping the issue is resolved.
Related
I have an application based on Jax-RS using CXF and Tomcat.
When my application throw a Exception, an ExceptionMapper is call :
#Component
public class MyExceptionHandler implements ExceptionMapper<MyException> {
#Override
public Response toResponse(MyException exception) {
return Response
.status(500)
.entity(new ExceptionBodyResponse(exception))
.build();
}
}
Then I except a response with a custom XML message. But what I get is the Tomcat HTML error page (containing the wanted message inside).
I try to figure out how to receive the response without the Tomcat error page, only the XML.
I found another thread about this problematic, but for Jersey, nothing for CXF : Dropwizard Response.status(Response.Status.NOT_FOUND).build() returns html
Introduction
I have a custom ErrorController implementation to handle all exceptions and create a custom error message:
#RestController
public class CustomErrorController implements ErrorController {
#RequestMapping("/error")
public ResponseEntity handleError(HttpServletRequest request) {
HttpStatus status = HttpStatus.valueOf((Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE));
String body = ... // Code to calculate the body based on the request
return ResponseEntity.status(status.value()).body(body);
}
#Override
public String getErrorPath() {
return "/error";
}
}
Problem Description
However, this also enables access to the path /error which I would like to disable.
When trying to access https://localhost:8443/error, a NullPointerException is thrown by the HttpStatus.valueOf() method, because the status code could not be extracted. As a result, an Internal Server Error (500) is created, which is run through my custom controller, creating a custom 500 error response.
Temporary Fix
As a workaround, I can check if the status code attribute exists, and handle that case separately. But it is a work-around and not an actual fix.
The Question
What I would like is to disable the /error mapping from external access. If attempted, the result should be Not Found (404) which is then run through my custom controller.
Is the #RequestMapping("/error") necessary or could this be implemented differently?
Edits
Spring Boot version is 2.1.2.RELEASE
The server.error.whitelabel.enabled property is set to false. The issue does not seem to be related with it.
I have a controller in my project that handles all exceptions defined like this:
#ControllerAdvice
public class GlobalExceptionHandlingController {
#ResponseBody
#ExceptionHandler(value = AccessDeniedException.class)
public ResponseEntity accessDeniedException() {
Logger.getLogger("#").log(Level.SEVERE, "Exception caught!");
return new ResponseEntity("Access is denied", HttpStatus.FORBIDDEN);
}
}
I'm focusing on one specific exception here and that is AccessDeniedException that is thrown by Spring Security on unauthorized requests. This is working properly for "normal" aka non-ajax requests. I can click on a link or enter URL directly in the location bar and I will see this message if request is unauthorized.
However on AJAX request (using Angular for it) I'm getting standard 403 error page as a response but what's interesting is that I can see that AccessDeniedException is caught by this controller!
I did some research and it seems that I need to have custom AccessDeniedHandler so I made this:
Added this lines in my Spring Security configuration:
.and()
.exceptionHandling().accessDeniedPage("/error/403/");
and I made special controller just to handle this:
#Controller
public class AjaxErrorController {
#ResponseBody
#RequestMapping(value = "/error/403/", method = RequestMethod.GET)
public ResponseEntity accessDeniedException() {
return new ResponseEntity("Access is denied (AJAX)", HttpStatus.FORBIDDEN);
}
}
Now this is working fine but the exception is still caught in the first controller but return value of that method is getting ignored. Why?
Is this how it's supposed to be done? I have a feeling that I am missing something here.
I'm using Spring 4.2.5 with Spring Security 4.0.4.
Although I don't know all the details, my theory is that it can be a content type issue.
Often when doing AJAX requests, the response is expected to be in JSON, so the browser will add an Accept: application/json header to the request.
Your response entity on the other hand:
new ResponseEntity("Access is denied", HttpStatus.FORBIDDEN)
is a text response, the default Content-Type of this with a typical Spring setup is text/plain.
When Spring detects that it can't deliver a response with type the client wants, it fallbacks to the default error page.
I have a spring boot application.
I have a custom error controller, that is mapped to using ErrorPage mappings. The mappings are largely based on HTTP Status codes, and normally just render a HTML view appropriately.
For example, my mapping:
#Configuration
class ErrorConfiguration implements EmbeddedServletContainerCustomizer {
#Override public void customize( ConfigurableEmbeddedServletContainer container ) {
container.addErrorPages( new ErrorPage( HttpStatus.NOT_FOUND, "/error/404.html" ) )
}
And my error controller:
#Controller
#RequestMapping
public class ErrorController {
#RequestMapping( value = "/error/404.html" )
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public String pageNotFound( HttpServletRequest request ) {
"errors/404"
}
This works fine - If I just enter a random non-existent URL then it renders the 404 page.
Now, I want a section of my site, lets say /api/.. that is dedicated to my JSON api to serve the errors as JSON, so if I enter a random non-existent URL under /api/.. then it returns 404 JSON response.
Is there any standard/best way to do this? One idea I tried out was to have a #ControllerAdvice that specifically caught a class of custom API exceptions I had defined and returned JSON, and in my standard ErrorController checking the URL and throwing an apprpriate API exception if under that API URL space (but that didn't work, as the ExceptionHandler method could not be invoked because it was a different return type from the original controller method).
Is this something that has been solved?
The problem was my own fault. I was trying to work out why my #ExceptionHandler was not able to catch my exception and return JSON - As I suggested at the end of my question, I thought I was having problems because of conflicting return types - this was incorrect.
The error I was getting trying to have my exception handler return JSON was along the lines of:
"exception": "org.springframework.web.HttpMediaTypeNotAcceptableException",
"message": "Could not find acceptable representation"
I did some more digging/experimenting to try to narrow down the problem (thinking that the issue was because I was in the Spring error handling flow and in an ErrorController that was causing the problem), however the problem was just because of the content negotiation stuff Spring does.
Because my errorPage mapping in the web.xml was mapping to /error/404.html, Spring was using the suffix to resolve the appropriate view - so it then failed when I tried to return json.
I have been able to resolve the issue by changing my web.xml to /error/404 or by turning off the content negotiation suffix option.
Now, I want a section of my site, lets say /api/.. that is dedicated
to my JSON api to serve the errors as JSON, so if I enter a random
non-existent URL under /api/.. then it returns 404 JSON response.
Is there any standard/best way to do this? One idea I tried out was to
have a #ControllerAdvice that specifically caught a class of custom
API exceptions I had defined and returned JSON, and in my standard
ErrorController checking the URL and throwing an apprpriate API
exception if under that API URL space (but that didn't work, as the
ExceptionHandler method could not be invoked because it was a
different return type from the original controller method).
I think you need to rethink what you are trying to do here. According to HTTP response codes here
The 404 or Not Found error message is an HTTP standard response code
indicating that the client was able to communicate with a given
server, but the server could not find what was requested.
So when typing a random URL you may not want to throw 404 all the time. If you are trying to handle a bad request you can do something like this
#ControllerAdvice
public class GlobalExceptionHandlerController {
#ExceptionHandler(NoHandlerFoundException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
public ResponseEntity<ErrorResponse> noRequestHandlerFoundExceptionHandler(NoHandlerFoundException e) {
log.debug("noRequestHandlerFound: stacktrace={}", ExceptionUtils.getStackTrace(e));
String errorCode = "400 - Bad Request";
String errorMsg = "Requested URL doesn't exist";
return new ResponseEntity<>(new ErrorResponse(errorCode, errorMsg), HttpStatus.BAD_REQUEST);
}
}
Construct ResponseEntity that suites your need.
I'm trying to handle errors coming from my backend. The handleMessage() is called if an error occurs but the content is an instance of XmlMessage. I would like to change it to my own response - just set the response code and add some message.
I haven't found any proper documentation which could tell me how to do this...
These axamples are for REST but I'd like to manage this thing in SOAP too.
interceptor
public class ErrorHandlerInterceptor extends AbstractPhaseInterceptor<Message> {
public ErrorHandlerInterceptor() {
super(Phase.POST_LOGICAL);
}
#Override
public void handleMessage(Message message) throws Fault {
Response response = Response
.status(Response.Status.BAD_REQUEST)
.entity("HOW TO GET A MESSAGE FROM AN EXCEPTION IN HERE???")
.build();
message.getExchange().put(Response.class, response);
}
}
context.xml
<bean id="errorHandlerInterceptor"
class="cz.cvut.fit.wst.server.interceptor.ErrorHandlerInterceptor" />
<jaxrs:server address="/rest/">
<jaxrs:serviceBeans>
<ref bean="restService" />
</jaxrs:serviceBeans>
<jaxrs:outFaultInterceptors>
<ref bean="errorHandlerInterceptor" />
</jaxrs:outFaultInterceptors>
</jaxrs:server>
If you're using JAX-RS, why not setup an exception mapper, and then use that mapper to handle the response.
A simple example:
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class MyExceptionMapper implements
ExceptionMapper<MyException> {
#Override
public Response toResponse(MyException e) {
return Response.status(Status.NOT_FOUND).build();
}
}
Then you would need to register the provider in the jaxrs serve by adding:
<jaxrs:providers>
<bean class="com.blah.blah.blah.blah.MyExceptionMapper"/>
</jaxrs:providers>
in the server config in the context. With that you have full access to the exception, and can get whatever you want from it.
And here's the other piece of your puzzle. You're already using JAX-RS, so why not use JAX-WS as well?
This thread and this blog post cover mapping Exceptions into SOAP faults. Short and sweet:
The JAX-WS 2.0 specification demands that the exception annotated with #WebFault must have two constructors and one method [getter to obtain the fault information]:
WrapperException(String message, FaultBean faultInfo)
WrapperException(String message, FaultBean faultInfo, Throwable cause)
FaultBean getFaultInfo()
The WrapperException is replaced by the name of the exception, and FaultBean is replaced by the class name that implements the fault bean. The fault bean is a Java bean that contains the information of the fault and is used by the Web service client to know the cause for the fault.
And there's your mapping. Simply specify implementations of the above signatures in the context of #WebFault and your SOAP API should map these happily. Obviously, the links contain more details.