I tried to add model attribute in controller which is causing Validator to throw exception.
java.lang.IllegalStateException: Invalid target for Validator
#ModelAttribute("summary")
public Summary createStudent(Student student) {
return saveService.saveStudent(student);
}
If #ModelAttribute("summary") is removed it works perfectly but not able to access the data in view.
If #ModelAttribute("summary") is enabled it throws above exception.
Validator
public class StudentValidator implements Validator {
public StudentValidator() {
}
public boolean supports(Class<?> paramClass) {
return Student.class.equals(paramClass);
}
public void validate(Object object, Errors errors) {
Student sutdent = (Student) object;
// Validation here for student
}
}
Already Refered Invalid target for Validator in spring error?
What is wrong?
I don't see any problem with my validator.
Thanks.
Related
I do have a Spring boot controller class and a corresponding ControllerAdvice class which has ExceptionHandlers to handle different exception.
My controller method calls a simple validation helper class to validate input fields which throws an exception if validation fails. Now if I don't put a try catch block in my controller it keeps complaining me that you have a method which has untangled exception even through the logic for handling validation exception is defined in controlleradvice class. Please suggest how do I solve it.
From the method of ValidationHelper class if you throw any Checked Exception then you need to use a try-catch block to call that method.
If you don't want then it's better to use any Custom Exception class which will extend the RuntimeException class and you throw that exception. Then you don't need to explicitly mention the throws as well as you don't need to have a try-catch block at the controller.
#RestController
#RequiredArgsConstructor
public class SampleController {
private final ValidationHelper validationHelper;
#ResponseStatus(HttpStatus.OK)
#GetMapping("/sample")
public String getRequest(#RequestParam String name) {
validationHelper.validate(name);
return "";
}
}
#Service
public class ValidationHelper {
public Boolean validate(String name) {
throw new CustomException("Validation Failed");
}
}
public class CustomException extends RuntimeException {
public CustomException(String message) {
super(message);
}
}
In my spring boot application, I have created a custom exception handler using #ControllerAdvice, and a custom exception ServerException, when I throw the custom exception, it does not get caught by my customExcpetionHandler, though I am able to check whether actually the excpetion is thrown and it is getting thrown as shown by logs.
Below is the code for my ServerException:
public class ServerException extends Exception {
/**
*
*/
private static final long serialVersionUID = <uid>;
public ServerException(String message) {
super(message);
}
}
Below is my GlobalCustomExceptionHandler class:
#ControllerAdvice
#EnableWebMvc
public class GlobalCustomExceptionHandler extends ResponseEntityExceptionHandler{
#ExceptionHandler(ServerException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public ModelMap handleServerException(ServerException ex) {
ModelMap modelMap = new ModelMap();
modelMap.addAttribute("status", "ERROR_400_Bad_Request");
modelMap.addAttribute("error_message", ex.getMessage());
return modelMap;
}
}
I am throwing the exception in one of the restcontroller as follows:
throw new ServerException("invalid server configs");
But I can only see the exception getting printed in log file, and not getting it as response mentioned in handleServerException() method of GlobalCustomExceptionHandler class.
What could be the reason ?
I have just reproduced Your copy-pasted piece of code with simple REST endpoint, and it works as expected:
#RestController
public class SystemController {
#GetMapping(value = "/system")
public ResponseEntity<Object> getSystem() throws ServerException {
if (true)
throw new ServerException("Checking this out");
return new ResponseEntity<>(HttpStatus.OK);
}
}
Calling http://localhost:8080/system
Results with:
{"status":"ERROR_400_Bad_Request","error_message":"Checking this out"}
I need bigger picture to help You. Paste controller that is throwing that as well as main application config class.
I'm having some problem with validation with Spring Rest, the #HandleBeforeCreate event handler is running before the validation. I was expecting it to run after the validation.
In my test application I have a transaction, which has two fields to store the transaction value, one for the real transaction currency and another for the final value converted to the user currency. In my handle before create I'm dealing with that conversion, but I want the request to stop in the validator if the amount is null.
I could validate the resource in the event handler (I'm ready to handle a RepositoryConstraintViolationException), but it make me think on the point of using validator. It also seams a little inefficient that on every data rest request, spring loop through all validator checking if they support the object class.
Is validation on EventHandlers preferable than Validators (for performance reasons)? How can I force validator to run before EventHandlers?
*I'm using spring-boot 1.4.2.RELEASE
Validator
public class TransactionValidator extends SpringValidator<Transaction> {
#Override
public boolean supports(Class<?> clazz) {
return Transaction.class.equals(clazz) ;
}
#Override
public void validateObject(Transaction transaction, Errors errors) {
... validations ...
}
}
public abstract class SpringValidator<T> implements Validator {
#Override
public void validate(Object target, Errors errors) {
validateObject((T) target, errors);
}
protected abstract void validateObject(T target, Errors errors);
}
Event Handler
#Component
#RequiredArgsConstructor
#RepositoryEventHandler(Transaction.class)
public class TransacationEventHandler {
private final CurrencyUnitService currencyUnitService;
#HandleBeforeCreate
public void beforeCreate(Transaction transaction) {
adjustTransactionAmount(transaction);
}
#HandleBeforeSave
public void beforeSave(Transaction transaction) {
adjustTransactionAmount(transaction);
}
}
Edit
I checked the source code and the listeners are invoked in the following order:
Which make sense actually, using a BeforeCreateHandler is the only way to fix/change something in the Entity before running the Validator. I'm 100% open to inputs.
1. Use #Validated (did not test it)
You could do this:
#HandleBeforeCreate
public void beforeCreate(#Validated Transaction transaction) {
adjustTransactionAmount(transaction);
}
2. Otherwise - you may define the Validator through Java Configuration
You may follow this answer from #MathiasDpunkt (Spring data rest validation + exception mapper: confusing):
#Configuration
public class MyValidationConfiguration extends RepositoryRestConfigurerAdapter {
#Bean
#Primary
/**
* Create a validator to use in bean validation - primary to be able to autowire without qualifier
*/
Validator validator() {
return new LocalValidatorFactoryBean();
}
#Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener
validatingListener) {
Validator validator = validator();
//bean validation always before save and create
validatingListener.addValidator("beforeCreate", validator);
validatingListener.addValidator("beforeSave", validator);
}
}
I have #RestControllerAdvice (spring boot 1.4.2) that looks like this
#RestControllerAdvice
public class GlobalControllerExceptionHandler {
#ExceptionHandler(value = { AvailabilityException.class })
public RestResponse availabilityException(AvailabilityException ex) {
//logic
}
#ExceptionHandler(value = { HrsException.class })
public RestResponse hrsException(HrsException ex) {
//logic
}
}
This class catches excpetions of type HrsException but does not catch exceptions of type AvailabilityException
HrsException
public class HrsException extends RuntimeException {
public Integer errorCode;
public String messageKey;
}
AvailabilityException
public class AvailabilityException extends HrsException {
}
So I'm guessing AvailabilityException is not being caught by the controller advice because it's extending HrsException, what's the explanation for this and how can I continue with this a design?
Basically I want to create a bunch of exceptions that inherits from HrsException (because I don't want duplicate code) and want to catch them in the controller advice.
There was a catch somewhere in the code that was interfering with the controller advice, if someone faces the issue make sure you have no catches in your code preventing the chain from reaching the controller advice.
I want to produce HTTP Response Body with an error message referencing something like _"missing ... 'CUSTOM_AUTHORITY'"_ in addition to a 403 Forbidden HTTP Status code.
My application is Spring Boot with a Spring-Security-Secured #PreAuthorize method within a Spring-MVC-REST #Controller:
MyController
#Controller
#RequestMapping("/foo")
public FooController{
#PreAuthorize("hasAuthority('CUSTOM_AUTHORITY')")
public Object getSomething(){ ... }
}
GlobalExceptionHandlerResolver
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(AccessDeniedException.class)
#ResponseStatus(HttpStatus.FORBIDDEN)
public Object forbidden(AccessDeniedException exception){ ... }
}
What I want is to expose/inject Collection<ConfigAttribute>. The Spring Security docs reference it.
There doesn't seem to be a straightforward way of accomplishing this. The AccessDecisionManager (which is AffirmativeBased) throws the AccessDeniedException with none of the information you want. So if you want to "expose/inject" the Collection<ConfigAttribute>, you'll want to provide your own AccessDecisionManager that throws a custom exception that holds the ConfigAttributes.
The easiest way to do this could be to wrap the default AccessDecisionManager with your own and delegate method calls to it:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled=true)
CustomMethodSecurityConfig extends GlobalMethodSecurityConfiguration
#Override
protected AccessDecisionManager accessDecisionManager() {
AccessDecisionManager default = super.accessDecisionManager();
MyCustomDecisionManager custom = new CustomDecisionManager(default);
}
}
You could define your custom AccessDecisionManager as follows:
public class MyCustomDecisionManager implements AccessDecisionManager {
private AccessDecisionManager default;
public MyCustomDecisionManager(AccessDecisionManager acm) {
this.default = acm;
}
#Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException{
try {
default.decide(authentication, object, configAttributes)
} catch(AccessDeniedException ex) {
throw new CustomAccessDeniedException(ex.getMessage(), configAttributes);
}
}
// other methods delegate to default
}
Now whenever access is denied, you will get an exception that holds the Collection<ConfigAttribute>.
Your custom exception could look like this:
public class CustomAccessDeniedException extends AccessDeniedException {
private Collection<ConfigAttribute> attributes;
public CustomAccessDeniedException(String message, Collection<ConfigAttribute> attr) {
super(message);
this.attributes = attr;
}
public Collection<ConfigAttribute> getAttributes() {
return this.attributes;
}
}
Now your #ExceptionHandler could handle your CustomAccessDeniedException and have access to the ConfigAttributes.
HOWEVER...
I am not sure that will provide you with the error message you wanted. The ConfigAttribute interface only has one method:
String getAttribute();
And the javadoc states:
If the ConfigAttribute cannot be expressed with sufficient precision as a String, null should be returned.
Since we can't rely on the interface method, how you deal with each ConfigAttribute will be heavily dependent on the type of the particular object you're dealing with.
For example, the ConfigAttribute that corresponds to #PreAuthorize("hasAuthority('CUSTOM_AUTHORITY')") is PreInvocationExpressionAttribute, and to print something that resembles what you want, you could do:
PreInvocationExpressionAttribute attr = (PreInvocationExpressionAttribute)configAttribute;
String expressionString = attr.getAuthorizeExpression().getExpressionString();
System.out.println(expressionString); // "hasAuthority('CUSTOM_AUTHORITY')"
That's the major drawback. Also, you would get ALL the ConfigAttributes, not necessarily the ones that failed.