Java 以CXF为实现限制查询参数JAX-RS的值

Java 以CXF为实现限制查询参数JAX-RS的值,java,spring,rest,jax-rs,cxf,Java,Spring,Rest,Jax Rs,Cxf,我有一个用例,需要限制可以作为查询参数传递的值 @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

我有一个用例,需要限制可以作为查询参数传递的值

@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
    }
}
在上面的示例中,我想限制可以通过API传递给
ASC,DESC
的query param
sort
的值

是否有任何现有的CXF注释可用于限制参数上的值?我没有找到任何,因此我尝试了以下解决方案

我的方法:

@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ValueSet {

    String[] allowedValues();
}
修改后的界面如下所示

@Path("/foo")
public interface Foo {

    @GET
    @PathParam("/details/id/{id}")
    void getFooDetails(@PathParam("id") String id, @QueryParam("sort") @ValueSet(allowedValues = {"ASC", "DESC"}) String sortDirection);
}
我编写了一个CXF拦截器,用于拦截API调用。我使用反射来获取
FooImpl.getFooDetails
params的句柄。但我面临的问题是,拦截器查看FooImpl.getFooDetails方法,但在方法参数上找不到@QueryParam注释,因为@QueryParam位于基本方法上,并且注释没有继承

拦截器实现:

@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();
                }
            }
        }

    }
}
@Provider
公共类ParamValidationInterceptor扩展了AbstractPhaseInterceptor{
公共参数ValidationInterceptor(){
超级(阶段前调用);
super.addBefore(someInterceptor);
}
@凌驾
public void handleMessage(消息消息消息)引发错误{
UriInfo UriInfo=新的UriInfoImpl(消息);
多值Map queryParams=uriInfo.getQueryParameters();
methodMethodToInvoke=(Method)message.get(“org.apache.cxf.resource.Method”);
Parameter[]parameters=methodToInvoke.getParameters();
用于(参数:参数){
if(参数.isAnnotationPresent(ValueSet.class)){
ValueSet ValueSet=parameter.getAnnotation(ValueSet.class);
QueryParam QueryParam=parameter.getAnnotation(QueryParam.class);
objectinvokedval=queryParams.get(queryParam.value());
字符串[]allowedValues=valueSet.allowedValues();
如果(!Arrays.asList(allowedValues).contains(invokedVal)){
抛出新的CustomException();
}
}
}
}
}
有人能提出一个前进的方向吗?如果有人能提出另一种方法,那就太好了

另外:我使用CXF作为JAX-RS的实现,spring用作容器

更新:

@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ValueSet {

    String[] allowedValues();
}
就像@Cássio Mazzochi Molin和@Andy McRight建议的那样,我将使用@Pattern annotation。但是我很好奇为什么JAX-RS注释没有从接口继承,尽管规范说它们将被继承。

注释继承 根据的第3.6节注释继承,建议始终重复注释,而不是依赖注释继承

有关完整的报价,请参阅此

@QueryParam
可以应用于不同的目标 请记住,注释可应用于:

  • 资源方法参数
  • 资源类字段
  • 资源类bean属性
因此,手动验证可能很棘手

使用Bean验证

为了验证目的,你应该考虑。考虑带有允许值的注释:

模式(regexp=“ASC | DESC”) 只需注释资源方法参数:

@GET
@路径(“foo”)
公共响应getFoos(@QueryParam(“排序”)
@模式(regexp=“ASC | DESC”)字符串排序方向){
...
}
如果您更喜欢不区分大小写的值,请使用:

@Pattern(regexp=“ASC | DESC”,flags=Pattern.Flag.不区分大小写)
如果给定的值无效,将抛出一个。要处理此类异常并返回自定义响应,可以使用:

@Provider
公共类约束ViolationExceptionMapper
实现ExceptionMapper{
@凌驾
公众对响应的响应(ConstraintViolationException){
...
} 
}

也许这只是一个输入错误,但CXF可能无法识别GetFoodDetails方法(在接口上),因为它是用@PathParam而不是@Path注释的

我没有使用您的ValueSet方法,而是使用了BeanValidation,但是下面的代码对我很有用

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 ...;
    }
我已经在基于CXF3.1.11的WebSphereLiberty 17.0.0.2上完成了这项工作

希望这有帮助,
Andy

另一个答案中的规范谈到注释继承。它说,如果扩展类上没有注释,那么注释是继承的。尽管推荐的方法是重复注释,但我选择不这样做,以便以更干净的方式维护接口上的API规范。但是规范说,如果扩展类上没有注释,注释将被继承。但我无法通过具体类的方法对象句柄来访问注释。我希望能够从基类访问注释。Mazzochi Molin,模式注释似乎不适合我。我在maven依赖项中有javax验证jar和hibernate验证程序。我还需要做些什么才能让它工作吗?我在tomcat容器中使用spring和cxf作为jax-rs实现。我假设创建了一个spring代理,每当调用这个方法时,它都会验证方法args。如果我错了,请更正。@yaswans您可能缺少任何配置。查看文档:我仍然建议使用BeanValidation。这当然比使用拦截器检查ResourceImpl类上的注释要简单。而且似乎有人也认为这是个好主意我同意你的看法。我将使用@Pattern注释。但是我很想知道为什么我不能从具体类访问接口的JAXRS注释?我只是想知道这是不是出了什么问题