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.
Related
Assuming I have a controller like:
public class MyController {
public String endpoint1() {...}
public String endpoint2() {...}
}
I want to disable endpoint1 for whatever reason in Spring. Simply, just disable it so that it cannot be accessed. So, I am not looking for how and what response to return in that case or how to secure that endpoint. Just looking to simply disable the endpoint, something like #Disabled annotation on it or so.
SOLUTION UPDATE:
Thanks all who contributed. I decided to go with #AdolinK suggestion . However, that solution will only disable access to the controller resulting into 404 Not Found. However, if you use OpenApi, your controller and all of its models such as request/response body will still show in swagger.
So, in addition to Adolin's suggestion and also added #Hidden OpenApi annotation to my controllers like:
In application.properties, set:
cars.controller.enabled=false
Then in your controller, use it. To hide controller from the OpenApi/Swagger as well, you can use #Hiden tag:
#Hidden
#ConditionalOnExpression("${cars.controller.enabled}")
#RestController
#RequestMapping("/cars")
public class Carontroller {
...
}
After this, every end point handled by this controller will return 404 Not Found and OpenApi/Swagger will not show the controllers nor any of its related schema objects such as CarRequestModel, CarResponseModel etc.
You can use #ConditionalOnExpression annotation.
public class MyController {
#ConditionalOnExpression("${my.controller.enabled:false}")
public String endpoint1() {...}
public String endpoint2() {...}
}
In application.properties, you indicates that controller is enabled by default
my.controller.enabled=true
ConditionalOnExpression sets false your property, and doesn't allow access to end-point
Why not remove the mapping annotation over that method?
Try this simple approach: You can define a property is.enable.enpoint1 to turn on/off your endpoint in a flexible way.
If you turn off the endpoint, then return a 404 or error page, which depends on your situation.
#Value("${is.enable.enpoint1}")
private String isEnableEnpoint1;
public String endpoint1() {
if (!"true".equals(isEnableEnpoint1)) {
return "404";
}
// code
}
I have two different serializers for String fields. I want to use either of them conditionally based on an annotation present on the calling Controller method. I'm looking at different ways of doing this via Jackson (eg. annotationIntrospector, JsonView etc). However, I do not see anywhere I can use method annotation during serialization. I can probably check if I can follow something similar to how Jackson implements JsonViews but haven't got to a solution yet.
Here is the use case.
// Dto
public class MyDto {
#Masked //Mask the fields with an option to avoid masking based controller method annotation.
private final String stringField;
// getters, setters.
}
// controller.
// default behavior is to serialize masked.
#ResponseBody
public MyDto getMaskedDto() {
// return dto with masked value.
return this.someService.getDto();
}
// Controller
#IgnoreMasking // Do not mask the dto if method is annotated with #IgnoreMasking.
#ResponseBody
public MyDto getDtoSkipMasking() {
// return dto without masking String field value.
return this.someService.getDto();
}
You could extend Jackon's StdSerializer and override the serialize method.
So something like this:
Create a new CustomSerializer class extending StdSerializer
Override the serialize method
In the overridden method, check for the existence of the object being serialised for the existence of your custom annotation (ie IgnoreMasking). You can do this via reflection
Do your processing
Register your custom serializer into Jackson's ObjectMapper configuration as a new SimpleModule
Is there a way to add validation to feign clients on the request parameters.
For example:
#FeignClient
public interface ZipCodeClient {
#GetMapping("/zipcodes/{zipCode}")
Optional<ZipCodeView> findByZipCode(#PathVariable("zipCode") String zipCode);
}
It would be nice to verify that zipcode is not empty and is of certain length etc, before sending the HTTP call to the server.
If your validations are simple, apply to only headers and query string parameters, you can use a RequestInterceptor for this, as it provides you the opportunity to review the RequestTemplate before it is sent to the Client.
public class ValidatingRequestInterceptor implements RequestInterceptor {
public void apply(RequestTemplate requestTemplate) {
// use the methods on the request template to check the query and values.
// throw an exception if the request is not valid.
}
}
If you need to validate the request body, you can use a custom Encoder
public class ValidatingEncoder implements Encoder {
public void encode(Object object, Type type, RequestTemplate template) {
// validate the object
// throw an exception if the request is not valid.
}
}
Lastly, if you want to validate individual parameters, you can provide a custom Expander for the parameter and validate it there. You can look at this answer for a complete explanation on how to create a custom expander that can work with Spring Cloud.
How to custom #FeignClient Expander to convert param?
For completeness, I've included an example for how to do this with vanilla Feign.
public class ZipCodeExpander implements Expander {
public String expand(Object value) {
// validate the object
// throw an exception if the request is not valid.
}
}
public interface ZipCodeClient {
#RequestLine("GET /zipcodes/{zipCode}")
Optional<ZipCodeView> findByZipCode(#Param(expander = ZipCodeExpander.class) ("zipCode") String zipCode);
}
As pointed out in this comment, a solution using the Bean Validation API would be nice. And indeed, I found in a Spring Boot project that merely placing #org.springframework.validation.annotation.Validated on the interface is sufficient for enabling Bean Validation.
So for example:
#FeignClient
#Validated
public interface ZipCodeClient {
#GetMapping("/zipcodes/{zipCode}")
Optional<ZipCodeView> findByZipCode(#PathVariable("zipCode") #NotEmpty String zipCode);
}
triggering a ConstraintViolationException in the case of violations.
Any standard Bean Validation feature should work here.
UDPATE Note that there seems to be a potential issue with this solution that might require setting a Hibernate Validator configuration property like this: hibernate.validator.allow_parallel_method_parameter_constraint=true
I have few methods in my spring controllers which are mapped on the same path, example.
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
protected ResourceDTO getById(#PathVariable int id) {
return super.getById(id);
}
I was wondering if there is a way to create an annotation that will automatically have set value and method, to have something like this:
#RequestMappingGetByID
protected ResourceDTO getById(#PathVariable int id) {
return super.getById(id);
}
Have a nice day everyone
Update
The goal of this is the following
all my controllers (eg. user, order, client) extends a parametrized BaseController that includes a base set of function (get by id, save, update, delete, etc) All the logic is on the BaseController, but in order to map the value I have to add the annotation on the specific controller.
Instead of writing all the time {id} and post I would like to annotate the methods with a custom interface that already includes those values
The following works for Spring 4.1.x that I tested:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
#interface RequestMappingGetByID {
}
Then you can use
#RequestMappingGetByID
protected ResourceDTO getById(#PathVariable int id) {
return super.getById(id);
}
like you mention.
This kind of annotation is was Spring calls a meta-annotation. Check out this part of the documentation
I am not sure if this meta-annotation would work in versions of Spring prior to 4.x, but it's definitely possible since Spring had some meta-annotation handling capabilities in the 3.x line
If you where using Groovy, you could also take advantage of the #AnnotationCollector AST, which in effect would keep the duplication out of your source code, but would push the regular #RequestMapping annotation into the produced bytecode. Check out this for more details.
The benefit in this case would be that Spring need not have to be equipped with the meta-annotation reading capabilities, and there for the solution possibly works on older Spring versions
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).