You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让RESTEasy对无效查询参数名称返回错误?

问题描述

合法的REST请求格式为:/list?sort-by=date
当用户发起请求 /list?sort_by=date 时,将参数名中的连字符替换成了下划线。此时请求能正常执行,但该参数会被系统忽略,转而使用默认排序规则(该参数为非必填项)。

使用Jackson处理JSON请求时,若传入包含无效成员的JSON会直接抛出异常。希望查询参数(最好同时支持请求头参数)也能实现类似的校验逻辑——即遇到未定义的参数时立即抛出异常。此前尝试过@BeanParam,但发现该场景下并未使用Jackson进行参数绑定。当前使用的RESTEasy版本为3.15.1。

解决方案

针对你的需求,在RESTEasy 3.15.1中可以通过以下方式实现参数校验:

1. 自定义请求过滤器拦截无效参数

通过实现ContainerRequestFilter,在请求匹配资源前遍历所有参数,与接口定义的合法参数比对,发现未定义参数时抛出异常。

实现步骤

  • 创建过滤器类并标注@Provider和@PreMatching(确保在资源匹配前执行校验)。
  • 借助ResourceInfo获取当前请求对应的资源方法,收集所有通过@QueryParam、@HeaderParam定义的合法参数名。
  • 分别检查查询参数和请求头参数,若存在不在合法列表中的项,抛出BadRequestException。

示例代码

import javax.ws.rs.BadRequestException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;
import java.lang.reflect.Parameter;
import java.util.HashSet;
import java.util.Set;

@Provider
@PreMatching
public class InvalidParamValidationFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext requestContext) {
        Set<String> validParamNames = new HashSet<>();

        // 收集接口定义的合法查询参数和请求头参数名
        Parameter[] methodParams = resourceInfo.getResourceMethod().getParameters();
        for (Parameter param : methodParams) {
            if (param.isAnnotationPresent(javax.ws.rs.QueryParam.class)) {
                validParamNames.add(param.getAnnotation(javax.ws.rs.QueryParam.class).value());
            }
            if (param.isAnnotationPresent(javax.ws.rs.HeaderParam.class)) {
                validParamNames.add(param.getAnnotation(javax.ws.rs.HeaderParam.class).value());
            }
        }

        // 校验查询参数
        requestContext.getUriInfo().getQueryParameters().keySet().forEach(queryParam -> {
            if (!validParamNames.contains(queryParam)) {
                throw new BadRequestException("无效的查询参数: " + queryParam);
            }
        });

        // 校验请求头参数(注意:可根据需求过滤Host、User-Agent等标准请求头)
        requestContext.getHeaders().keySet().forEach(headerName -> {
            String lowerCaseHeader = headerName.toLowerCase();
            boolean isHeaderValid = validParamNames.stream()
                    .anyMatch(validName -> validName.toLowerCase().equals(lowerCaseHeader));
            if (!isHeaderValid) {
                throw new BadRequestException("无效的请求头参数: " + headerName);
            }
        });
    }
}

2. 基于@BeanParam结合Jackson实现校验

如果使用@BeanParam传递参数,可以自定义转换器,利用Jackson的FAIL_ON_UNKNOWN_PROPERTIES特性实现未知参数校验。

核心思路

  • 为@BeanParam对应的实体类添加@JsonIgnoreProperties(ignoreUnknown = false)注解,开启未知属性报错。
  • 实现ParamConverterProvider和ParamConverter,将查询参数转换为键值对后,用Jackson的ObjectMapper反序列化为实体类,触发未知参数异常。

示例代码片段

// BeanParam实体类
@JsonIgnoreProperties(ignoreUnknown = false)
public class ListSortParam {
    @QueryParam("sort-by")
    private String sortBy;

    // getter、setter
}

// 自定义转换器提供类
@Provider
public class BeanParamJacksonConverterProvider implements ParamConverterProvider {
    private final ObjectMapper objectMapper;

    public BeanParamJacksonConverterProvider() {
        this.objectMapper = new ObjectMapper();
        this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
    }

    @Override
    public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
        if (rawType.isAnnotationPresent(BeanParam.class)) {
            return new ParamConverter<T>() {
                @Override
                public T fromString(String value) {
                    // 将查询参数转换为Map,再反序列化为实体类(实际实现需处理参数解析)
                    Map<String, String> paramMap = parseQueryParams(value);
                    try {
                        return objectMapper.convertValue(paramMap, rawType);
                    } catch (IllegalArgumentException e) {
                        throw new BadRequestException("存在无效参数: " + e.getMessage());
                    }
                }

                @Override
                public String toString(T value) {
                    try {
                        return objectMapper.writeValueAsString(value);
                    } catch (JsonProcessingException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
        }
        return null;
    }

    private Map<String, String> parseQueryParams(String queryString) {
        // 实现查询字符串到Map的解析逻辑
        Map<String, String> paramMap = new HashMap<>();
        if (queryString == null || queryString.isEmpty()) {
            return paramMap;
        }
        String[] pairs = queryString.split("&");
        for (String pair : pairs) {
            String[] keyValue = pair.split("=");
            if (keyValue.length == 2) {
                paramMap.put(keyValue[0], keyValue[1]);
            }
        }
        return paramMap;
    }
}

注意事项

  • 若部分接口需要允许额外参数,可以自定义@AllowUnknownParams注解,在过滤器中判断并跳过校验。
  • 请求头校验时建议排除标准HTTP请求头(如Host、User-Agent等),避免误拦截。
  • RESTEasy 3.x中@PreMatching注解确保过滤器在资源匹配前执行,能及时拦截无效参数。

内容的提问来源于stack exchange,提问作者Claudio Weiler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 13:54:52