关于WebFlux

Spring在2017年下半年迎来了Webflux,Webflux的出现填补了Spring在响应式编程上的空白,在一些特殊的场景下Webflux体现出了不俗的性能。那到底Webflux和WebMVC有什么区别呢。具体webflux文档参考官网文档。集成教程参考Swagger2 WebFlux小试牛刀,这里就不在介绍如何集成,只解决一些使用中的问题。

Mono和Flux返回swagger不能正确识别的问题

写两个测试接口:

    //同步写法
    @GetMapping("/message")
    public BaseResponse<ObjectMessage> messageBaseResponse() {
        return BaseResponse.ok(new ObjectMessage("hello world"));
    }
    //异步写法
    @GetMapping("/reactive_message")
    public Mono<BaseResponse<ObjectMessage>> reactiveMessageBaseResponse() {
        return Mono.create(sink -> sink.success(BaseResponse.ok(new ObjectMessage("hello world"))));
    }

同步写法在swagger显示

显示正常,返回解析也没问题。

异步写法在swagger显示

异步接口,response显示不正确。

查看model,类型正确,但是解析不正确。

问题分析

对比两个接口在swagger页面上的区别发现,同步异步在model的显示不同,那么替换掉异步model试试,只要将Mono<<>>去掉就和同步的model一样了。

源码分析

swagger源码中在ServiceModelToSwagger2MapperImpl的mapDocumentation方法将生产Swagger对象,Swagger包含了接口model值。

ServiceModelToSwagger2MapperImpl使用了@Component注解,由spring ioc管理了ServiceModelToSwagger2MapperImpl。既然由spring管理了这个bean那么事情简单了,使用Spring aop拦截mapDocumentation方法,将Swagger对象修改一下去掉Mono<<>>就可以了。

解决办法

写一个aop,环绕mapDocumentation方法,修改其返回值,将Mono<<>>去掉。

/**
 * Classname ReactiveServiceModelToSwagger2MapperAop
 * Description 修复Publisher的泛型变量mapping到swagger
 * Date 2019/5/23 4:36 PM
 * Created by Wang jun gang
 */
@Aspect
@Component
public class ReactiveServiceModelToSwagger2MapperAop {

    @Pointcut("execution(* springfox.documentation.swagger2.mappers.ServiceModelToSwagger2MapperImpl.mapDocumentation(..))")
    public void p() {
    }

    @Around("p()")
    public Object around(ProceedingJoinPoint pdj) throws Throwable {
        final Object proceed = pdj.proceed();
        if (proceed instanceof Swagger) {
            this.fixRef((Swagger) proceed);
        }
        return proceed;
    }

    @SuppressWarnings("deprecation")
    private Swagger fixRef(Swagger swagger) {
        swagger.getPaths().forEach((k, v) -> {
            final Operation get = v.getGet();
            if (Objects.nonNull(get)) {
                get.getResponses().forEach((k2, v2) -> this.fixSchema(v2.getSchema()));
            }
            final Operation post = v.getPost();
            if (Objects.nonNull(post)) {
                post.getResponses().forEach((k2, v2) -> this.fixSchema(v2.getSchema()));
            }

            final Operation put = v.getPut();
            if (Objects.nonNull(put))
                put.getResponses().forEach((k2, v2) -> this.fixSchema(v2.getSchema()));

            final Operation patch = v.getPatch();
            if (Objects.nonNull(patch))
                patch.getResponses().forEach((k2, v2) -> this.fixSchema(v2.getSchema()));
        });

        return swagger;
    }

    private void fixSchema(Property schema) {
        if (schema instanceof RefProperty) {
            final RefProperty refProperty = (RefProperty) schema;
            if (refProperty.getSimpleRef().startsWith("Mono«")) {
                final String replaced = refProperty.getSimpleRef()
                        .replaceFirst("Mono«", "")
                        .replaceAll("»$", "");
                refProperty.set$ref("#/definitions/" + replaced);
            } else if (refProperty.getSimpleRef().startsWith("Flux«")) {
                final String replaced = refProperty.getSimpleRef()
                        .replaceFirst("Flux«", "")
                        .replaceAll("»$", "");
                refProperty.set$ref("#/definitions/" + replaced);
            } else {
                refProperty.set$ref(refProperty.get$ref()
                        .replace("definitions/MonoOf", "definitions/")
                        .replace("definitions/FluxOf", "definitions/"));
            }
        }
    }
}

结果和总结

结果

重启应用打开swagger页面查看接口,异步接口显示正常了。

总结

swagger大部分bean由spring 管理,所以可以使用spring的ioc和aop来修改swagger的默认实现。

发表评论

电子邮件地址不会被公开。 必填项已用*标注