I know I can validate forms in Spring, but can I apply similar validate to URL parameters? For example, I have a method in my controller as follows:
public String edit(#PathVariable("system") String system,
#RequestParam(value="group") String group,
ModelMap model) throws DAOException {
Can I validate the values of system and group before the method is called, to ensure they are of a certain value or match a certain regex?
Thanks
You may be able to use Spring Asserts for this. The Assert api (http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/util/Assert.html) runs a supplied expression against the specified parameters and if the expression equates to false then it throws an exception.
Ex:
Assert.isTrue(system.equals("ValidSystemName"), "You must supply a valid system");
It also contains functions to check that parameters are not null or are not empty strings, etc.
Create an annotation that marks parameters that should be validated. This annotation needs a #Retention of RUNTIME and a #Target of ElementType.PARAMETER.
Create a validator implemented as an AspectJ Aspect.
Wrap calls to controllers with this validator.
A sample annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
#Documented
public #interface ValidSystemParameter {
}
A sample validator:
#Aspect
public class ValidSystemParameterValidator {
#Pointcut("TODO: write your pointcut expression")
public void controllerMethodWithValidSystemParameter();
#Before(pointcut = "controllerMethodWithValidSystemParameter()")
public void validateSystemParameter(String systemParameter) {
// validate the parameter (throwing an exception)
}
}
To learn about the AspectJ pointcut expression language see: http://www.eclipse.org/aspectj/doc/released/progguide/language-joinPoints.html
To learn about AspectJ integration in Spring see: http://static.springsource.org/spring/docs/current/spring-framework-reference/html/aop.html#aop-ataspectj
I might be a little late, but with Spring 3.0 you have the option of using JSR-303 validation with the #Valid annotation. There are also some more specific annotations as #DateTimeFormat and #NumberFormat. More details here: http://static.springsource.org/spring/docs/3.0.5.RELEASE/reference/validation.html#validation-mvc
As I see it you have two options:
Define your request parameters as objects and user JSR-303
validation.
Use the Assert api as mentioned above.
If you just want to make a simple validation on a single value, I would go with the latter (that's what I did when I had simple int values to check for max value).
Related
I have a function under class MyController:
#RestController
#RequestMapping(value = "/api/service")
public class MyController {
#PostMapping(value = "add_person")
public MyResponse addPerson(#RequestBody Person person) {
// ...
}
#PostMapping(value = "add_person_2")
public MyResponse addPerson(#PathVariable(value = "person_age") Int age, #RequestBody Person person) {
// ...
}
}
I have setup AspectJ in my project to have a AOP logic to run whenever those two addPerson(...) method above is called:
#Around("execution(public MyResponse addPerson(..))")
public void around(ProceedingJoinPoint joinPoint) {
// NO matter which addPerson(...) is executing, I am only interested in the
// parameter value annotated with #RequestBody.
// How can I access the parameter that passed in addPerson(...) & is annotated with
// #RequestBody through ProceedingJoinPoint ?
}
My question is mentioned in above code comment. I wonder how can I access the parameter annotated with #RequestBody in my AOP function? I don't want to check parameter type or name, but interested to know how to access parameter by checking the annotation through ProceedingJoinPoint. Is it possible?
I do not want to mark this question as a duplicate because it is no exact duplicate, but my answer here should answer the question about how to
match an annotated parameter at any position,
get the annotation + the parameter value itself.
The linked answer uses a #Before advice. If you want to somehow replace the value by another one in an #Around advice when calling proceed() this is also possible, but was not asked here and my request for seeing more of the advice method body was also ignored.
If you want to limit to annotated Person parameters, you would have to use the fully qualified class name my.package.Person instead of the * inside (*) and do the corresponding cast after accessing the parameter in the advice body.
In my comment I also asked if the parameter has a fixed relative position in the parameter list such as first, last or second/third from left/right. If the OP would have confirmed such a fixed relative position, reflection would not be necessary and the corresponding parameter could be bound to an advice method parameter directly via args() pointcut designator. This would be quite elegant and eliminate the need to loop over getArgs() or over a two-dimensional array of parameter annotations.
I want to seek a best practice for applying business rules when working with spring data rest.
Lets consider following scenario:
I have a Customer and Order in #OneToMany relationship.
I have a business rule saying that Customer needs to have verified flag set to be able to make orders
So I need to make sure that whenever someone POSTs to /orders the Customer making the call is verified.
I'm considering using beforeSave Validators autowiring other service/repositories into the Validator and check whatever needs to be checked.
Is there better way of achieving the same?
There are several ways to solve this. As far as my knowledge goes:
Usage of spring security annotations like #PreAuthorize. The intended use of these annotations is however for security purposes and you are mentioning business rules. I would use these for user authorization rules Spring data rest security chapter
The use of validators as you mentioned yourself. Spring data rest Validators
Use spring data rest events Spring data rest events. You can create global event handlers, however here you need to determine the entity type. I would go with Annotated event handlers to perform business logic Spring data rest annotated event handler
So just for the sake of world piece I'm adding my solution. Went with #2.
The documentation is pretty clear on how to proceed so just sharing few tips which may save you time.
You need to assign validators manually, auto-discovery doesn't work
Manually spelling event type is error prone, some helper Enum could be handy.
Like:
/**
* "beforeSave" gets called on PATCH/PUT methods
* "beforeCreate" on POST
* "beforeDelete" on DELETE
*/
enum Event {
ON_CREATE("beforeCreate"), ON_UPDATE("beforeSave"),
ON_DELETE("beforeDelete");
private String name;
Event(String name) {
this.name = name;
}
}
...
private static void addValidatorForEvents(ValidatingRepositoryEventListener eventListener, Validator validator, Event... events) {
Arrays.asList(events).forEach(event -> eventListener.addValidator(event.name, validator));
}
One out of the box solution you can use to solve your Business rules related problems, is using Spring AOP. What you can do, is define an Annotation (say #X) and place that annotation on top of your POST call.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface X{}
Next what you need to do is, create an aspect, and run your custom validation logic in this aspect as follows,
#Aspect
#Component
public class CustomAspect {
//You can autowire beans here
#Around("#annotation(qualified name of X)")
public Object customMethod(ProceedingJoinPoint joinPoint) throws Throwable {
flag = customLogic();
if (flag){
return joinPoint.proceed(); //return if logic passes, otherwise
}else{
throw new BusinessRuleException("Business rule violated");
}
}
private boolean customLogic(){
//your custom logic goes here
}
}
And finally apply this annotation on top of any method in controller layer like:
#X
#RequestMapping(method = RequestMethod.POST, value = "do-something")
public void callSomething(HttpServletRequest request) throws Exception {
// your business logic goes here
}
Only thing to note above is that you need to pass HttpServletRequest request explicitly to your controller method in order to AOP aspect get the same context for manipulation of user session related attributes like session_id, etc.
Above solution will help you add business rules on top of your Business Logic and help you with all kinds of pre validations you want to build in your web application. It is a pretty handy application of Spring AOP. Do reach out in case of any
I am using spring boot. I want to validated the POST request params. So I have gine through #Validated annotation but this require creating a different class for Every API. How should I write my code?
As for example, this is my api
#RequestMapping("/tags/{tagId}/{tagParentId}")
public Response<Demo> a(#PathVariable int tagId, #PathVariable int tagParentId){
... code
}
#RequestMapping("/data/{courseId}/{instId}")
public Response<Demo> b(#PathVariable int courseId, #PathVariable int instId){
... code
}
How should I change my code to add params validation for there API's such that I do not need to create two different validation class? Just one class and then I can add different functions for different API's.
#Validated should be used, to check that a parameter is syntactical correct.
As you are using int values, this is already done by spring.
If tagId is not a valid int, the client will already receive a Http error code.
The validation, whether there is a tag with the given tagId is implicitly done in your code, you do not need an additional validator for that.
If you read tags for example from the database, and you cannot find a tag for the tagId, you should
return new ResponseEntity(HttpStatus.NOT_FOUND);
from your controller method.
You may need to change the return type of your controller method to a common superclass or just to Object, to allow returning the ResponseEntity.
Its also possible to throw exceptions in the controller methods and to configure spring to return a regarding HttpStatus.
See exception-handling-for-rest-with-spring
Plain and simple issue:
I would like to annotate my method parameter with some sort of annotation that would fire regular spring validation mechanism (based on Validator interface)
I don't want to include JSR303 dependency
Any ideas? I looked on #Validated but it seems that it was not created for this purpose.
Right now I do it like this:
public String req(#ModelAttribute SomeRequest request, BindingResult errors) {
validator.validate(request, errors); // This can be avoided
if (!errors.hasErrors()) {
// Valid request
return ...
} else {
// There were errors
return ...
}
}
In that case you need to create custom annotation using #interface and then use that instead of standard #Valid annotation and at runtime identify it and validate your fields accordingly. Hope this helps you. Cheers.
Is it possible with JSR-303 bean validation to write a custom annotation that can talk to a back end service?
We accomplished this type of validation with the "old school" Spring validators. In that case, the validator was a Spring bean and could have other services injected into it. Then that validator is injected into the controller.
An example might be an annotation (perhaps #EmailExists) to verify if an email already exists. I can only do this with a SQL query using one of our services. I would prefer to "validate" this alongside the other annotations and check it as soon as possible and not have to explicity do it in a back end service.
NOTE: We are using iBatis/MyBatis so I can't use any JPA/Hibernate tricks :-)
thanks!
That's definitely possible. Spring provides dependency injection support also within constraint validators. So you can simply inject any required services in your custom validators like this:
public class EmailExistsValidator implements ConstraintValidator<EmailExists, String> {
#Inject
private EmailValidationService service;
#Override
public void initialize(EmailExists constraintAnnotation) {}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
return service.exists(value);
}
}
Depending on your concrete scenario it might be a good idea to first check "cheap" constraints such as #NotNull and only if these constraints are valid check more expensive constraints such as #EmailExists.
You can do this with help of group sequences and a redefined default group sequence for your type.