如何让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
相关产品推荐
相关产品推荐

