Controller Advice not handling FileTooLargeException by exceuting method annotated with FileTooLargeException - spring-boot

My rest controller contains following post mapping :
#PostMapping
public ResponseEntity<RespDTO> uploadDocument(#ModelAttribute #Valid RequestDTO requestDTO,#RequestParam(value = "fileContent") MultipartFile fileContent) throws ServiceException, URISyntaxException { }
ServiceExceptionn is a custom exception specific to my application.
The controller advice looks like below:
#ControllerAdvice
public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait {
#Override
public ResponseEntity process(#Nullable ResponseEntity<Problem> entity, NativeWebRequest request) {
}
#ExceptionHandler(FileTooLargeException.class)
public ResponseEntity<ResponseDTO> handleFileTooLargeException(FileTooLargeException ex, #Nonnull NativeWebRequest request){
}
}
application.yml contains below property :
spring:
servlet:
multipart:
max-file-size: 2MB
If I call the rest api using file having size greater than 2MB, then I am getting below exception:
io.undertow.server.handlers.form.MultiPartParserDefinition$FileTooLargeException: UT000054: The maximum size 5242880 for an individual file in a multipart request was exceeded
Issue that I am facing here is:
the controller advice is not working as per expectation.
handleFileTooLargeException - this method must get executed because it is annotated with ExceptionHandler mentioned with the specific exception type.
But instead of that, the control goes into process method of the controller advice.
Not able to understand what I am missing here.

You need ExceptionTranslator class to extend ResponseEntityExceptionHandler class to enable methods with #ExceptionHandler annotation

Related

Http status code from a global #ExceptionHandler with the #ControllerAdvice annotation

I'm implementing a global exception handler inside a Spring Boot App, with the #ControllerAdvice annotation, and I'd like to know, how could I get the http status code for showing a different message when it's 404 and to persist a log with the error, in other cases.
This is a simplified version of the code:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(RuntimeException.class)
public ModelAndView handleException(Exception ex, HttpServletRequest request, HttpServletResponse response) {
...
ModelAndView model = new ModelAndView();
model.addObject("message", ex.getMessage());
model.addObject("trace", trace);
model.addObject("path", path);
//model.addObject("status", response.getStatus());
model.setViewName("error");
return model;
}
I've tried this approach, without success:
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
Integer statusCode = Integer.valueOf(status.toString());
To get the request attribute, this other name; javax.servlet.error.status_code doesn't work either.
You have to set your own status code corresponding every exception that you are handling. If any exception missed, default will be 5.x.x server error.
I remember doing this by extracting the expected exception to a separate class that extends Exception.
By doing this, you can add #ResponseStatus to set your required status code.
This custom exception can be thrown in your controller needed.
#ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Person Not Found")
public class PersonNotFoundException extends Exception {
public PersonNotFoundException (int id){
super("PersonNotFoundException with id="+id);
}
}
Instead of specifying the generic RunTime exception, handle the PersonNotFoundException in your #ExceptionHandler and add the exception object to your ModelAndView.

#ConfigurationProperties object returned null in spring boot application

I have a config object which is mapped to the config file as below:
#Data
#Component
#ConfigurationProperties("app.cors")
public class DomainProperties {
private Map<String, String> domainMap;
}
and my application.properties config file looks like:
app.cors.domainMap.local=localhost:8080
app.cors.domainMap.onlive=test.com
I am trying to read the values in the domainMap from the above properties file, to set them as Access-Control-Allow-Origin headers for the response of my application. What I did so far is:
public class HeaderInterceptor extends HandlerInterceptorAdapter {
#Autowired
private DomainProperties domainProperties;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
List<String> domainList= domainProperties.getDomainMap().values().stream().collect(Collectors.toList());
domainList.stream().forEach(domain -> response.addHeader("Access-Control-Allow-Origin", domain));
return super.preHandle(request, response, handler);
}
but the problem is I received back a null domainProperties object, therefore a NullPointerException is thrown here.
Can anyone explain me why did I get a null domainProperties object here? and how to resolve this problem. Thank you in advanced!

Stacktrace of exceptions in Spring Rest responses

I have few Rest web services implemented through Spring. The problem is that if any exception is thrown the webservice returns json object with formatted error message that contains stacktrace. Can I have a single point of handling exceptions, and return my custom json objects with messages that wouldn't contain stacktrace?
I see descriptions for spring mvc but im not really using that for building my views etc.
I know it's too late, but just pointing out some solutions that may help others!
case 1: if you're using application.properties file, add following line to your properties file.
server.error.include-stacktrace=on_trace_param
case 2: if you're using application.yml file, add following line to your yml file.
server:
error:
include-stacktrace: on_trace_param
case 3: In case, none of them works, try following changes:
Try to suppress the stack trace by overriding fillInStackTrace method in your exception class as below.
public class DuplicateFoundException extends RuntimeException {
#Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
ps1: I referred this article.
Spring provides an out of the box solution to handle all your custom exceptions from a single point. What you need is #ControllerAdvice annotation in your exception controller:
#ControllerAdvice
public class GlobalDefaultExceptionHandler {
#ExceptionHandler(Exception.class)
public String exception(Exception e) {
return "error";
}
}
If you want to go deep into Springs #ExceptionHandler at individual controller level or #ControllerAdvice at global application level here is a good blog.
To handle exceptions thrown from a spring application at a single point, this is the best way to do it. #ControllerAdvice will create an aspect join-point which will intercept all the exceptions with required matching types bound to the corresponding public method.Here, public ResponseEntity handleDataIntegrityViolationException(DataIntegrityViolationException dataIntegrityViolationException,
WebRequest request) is handling DataIntegrityViolationException thrown out of the system at one place.
#ControllerAdvice
public class GlobalControllerExceptionHandler {
private Logger logger = Logger.getLogger(this.getClass());
private HttpHeaders header = new HttpHeaders();
#Autowired
private MessageSource messageSource;
public GlobalControllerExceptionHandler() {
header.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
}
/**
* #param dataIntegrityViolationException
* #param request
* #return
*/
#ExceptionHandler({ DataIntegrityViolationException.class })
public ResponseEntity<?> handleDataIntegrityViolationException(DataIntegrityViolationException dataIntegrityViolationException,
WebRequest request) {
String message = ExceptionUtils.getMessage(dataIntegrityViolationException);
logger.error("*********BEGIN**************DataIntegrityViolationException******************BEGIN*******************\n");
logger.error(message, dataIntegrityViolationException.fillInStackTrace());
logger.error("*********ENDS**************DataIntegrityViolationException*******************ENDS*****************************\n");
return ResponseEntity.status(HttpStatus.CONFLICT).headers(header).body(dataIntegrityViolationException);
}
}

How to check security acess (#Secured or #PreAuthorize) before validation (#Valid) in my Controller?

here is my Controller code :
#PreAuthorize("hasRole('CREATE_USER')")
#RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public UserReturnRO createUser(#Valid #RequestBody UserRO userRO) throws BadParameterException{
return userService.createUser(userRO);
}
My need is when a client without the appropriate role tries to create a user, the controller responds "Not authorized" even if the data sent are not valid. Instead of that, if the client (without the appropriate role) tries to create a user with wrong data, my controller responds with the #Valid message (ex : "password cannot be empty"), while I want it responds "not authorized".
In the PreAuthorized Interface we can find this sentence :
Annotation for specifying a method access-control expression which will be evaluated to decide whether a method invocation is allowed or not.
but it seems that it's not the case.
You can not do this directly, since #Valid is processed before an actual method call and as a result before #PreAuthorize.
But what you can do instead is to inject BindingResult just right after your model (userRO) and in doing so - take control of validation process. Then check if BindingResult has some errors and if so return bad request response (similar to what spring does).
Example:
#ResponseBody
#RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
#PreAuthorize("hasRole('CREATE_USER')")
public ResponseEntity<?> createUser(#RequestBody #Valid UserRO userRO, BindingResult result) {
if (result.hasErrors()) {
return ResponseEntity.badRequest().body(result.getAllErrors());
}
return ResponseEntity.ok(userService.createUser(userRO));
}
As already stated, Spring Security's #PreAuthorize is method advice, which means that it does not get to participate until the method and its arguments have already been resolved.
Aside from the answer already given, there are a few ways to move authorization before argument resolution, instead.
Filter Security
First, Spring Security checks URLs before the request is mapped to a method. And since this is a #Controller, it's reasonable to suppose that you could instead map the request to the role at that level instead of #PreAuthorize:
http
.authorizeRequests()
.mvcMatchers(POST, "/somepath").hasRole("CREATE_USER")
Handler Interceptor
Second, Spring MVC does ship with limited support for checking authorities before parsing method arguments. For example, you can do:
#EnableWebMvc
public static class MvcConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
UserRoleAuthorizationInterceptor userRole =
new UserRoleAuthorizationInterceptor();
userRole.setAuthorizedRoles("CREATE_USER");
registry.addInterceptor(userRole);
}
}
This is much more basic than #PreAuthorize since it's a global setting, but I've included it for completeness.
Handler Interceptor, Part 2
Third (warning, some inelegance ahead), you can create your own HandlerInterceptor.
The flow is:
FilterSecurityInterceptor <== where .mvcMatchers(...).hasRole(...) lives
Then HandlerInterceptors
Then argument validation
Then MethodSecurityInterceptor <== where #PreAuthorize lives
So, your HandlerInterceptor would check before arguments are resolved. It doesn't have to be as involved as MethodSecurityInterceptor, though. It could, for example, simply be:
static class AuthorizationInterceptor extends HandlerInterceptorAdapter {
SecurityMetadataSource securityMetadataSource;
AccessDecisionManager accessDecisionManager;
#Override
public void preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) {
Authentication authenticated = (Authentication) request.getUserPrincipal();
MethodInvocation mi = convert(handler);
Collection<ConfigAttribute> attributes =
this.securityMetadataSource.getAttributes(mi);
// throws AccessDeniedException
this.accessDecisionManager.decide(authenticated, mi, attributes);
return true;
}
}
Then you wire it together with:
#EnableGlobalMethodSecurity(prePostEnabled = true)
static class MethodConfig extends GlobalMethodSecurityConfiguration {
#Bean
HandlerInterceptor preAuthorize() throws Exception {
return new AuthorizationInterceptor(
accessDecisionManager(), methodSecurityMetadataSource());
}
}
#EnableWebMvc
public static class MvcConfig implements WebMvcConfigurer {
#Autowired
AuthorizationInterceptor authorizationInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authorizationInterceptor);
}
}
It's inelegant because MethodSecurityInterceptor would still participate in authorized requests, which would ostensibly be the majority.

Spring 4 RestController - How to return jaxb object with ResponseEntity

I am using Spring #RESTController for my REST webservice. instead of returning the object of ModelAndView I am trying to return the object of ResponseEntity object in my rest method. for the Strgin type of response it is working ut when I am building ResponseEntity with a Jaxbobject it is giving me HTTP error 406
#RestController
#RequestMapping(value="/service")
public class MyController {
public #ResponseBody ResponseEntity<String> getDashBoardData() throws JAXBException {
// Some Operation
return new ResponseEntity<String>(myStringXML, responseHeaders, HttpStatus.OK);
}
}
Below is not working
#RestController
#RequestMapping(value="/service")
public class MyController {
public #ResponseBody ResponseEntity<MyJaxbClass> getDashBoardData() throws JAXBException {
// Some Operation
return new ResponseEntity<MyJaxbClass>(MyJaxbClassObject, HttpStatus.OK);
}
}
The #RestController annotation already implies the #ResponseBody annotation for all request handling methods, that is one of its purposes (it saves you from putting all those annotations there). So you can/should remove it.
Processing the return value of the method is done by a 'HandlerMethodReturnValueHandlerand the specific one which should handle this delegates to aHttpMessageConverter. It selects a specificHttpMessageConverterbased on the requested/supported response types for the current request and the support response types from theHandlerMethodReturnValueHandler`.
In general when using #EnableWebMvc or <mvc:annotation-driven /> everything should be setup automatically. The automatic setup does some detection on which libs are available (jaxb, json etc).
Based on the response code (406) you either have manually configured something wrong on the server side or the client doesn't support xml as a response type.

Resources