Limiting the values of Query Params JAX-RS with CXF as implementation

Viewed 2532

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.

2 Answers
Related