I create a custom annotation and HandlerInterceptorAdapter that will just get memberNo and print it out.
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface MyCustomAnnotation
{
String memberNo();
}
And on Controller something like this:
#MyCustomAnnotation(memberNo = "${someBodyObject.memberNo}")
#RequestMapping(value = "/test/", method = RequestMethod.GET)
public String test(#RequestBody SomeBodyObject someBodyObject) {
System.out.println("--- TEST ---");
return "-- FINISHED ---";
}
Request body SomeBodyObject have one filed and it's memberNo.
How can I inject that memberNo from RequestBody to HandlerInterceptorAdapter?
And is it possible to user Spring SpEL or something else to get data from Body and send to AnnotationResolver?
Or maybe there is some other way to do this?
I try like this and response is: ${token.memberNo} as a String
That's not possible.
Annotations require constant values and a method parameter is dynamic.
Related
I have an endpoint secured with a token given in the header. I would like to create my custom annotation so when I annotate the controller method the validation for the token goes first and then if the token was accurate do the rest of the method. I tried to do it with an interceptor and it worked but I want to do it using reflection. I can share some of my code snippet but there is not a lot of code because I couldn't find some that tells me how to get a token from the header and validate it.
Custom Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface TokenValidation {
String token() default "";
}
Controller Class:
#TokenValidation
#PostMapping("/endpoint")
public ResponseEntity createComplianceDirectory(#RequestBody ComplianceDirRequest request) {
return ResponseEntity.status(HttpStatus.OK).build();
}
Reflection class:
#Value("${token}")
private String token;
private Reflections reflections;
public Reflection() {
reflections = new Reflections(new ConfigurationBuilder().setUrls(ClasspathHelper.forPackage("com.sampleapp.controller"))
.setScanners(new FieldAnnotationsScanner()));
setReflections();
}
public void setReflections() {
Set<Method> methods = reflections.getMethodsAnnotatedWith(TokenValidation.class);
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
String requestToken = request.getHeader("Authorization");
}
}
My questions are:
How to register that reflection class in spring boot to be invoked when the controller method is called.
Is it a good way in my reflection class to retrieve the header ?
I have annotation & want to send class name there as parameter:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
#Documented
public #interface PostApiRequest {
Class<?> value();
}
I attach annotation to method of parent class:
#PostApiRequest(value = ...)
#Override
public ResponseEntity<D> save(#RequestBody D dto) {
Application don't know, which inheritor shall call this method, than I want to send inheritor there to work with its properties later. I should see any like this:
#PostApiRequest(value = this.class) //send inheritor
#Override
public ResponseEntity<D> save(#RequestBody D dto) {
but it not works.
Please, give advice, how to do it?
I'm looking for something like JSR-303 Validation Groups (bean validation, when you mark method argument with #Validated(GroupName.class) in controller and specify group in request class fields where needed), but it should decide how to validate at runtime depending on one of request fields.
For example, if we have controller class like this
#Controller
public class MyController {
#Autowired
private MyService myService;
#ResponseBody
#RequestMapping(value = "/path", method = RequestMethod.PUT)
public ResponseVo storeDetail(/*maybe some annotation here*/ DetailRequestVo requestVo) {
return myService.storeDetail(requestVo);
}
class DetailRequestVo {
String type;
Long weight;
Long radius;
}
}
And we want validation depending on type field value: if type = "wheel" then radius and weight fields should be presented, if type = "engine" then only weight field should be presented.
Does Spring (as of 3.2.17) provide API to implement these rules in more declarative approach? org.springframework.validation.Validator looks like not an option here, because its method boolean supports(Class<?> clazz) decides based on class info, not instance info.
Thanks in advance
Not sure about Spring but you can do that with plain JSR-303 using a custom Validator for the class itself... like
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE })
#Constraint(validatedBy = TypeValidator.class)
#Documented
public #interface ValidType {
...
}
public class TypeValidator implements ConstraintValidator<ValidType , Object> {
public boolean isValid(final Object target, final ConstraintValidatorContext context) {
DetailRequestVo request = (DetailRequestVo) target;
// do your checks here
}
and used like
#ValidType
class DetailRequestVo {
String type;
Long weight;
Long radius;
}
Since the custom Validator has access to the whole DetailRequestVo-Object you can do your check of field A depending on field B etc.
I have the following combined custom annotation with Springs #PreAuthorize annotation,
#RequestMapping(
produces = MimeTypeUtils.APPLICATION_JSON_VALUE + ";charset=UTF-8",
method = RequestMethod.GET)
#ResponseBody
#PreAuthorize(value = "hasRole('permitAll()')")
#ResponseStatus(HttpStatus.OK)
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Get {
#AliasFor(annotation = RequestMapping.class, attribute = "value")
String[] value() default {};
#AliasFor(annotation = PreAuthorize.class, attribute = "value")
String authorize() default "permitAll()";
}
And I've got the following client using it
#Get(value = "/users", authorize = "hasRole('ROLE_GET_USERS')")
public List<User> retrieveUsers() {
// body
}
As you can see the purpose is to allow clients of #Get annotation to override the #PreAuthorize so that they can provide the role they require for.
I din't have so far any problem using #AliasFor, even in this example #RequestMapping is working, but unfortunately it does not override the value of #PreAuthorize and everyone can still access the resources as the default value is permitAll().
I wonder first of all why this does not work, and second if it is possible to make it work?
In Spring MVC, it is easy to bind request parameter to method paramaters handling the request. I just use #RequestParam
#Controller
public class ConfirmOrderAction {
public String toConfirmOrder(#RequestParam String itemIds){
}
}
but i hope like this
#Controller
public class ConfirmOrderAction {
#RequestParam
private String itemIds;
getItemIds(){}
setItemIds(){}
public String toConfirmOrder(){}
}
like struts2
This is not possible with Spring's #RequestParam annotation:
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface RequestParam {
...
}
This means #RequestParam can only be used on method parameters.
Do not forget that Spring controllers are singletons by default (one instance is shared by multiple requests). So it would be a bad idea to bind request parameters to instance fields.