解決swagger和自定義參數(shù)解析器的功能沖突

源?/?? ? ? ??文/?
前情提要
@RequestBody使用的參數(shù)解析器RequestResponseBodyMethodProcessor優(yōu)先級高于我們自定義的參數(shù)解析器,所以為了正常使用,需要將@RequestBody?注解去掉。這就會導(dǎo)致swagger無法識別正確的參數(shù)類型,將請求體識別為Query Params,然后將body展開。
ModelAttribute類型(query標(biāo)志),而我們所期待的正確格式應(yīng)當(dāng)是如下樣子
問題產(chǎn)生的原因
spring mvc和swagger都對@RequestBody注解進(jìn)行了單獨的判定,功能上都依賴于該注解本身。springmvc對@RequestBody注解的依賴
@RequestBody?注解,對參數(shù)的反序列化會提前被RequestResponseBodyMethodProcessor攔截,自定義的參數(shù)解析器會失效。https://github.com/spring-projects/spring-framework/blob/5.2.x/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java#L111
@ReuqestBody注解的參數(shù)都支持解析,然后做序列化的操作。然而它在參數(shù)解析器列表中的優(yōu)先級比較高,自定義的參數(shù)解析器添加到參數(shù)解析器列表之后會排在它的后面,所以如果加上@RequestBody注解,自定義的參數(shù)解析器就失效了。@RequestBody注解https://github.com/spring-projects/spring-framework/blob/5.2.x/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java#L129
此案例中用到的自定義參數(shù)解析器為 HdxArgumentResolver
swagger對@Requestbody的依賴
@RequestBody注解有單獨判定!(感興趣的可以自行追蹤??)請求類型判定:也就是說 POST請求類型是哪種類型,這決定了入?yún)⑹欠駮鳛?/span>Request Parameter被展開參數(shù),也就是文中的第一張圖,整個model都被視為ModelAttribute展開了。Definition屬性值填充:這確保被@RequestBody注解修飾的入?yún)徽o@示,如文中第二張圖片所示。
請求類型判定
https://github.com/springfox/springfox/blob/2.9.2/springfox-spring-web/src/main/java/springfox/documentation/spring/web/readers/operation/OperationParameterReader.java#L151
RequestBody等常用注解進(jìn)行了單獨的判定,確保這些注解修飾的入?yún)⒉粫蛔鳛?/span>RequestParam展開。Definition屬性值填充
Definition屬性中填充了入?yún)ⅰ⒊鰠⒌葏?shù)類型,如果沒有相應(yīng)的Model定義,則swagger信息就會是不完整的,在瀏覽器頁面中的顯示也會是不全的。填充Definition的邏輯也依賴于@RequestBody注解。https://github.com/springfox/springfox/blob/2.9.2/springfox-spring-web/src/main/java/springfox/documentation/spring/web/readers/operation/OperationModelsProvider.java#L80
RequestBody注解和RequestPart注解修飾的入?yún)⒉艜唤邮者M(jìn)入Definition屬性。swagger功能依賴于@RequestBody注解,入?yún)⑷绻槐辉撟⒔庑揎棧瑒tswagger功能就會不完整,這和在springmvc中使用獨立的參數(shù)解析器功能不得使用@RequestBody注解矛盾。解決問題
springmvc中獨立的參數(shù)解析器功能和swagger功能上的沖突,一個要求不能加上@RequestBody注解,一個要求必須加上@RequestBody注解,所以解決方法上可以使用兩種方式從 springmvc入手,想辦法提高自定義參數(shù)解析器的優(yōu)先級,只要自定義的參數(shù)解析器優(yōu)先級比RequestResponseBodyMethodProcessor高,則就可以在自定義的參數(shù)上加上@RequestBody注解,swagger功能自然而然就能正常了。從 swagger入手,想辦法解決掉上面兩部分對@RequestBody的單獨判定,不修改springmvc相關(guān)功能也可以讓swagger功能正常。
springmvc功能可能會對以后的版本升級造成較大影響,這里決定利用切面修改原有的swagger對@RequestBody的兩個地方的行為,從而讓swagger功能正常。請求類型判定的邏輯調(diào)整
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public?@interface?NoSwaggerExpand?{
????/**
?????*?default?swagger?expand?disable
?????*?@see?OperationParameterReader#shouldExpand(springfox.documentation.service.ResolvedMethodParameter,?com.fasterxml.classmate.ResolvedType)
?????*/
????boolean?expand()?default?false;
}
????@ApiOperation(value?=?"demo",?notes?=?"demo")
????@PostMapping(value?=?"/test")
????public?Result<boolean>?test(@HdxDecrypt?@NoSwaggerExpand?@ApiParam(required?=?true)?ReqDTO?reqDTO)?{
????????try?{
????????????log.info(ObjectMapperFactory.getObjectMapper().writeValueAsString(reqDTO));
????????}?catch?(JsonProcessingException?e)?{
????????????log.error("",?e);
????????}
????????return?null;
????}
@Slf4j
@Aspect
@Component
public?class?SwaggerExpandAspect?{
????private?final?ModelAttributeParameterExpander?expander;
????private?final?EnumTypeDeterminer?enumTypeDeterminer;
????@Autowired
????private?DocumentationPluginsManager?pluginsManager;
????@Autowired
????public?SwaggerExpandAspect(
????????????ModelAttributeParameterExpander?expander,
????????????EnumTypeDeterminer?enumTypeDeterminer)?{
????????this.expander?=?expander;
????????this.enumTypeDeterminer?=?enumTypeDeterminer;
????}
????@Around("execution(*?springfox.documentation.spring.web.readers.operation.OperationParameterReader.apply(..))")
????public?Object?pointCut(ProceedingJoinPoint?point)?throws?Throwable?{
????????Object[]?args?=?point.getArgs();
????????OperationContext?context?=?(OperationContext)?args[0];
????????context.operationBuilder().parameters(context.getGlobalOperationParameters());
????????context.operationBuilder().parameters(readParameters(context));
????????return?null;
????}
????private?List?readParameters(final?OperationContext?context)? {
????????List?methodParameters?=?context.getParameters();
????????List?parameters?=?newArrayList();
????????for?(ResolvedMethodParameter?methodParameter?:?methodParameters)?{
????????????ResolvedType?alternate?=?context.alternateFor(methodParameter.getParameterType());
????????????if?(!shouldIgnore(methodParameter,?alternate,?context.getIgnorableParameterTypes()))?{
????????????????ParameterContext?parameterContext?=?new?ParameterContext(methodParameter,
????????????????????????new?ParameterBuilder(),
????????????????????????context.getDocumentationContext(),
????????????????????????context.getGenericsNamingStrategy(),
????????????????????????context);
????????????????if?(shouldExpand(methodParameter,?alternate))?{
????????????????????parameters.addAll(
????????????????????????????expander.expand(
????????????????????????????????????new?ExpansionContext("",?alternate,?context)));
????????????????}?else?{
????????????????????parameters.add(pluginsManager.parameter(parameterContext));
????????????????}
????????????}
????????}
????????return?FluentIterable.from(parameters).filter(not(hiddenParams())).toList();
????}
????private?Predicate?hiddenParams()? {
????????return?new?Predicate()?{
????????????@Override
????????????public?boolean?apply(Parameter?input)?{
????????????????return?input.isHidden();
????????????}
????????};
????}
????private?boolean?shouldIgnore(
????????????final?ResolvedMethodParameter?parameter,
????????????ResolvedType?resolvedParameterType,
????????????final?Set?ignorableParamTypes) ?{
????????if?(ignorableParamTypes.contains(resolvedParameterType.getErasedType()))?{
????????????return?true;
????????}
????????return?FluentIterable.from(ignorableParamTypes)
????????????????.filter(isAnnotation())
????????????????.filter(parameterIsAnnotatedWithIt(parameter)).size()?>?0;
????}
????private?Predicate?parameterIsAnnotatedWithIt(final?ResolvedMethodParameter?parameter)? {
????????return?new?Predicate<class>()?{
????????????@Override
????????????public?boolean?apply(Class?input)?{
????????????????return?parameter.hasParameterAnnotation(input);
????????????}
????????};
????}
????private?Predicate?isAnnotation()? {
????????return?new?Predicate<class>()?{
????????????@Override
????????????public?boolean?apply(Class?input)?{
????????????????return?Annotation.class.isAssignableFrom(input);
????????????}
????????};
????}
????private?boolean?shouldExpand(final?ResolvedMethodParameter?parameter,?ResolvedType?resolvedParamType)?{
????????return?!parameter.hasParameterAnnotation(RequestBody.class)
????????????????&&?!parameter.hasParameterAnnotation(RequestPart.class)
????????????????&&?!parameter.hasParameterAnnotation(RequestParam.class)
????????????????&&?!parameter.hasParameterAnnotation(PathVariable.class)
????????????????&&?!isBaseType(typeNameFor(resolvedParamType.getErasedType()))
????????????????&&?!enumTypeDeterminer.isEnum(resolvedParamType.getErasedType())
????????????????&&?!isContainerType(resolvedParamType)
????????????????&&?!isMapType(resolvedParamType)
????????????????&&?!noExpandAnnotaion(parameter);
????}
????private?boolean?noExpandAnnotaion(ResolvedMethodParameter?parameter)?{
????????log.info("開始決定是否展開問題");
????????if?(!parameter.hasParameterAnnotation(NoSwaggerExpand.class))?{
????????????return?false;
????????}
????????NoSwaggerExpand?noSwaggerExpand?=?(NoSwaggerExpand)?parameter.getAnnotations().stream().filter(item?->?item?instanceof?NoSwaggerExpand).findAny().orElse(null);
????????if?(noSwaggerExpand.expand())?{
????????????return?false;
????????}
????????return?true;
????}
}

Swagger當(dāng)做@RequestBody一樣處理。Definition屬性值填充的邏輯調(diào)整
@Slf4j
@Aspect
@Component
public?class?SwaggerDefinitionAspect?{
????private?static?final?Logger?LOG?=?LoggerFactory.getLogger(OperationModelsProvider.class);
????private?final?TypeResolver?typeResolver;
????@Autowired
????public?SwaggerDefinitionAspect(TypeResolver?typeResolver)?{
????????this.typeResolver?=?typeResolver;
????}
????
????@Around("execution(*?springfox.documentation.spring.web.readers.operation.OperationModelsProvider.apply(..))")
????public?Object?pointCut(ProceedingJoinPoint?point)?throws?Throwable?{
????????Object[]?args?=?point.getArgs();
????????RequestMappingContext?context?=?(RequestMappingContext)?args[0];
????????collectFromReturnType(context);
????????collectParameters(context);
????????collectGlobalModels(context);
????????return?null;
????}
????
????private?void?collectGlobalModels(RequestMappingContext?context)?{
????????for?(ResolvedType?each?:?context.getAdditionalModels())?{
????????????context.operationModelsBuilder().addInputParam(each);
????????????context.operationModelsBuilder().addReturn(each);
????????}
????}
????private?void?collectFromReturnType(RequestMappingContext?context)?{
????????ResolvedType?modelType?=?context.getReturnType();
????????modelType?=?context.alternateFor(modelType);
????????LOG.debug("Adding?return?parameter?of?type?{}",?resolvedTypeSignature(modelType).or("" ));
????????context.operationModelsBuilder().addReturn(modelType);
????}
????private?void?collectParameters(RequestMappingContext?context)?{
????????LOG.debug("Reading?parameters?models?for?handlerMethod?|{}|",?context.getName());
????????List?parameterTypes?=?context.getParameters();
????????for?(ResolvedMethodParameter?parameterType?:?parameterTypes)?{
????????????if?(parameterType.hasParameterAnnotation(RequestBody.class)
????????????????????||?parameterType.hasParameterAnnotation(RequestPart.class)
????????????||?parameterType.hasParameterAnnotation(NoSwaggerExpand.class)
????????????)?{
????????????????ResolvedType?modelType?=?context.alternateFor(parameterType.getParameterType());
????????????????LOG.debug("Adding?input?parameter?of?type?{}",?resolvedTypeSignature(modelType).or("" ));
????????????????context.operationModelsBuilder().addInputParam(modelType);
????????????}
????????}
????????LOG.debug("Finished?reading?parameters?models?for?handlerMethod?|{}|",?context.getName());
????}
}
Definition屬性中去。springmvc獨立的參數(shù)解析器功能和swagger功能沖突的問題。idea,歡迎聯(lián)系阿Q,添加阿Q可以加入技術(shù)交流群參與討論呦!
END
最后給大家來個福利:
一鍵三連「分享」、「點贊」和「在看」
評論
圖片
表情
