I have a use case where I need to limit the values that can be passed as the query param.
#Path("/foo")
public interface Foo {
#GET
#Path("/details/id/{id}")
void getFooDetails(#PathParam("id") String id, #QueryParam("sort") String sortDirection);
}
public class FooImpl {
public void getFooDetails(String id, String sortDir) {
//Implementation
}
}
In the above example, I want to restrict the value of query param sort that can be passed via the API to ASC, DESC.
Is there any existing CXF annotation which I can use to restrict the values on a parameter? I haven't found any and so I tried the following solution.
My Approach:
#Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Inherited
public #interface ValueSet {
String[] allowedValues();
}
The modified interface looks like this.
#Path("/foo")
public interface Foo {
#GET
#PathParam("/details/id/{id}")
void getFooDetails(#PathParam("id") String id, #QueryParam("sort") #ValueSet(allowedValues = {"ASC", "DESC"}) String sortDirection);
}
I wrote a CXF Interceptor which intercepts the API invocation. I used reflection to get a handle on FooImpl.getFooDetails params. But the problem I faced is that the interceptor looks at FooImpl.getFooDetails method and doesn't find the annotations #QueryParam on the method params since #QueryParam is on the base method and the annotation is not inherited.
Interceptor implementation:
#Provider
public class ParamValidationInterceptor extends AbstractPhaseInterceptor<Message> {
public ParamValidationInterceptor() {
super(Phase.PRE_INVOKE);
super.addBefore(someInterceptor);
}
#Override
public void handleMessage(Message message) throws Fault {
UriInfo uriInfo = new UriInfoImpl(message);
MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
Method methodToInvoke = (Method) message.get("org.apache.cxf.resource.method");
Parameter[] parameters = methodToInvoke.getParameters();
for (Parameter parameter : parameters) {
if (parameter.isAnnotationPresent(ValueSet.class)) {
ValueSet valueSet = parameter.getAnnotation(ValueSet.class);
QueryParam queryParam = parameter.getAnnotation(QueryParam.class);
Object invokedVal = queryParams.get(queryParam.value());
String[] allowedValues = valueSet.allowedValues();
if (!Arrays.asList(allowedValues).contains(invokedVal)) {
throw new CustomException();
}
}
}
}
}
Can anyone suggest a way forward? It would be great if anyone can suggest an alternative approach.
P.S: I am using CXF as an implementation for JAX-RS and spring is used as a container.
Update:
Like #Cássio Mazzochi Molin and #Andy McCright suggested, I will go with #Pattern annotation. But I am curious to know why the JAX-RS annotations are not inherited from the interface although the spec says they will be inherited.
Annotation inheritance
According to the section §3.6 Annotation Inheritance of the JAX-RS specification, it is recommended to always repeat annotations instead of relying on annotation inheritance.
Refer to this answer for the complete quote.
#QueryParam can be applied to different targets
Bear in mind that the #QueryParam annotation can be applied to:
Resource method parameters
Resource class fields
Resource class bean properties
Hence a manual validation can be tricky.
Use Bean Validation
For validation purposes, you should consider Bean Validation. Consider a #Pattern annotation with the allowed values:
#Pattern(regexp = "ASC|DESC")
And just annotate your resource method parameter:
#GET
#Path("foo")
public Response getFoos(#QueryParam("sort")
#Pattern(regexp = "ASC|DESC") String sortDirection) {
...
}
If you prefer case insensitive values, use:
#Pattern(regexp = "ASC|DESC", flags = Pattern.Flag.CASE_INSENSITIVE)
If the given value is invalid, a ConstraintViolationException will be thrown. To handle such exception and return a customized response, you can use an ExceptionMapper:
#Provider
public class ConstraintViolationExceptionMapper
implements ExceptionMapper<ConstraintViolationException> {
#Override
public Response toResponse(ConstraintViolationException exception) {
...
}
}
Perhaps it is just a typo, but CXF may not be recognizing the getFooDetails method (on the interface) because it is annotated with #PathParam instead of #Path.
Instead of using your ValueSet approach, I used BeanValidation, but the following code works for me.
Foo.java
#Path("/foo")
public interface Foo {
#GET
#Path("/details/id/{id}")
Response getFooDetails(
#PathParam("id") #Pattern(regexp="[0-9]*") String id,
#QueryParam("sort") #Pattern(regexp = "ASC|DESC") String sortDirection);
}
FooImpl.java
public class FooImpl implements Foo {
#Override
public Response getFooDetails(String id, String sortDirection) {
Integer idInt = Integer.parseInt(id);
if ("ASC".equals(sortDirection) || sortDirection == null) {
...
} else if ("DESC".equals(sortDirection)) {
...
}
return ...;
}
I've got this working on WebSphere Liberty 17.0.0.2 which is based on CXF 3.1.11.
Hope this helps,
Andy
Related
I have the following interface:
public interface TestRequestView {
String getCountryCode();
String getRegionCode();
}
It's used in several end points like so:
#PostMapping("/my/path/{id}")
public String test(#RequestBody TestRequestView request, #PathVariable String id) {
...
}
I would like to add a property to the TestRequestView that is only used in one new endpoint without breaking the rest, how can I mark that one property as ignorable? Something like:
public interface TestRequestView {
String getCountryCode();
String getRegionCode();
String getEmail(); // make this not required
}
Usually it is better to use 1 such a model per endpoint so they are independent. If you share models between endpoints this should be useful
This may help
You can use the #JsonIgnoreProperties annotation
Example:
#JsonIgnoreProperties(value = {"email"})
public interface TestRequestView {
String getCountryCode();
String getRegionCode();
#JsonProperty(required = false)
String getEmail();
}
Add annotation #JsonInclude(Include.NON_NULL) at class level for your TestRequestView. When passing your param you can pass it without that value and in your controller it will be received with that param as null. You just need to make sure that your controller can handle such case
I'm trying to map request parameters of a controller method into a POJO object, but only if any of its fields are present. However, I can't seem to find a way to achieve this. I have the following POJO:
public class TimeWindowModel {
#NotNull
public Date from;
#NotNull
public Date to;
}
If none of the fields are specified, I'd like to get an empty Optional, otherwise I'd get an Optional with a validated instance of the POJO. Spring supports mapping request parameter into POJO objects by leaving them unannotated in the handler:
#GetMapping("/shop/{shopId}/slot")
public Slice<Slot> getSlots(#RequestAttribute("staff") Staff staff,
#PathVariable("shopId") Long shopId, #Valid TimeWindowModel timeWindow) {
// controller code
}
With this, Spring will map request parameters "from" and "to" to an instance of TimeWindowModel. However, I want to make this mapping optional. For POST requests you can use #RequestBody #Valid Optional<T>, which will give you an Optional<T> containing an instance of T, but only if a request body was provided, otherwise it will be empty. This makes #Valid work as expected.
When not annotated, Optional<T> doesn't appear to do anything. You always get an Optional<T> with an instance of the POJO. This is problematic when combined with #Valid because it will complain that "from" and "to" are not set.
The goal is to get either (a) an instance of the POJO where both "from" and "to" are not null or (b) nothing at all. If only one of them is specified, then #Valid should fail and report that the other is missing.
I came up with a solution with a custom HandlerMethodArgumentResolver, Jackson and Jackson Databind.
The annotation:
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
public #interface RequestParamBind {
}
The resolver:
public class RequestParamBindResolver implements HandlerMethodArgumentResolver {
private final ObjectMapper mapper;
public RequestParamBindResolver(ObjectMapper mapper) {
this.mapper = mapper.copy();
this.mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(RequestParamBind.class) != null;
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mav, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
// take the first instance of each request parameter
Map<String, String> requestParameters = webRequest.getParameterMap()
.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue()[0]));
// perform the actual resolution
Object resolved = doResolveArgument(parameter, requestParameters);
// *sigh*
// see: https://stackoverflow.com/questions/18091936/spring-mvc-valid-validation-with-custom-handlermethodargumentresolver
if (parameter.hasParameterAnnotation(Valid.class)) {
String parameterName = Conventions.getVariableNameForParameter(parameter);
WebDataBinder binder = binderFactory.createBinder(webRequest, resolved, parameterName);
// DataBinder constructor unwraps Optional, so the target could be null
if (binder.getTarget() != null) {
binder.validate();
BindingResult bindingResult = binder.getBindingResult();
if (bindingResult.getErrorCount() > 0)
throw new MethodArgumentNotValidException(parameter, bindingResult);
}
}
return resolved;
}
private Object doResolveArgument(MethodParameter parameter, Map<String, String> requestParameters) {
Class<?> clazz = parameter.getParameterType();
if (clazz != Optional.class)
return mapper.convertValue(requestParameters, clazz);
// special case for Optional<T>
Type type = parameter.getGenericParameterType();
Class<?> optionalType = (Class<?>)((ParameterizedType)type).getActualTypeArguments()[0];
Object obj = mapper.convertValue(requestParameters, optionalType);
// convert back to a map to find if any fields were set
// TODO: how can we tell null from not set?
if (mapper.convertValue(obj, new TypeReference<Map<String, String>>() {})
.values().stream().anyMatch(Objects::nonNull))
return Optional.of(obj);
return Optional.empty();
}
}
Then, we register it:
#Configuration
public class WebConfig implements WebMvcConfigurer {
//...
#Override
public void addArgumentResolvers(
List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new RequestParamBindResolver(new ObjectMapper()));
}
}
Finally, we can use it like so:
#GetMapping("/shop/{shopId}/slot")
public Slice<Slot> getSlots(#RequestAttribute("staff") Staff staff,
#PathVariable("shopId") Long shopId,
#RequestParamBind #Valid Optional<TimeWindowModel> timeWindow) {
// controller code
}
Which works exactly as you'd expect.
I'm sure it's possible to accomplish this by using Spring's own DataBind classes in the resolver. However, Jackson Databind seemed like the most straight-forward solution. That said, it's not able to distinguish between fields that are set to null and fields that just not set. This is not really an issue for my use-case, but it's something that should be noted.
To implement logic (a) both not null or (b) both are nulls you need to implement custom validation.
Examples are here:
https://blog.clairvoyantsoft.com/spring-boot-creating-a-custom-annotation-for-validation-edafbf9a97a4
https://www.baeldung.com/spring-mvc-custom-validator
Generally, you create a new annotation, it's just a stub, and then you create a validator which implements ConstraintValidator where you provide your logic and then you put your new annotation to your POJO.
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.
Background
I'm creating RESTful services using Spring MVC. Currently, I have the following structure for a controller:
#RestController
#RequestMapping(path = "myEntity", produces="application/json; charset=UTF-8")
public class MyEntityController {
#RequestMapping(path={ "", "/"} , method=RequestMethod.POST)
public ResponseEntity<MyEntity> createMyEntity(
#RequestBody MyEntity myEntity,
#RequestHeader("X-Client-Name") String clientName) {
myEntity.setClientName(clientName);
//rest of method declaration...
}
#RequestMapping(path={ "/{id}"} , method=RequestMethod.PUT)
public ResponseEntity<MyEntity> updateMyEntity(
#PathVariable Long id,
#RequestBody MyEntity myEntity,
#RequestHeader("X-Client-Name") String clientName) {
myEntity.setClientName(clientName);
//rest of method declaration...
}
#RequestMapping(path={ "/{id}"} , method=RequestMethod.PATCH)
public ResponseEntity<MyEntity> partialUpdateMyEntity(
#PathVariable Long id,
#RequestBody MyEntity myEntity,
#RequestHeader("X-Client-Name") String clientName) {
myEntity.setClientName(clientName);
//rest of method declaration...
}
}
As you can see, all these three methods receive the same parameter for the header #RequestHeader("X-Client-Name") String clientName and applies it in the same way on each method: myEntity.setClientName(clientName). I will create similar controllers and for POST, PUT and PATCH operations will contain almost the same code but for other entities. Currently, most entities are designed to support this field vía a super class:
public class Entity {
protected String clientName;
//getters and setters ...
}
public class MyEntity extends Entity {
//...
}
Also, I use an interceptor to verify that the header is set for requests.
Question
How can I avoid repeating the same code through controller classes and methods? Is there a clean way to achieve it? Or should I declare the variable and repeat those lines everywhere?
This question was also asked in the Spanish community. Here's the link.
My suggestion is to store the header value in the request scoped bean inside the Spring interceptor or filter. Then you may autowire this bean wherever you want - service or controller and use the stored client name value.
Code example:
public class ClientRequestInterceptor extends HandlerInterceptorAdapter {
private Entity clientEntity;
public ClientRequestInterceptor(Entity clientEntity) {
this.clientEntity = clientEntity;
}
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String clientName = request.getHeader("X-Client-Name");
clientEntity.setClientName(clientName);
return true;
}
}
In your configuration file:
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(clientRequestInterceptor());
}
#Bean(name="clientEntity")
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Entity clientEntity() {
return new Entity();
}
#Bean
public ClientRequestInterceptor clientRequestInterceptor() {
return new ClientRequestInterceptor(clientEntity());
}
}
Then, lets assume we have to use this bean in our controller:
#RestController
#RequestMapping(path = "myEntity", produces="application/json; charset=UTF-8")
public class MyEntityController {
#Autowired
private Entity clientEntity; // here you have the filled bean
#RequestMapping(path={ "", "/"} , method=RequestMethod.POST)
public ResponseEntity<MyEntity> createMyEntity(#RequestBody MyEntity myEntity) {
myEntity.setClientName(clientEntity.getClientName());
//rest of method declaration...
}
// rest of your class methods, without #RequestHeader parameters
}
I have not compiled this code, so correct me if I made some mistakes.
I've got an interesting answer in the Spanish site (where I also posted this question) and based on that answer I could generate mine that adapts to this need. Here's my answer on SOes.
Based on #PaulVargas's answer and an idea from #jasilva (use inheritance in controller) I though on a stronger solution for this case. The design consists of two parts:
Define a super class for controllers with this behavior. I call this class BaseController<E extends Entity> because Entity is the super class for almost al my entities (explained in the question). In this class I'll retrieve the value of #RequestBody E entity parameter and assign it into a #ModelAttribute parameter like #PaulVargas explains. Generics power helps a lot here.
My controllers will extend BaseController<ProperEntity> where ProperEntity is the proper entity class I need to handle with that controller. Then, in the methods, instead of injecting #RequestBody and #RequestHeader parameters, I'll only inject the the #ModelAttribute (if needed).
Aquí muestro el código para el diseño descrito:
//1.
public abstract class BaseController<E extends Entity> {
#ModelAttribute("entity")
public E populate(
#RequestBody(required=false) E myEntity,
#RequestHeader("X-Client-Name") String clientName) {
if (myEntity != null) {
myEntity.setCreatedBy(clientName);
}
return myEntity;
}
}
//2.
#RestController
#RequestMapping(path = "myEntity", produces="application/json; charset=UTF-8")
public class MyEntityController extends BaseController<MyEntity> {
#RequestMapping(path={ "", "/"} , method=RequestMethod.POST)
public ResponseEntity<MyEntity> createMyEntity(
#ModelAttribute("entity") MyEntity myEntity) {
//rest of method declaration...
}
#RequestMapping(path={ "/{id}"} , method=RequestMethod.PUT)
public ResponseEntity<MyEntity> updateMyEntity(
#PathVariable Long id,
#ModelAttribute("entity") MyEntity myEntity) {
//rest of method declaration...
}
#RequestMapping(path={ "/{id}"} , method=RequestMethod.PATCH)
public ResponseEntity<MyEntity> partialUpdateMyEntity(
#PathVariable Long id,
#ModelAttribute("entity") MyEntity myEntity) {
//rest of method declaration...
}
}
In this way, I don't need to rewrite those lines of code in every method and controller, achieving what I've asked.
You could consider using RequestBodyAdvice. See the javadocs.
The HttpInputMessage object where you can access the http headers, is passed into the interface methods.
I couldn't catch the essence of the #ValidateOnExecution annotation. Could someone please explain the use case for it?
According to jersey's documentation, constraints on resource methods are automatically validated. This code snippet is from jersey's example.
#GET
#NotNull
#HasId
public List<ContactCard> getContacts() {
return StorageService.findByName("");
}
#GET
#Path("{id}")
#NotNull(message = "{contact.does.not.exist}")
#HasId
public ContactCard getContact(
#DecimalMin(value = "0", message = "{contact.wrong.id}")
#PathParam("id") final Long id) {
return StorageService.get(id);
}
If the constraints are in a pojo you can trigger validation with #Valid (See).
#Path("/")
class MyResourceClass {
#POST
#Consumes("application/xml")
public void registerUser(#Valid User user) {
...
}
}
So what is #ValidateOnExecution used for except explicitly turning off the validation?
According to Jersey latest documentation #ValidateOnExecution annotation should be used for next purposes:
According to Bean Validation specification, validation is enabled by default only for the so called constrained methods. Getter methods as defined by the Java Beans specification are not constrained methods, so they will not be validated by default. The special annotation #ValidateOnExecution can be used to selectively enable and disable validation. For example, you can enable validation on method getEmail shown in Example
#Path("/")
class MyResourceClass {
#Email
#ValidateOnExecution
public String getEmail() {
return email;
}
...
}
The default value for the type attribute of #ValidateOnExecution is IMPLICIT which results in method getEmail being validated.
Thus #ValidateOnExecution can be also used at least for enabling validation for getter-methods.