【问题标题】:Spring WebFlux filter to intercept request after request object deserializationSpring WebFlux过滤器在请求对象反序列化后拦截请求
【发布时间】:2020-05-21 19:52:43
【问题描述】:

我有一个响应式 WebFlux Spring Boot 应用程序(版本 2.2.3.RELEASE)。我已经定义了一个名为 SearchRequest 的 POJO,它在带注释的控制器中用于 GET 和 POST 请求:

@GetMapping(path = "/search")
Mono<ItemCollection> getItems(SearchRequest searchRequest);

@PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE)
Mono<ItemCollection> getItemsPost(@RequestBody SearchRequest searchRequest);

需要完成一些基本的验证,以及一些可能的转换。 SearchRequest 中的一些字段本身就是对象,例如:

public class SearchRequest {

    private Fields fields;
    ...
}

Fields 类如下所示:

public class Fields {

    private Set<String> include;
    private Set<String> exclude;
    ...
}

对于 HTTP POST 请求,用户可以简单地提交一个遵循此结构的 JSON 对象。对于 HTTP GET 请求,我正在处理的 API 规范允许您简单地提供一个 fields URL 参数,其中包含以“+”或“-”为前缀的字段名称数组。我创建了一个扩展 PropertyEditorSupport 的类来解析 URL 参数并填充 Fields 对象的包含和排除集。这是在我的控制器中使用@InitBinder 配置的:

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(Fields.class, "fields", fieldsPropertyEditor);
}

我的SearchRequest 对象支持使用bbox 值或intersects 值的地理空间查询,但是如果两者都指定,我需要返回400。我尝试了以下方法来实现此验证,但找不到我正在寻找的解决方案:

  1. 使用 WebDataBinder 注册另一个自定义编辑器 SearchRequest 未指定字段的类。
  2. WebDataBinder 注册验证器(均使用setValidatoraddValidators 方法)。
  3. 实现WebFilter

解决方案 1 似乎没有效果。编辑器永远不会被调用。在解决方案 2 中,每个请求都会调用 supports 方法,但永远不会调用 validate 方法。解决方案 3 可以工作,但需要我编写两组逻辑——一组用于 GET,一组用于 POST,因为 GET URL 参数与 POST 请求正文分开存储。另外,为了实际检查请求体,exchange.getRequest.getBody() 返回了一个DataBuffer,可以用来将请求体反序列化成一个对象,但是如果没有我之前提到的属性编辑器,我不能直接反序列化成SearchRequest(对于 GET 请求)。

我真的希望找到某种过滤器/转换器/转换器,我可以在之后将请求反序列化为SearchRequest 对象,但在调用控制器处理程序方法之前,但是我似乎找不到这样做的方法。

有人可以告知这是否可行,或者我唯一的选择是在WebFilter 中单独处理 GET/POST 请求吗?

【问题讨论】:

    标签: java spring-boot spring-mvc spring-webflux reactor-netty


    【解决方案1】:

    这不是我正在寻找的答案,但它似乎至少目前有效。基本上,我只是制作了一个自定义验证器,并在我的控制器方法中使用 @Valid 注释了我的 SearchRequest 参数。

    注释:

    @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = SingleGeometryValidator.class)
    @Documented
    public @interface SingleGeometry {
        String message () default "Request should not contain both `intersects` and `bbox`. Please choose one geometry to "
                + "search by.";
        Class<?>[] groups () default {};
        Class<? extends Payload>[] payload () default {};
    }
    

    验证器:

    public class SingleGeometryValidator implements ConstraintValidator<SingleGeometry, SearchRequest> {
    
        @Override
        public boolean isValid(SearchRequest searchRequest, ConstraintValidatorContext constraintValidatorContext) {
            return !(searchRequest.getBbox() != null && searchRequest.getIntersects() != null);
        }
    }
    

    请求对象:

    @Data
    @SingleGeometry
    public class SearchRequest {
    ...
    }
    

    控制器接口:

    @GetMapping(path = "/search")
    Mono<ItemCollection> getItems(@Valid SearchRequest searchRequest);
    
    @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE)
    Mono<ItemCollection> getItemsPost(@Valid @RequestBody SearchRequest searchRequest);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-31
      • 1970-01-01
      • 2019-07-23
      • 2018-10-31
      • 1970-01-01
      • 1970-01-01
      • 2013-03-17
      • 1970-01-01
      相关资源
      最近更新 更多