一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 實戰篇:解決 Swagger 和自定義參數解析器的功能沖突

實戰篇:解決 Swagger 和自定義參數解析器的功能沖突

2022-01-06 20:58阿Q說代碼kdyzm Java教程

在上文中提到過,@RequestBody使用的參數解析器RequestResponseBodyMethodProcessor優先級高于我們自定義的參數解析器,所以為了正常使用,需要將@RequestBody 注解去掉。這就會導致swagger無法識別正確的參數類型,將請求體識別為Query Params,

實戰篇:解決 Swagger 和自定義參數解析器的功能沖突

前情提要

看了上一篇文章看了同事寫的代碼,我竟然開始默默的模仿了。。。的小伙伴,應該已經對使用參數解析器來完成第三方接口的統一驗簽有了清晰的認識。

我們在上文中提到過,@RequestBody使用的參數解析器RequestResponseBodyMethodProcessor優先級高于我們自定義的參數解析器,所以為了正常使用,需要將@RequestBody 注解去掉。這就會導致swagger無法識別正確的參數類型,將請求體識別為Query Params,然后將body展開。

實戰篇:解決 Swagger 和自定義參數解析器的功能沖突

可以看到,所有參數都被識別為ModelAttribute類型(query標志),而我們所期待的正確格式應當是如下樣子

實戰篇:解決 Swagger 和自定義參數解析器的功能沖突

因為該方式可以大大提高代碼的可讀性和可復用性,所以我們要知難而上,找出問題,解決問題!

問題產生的原因

產生這個問題的根本原因就是spring mvc和swagger都對@RequestBody注解進行了單獨的判定,功能上都依賴于該注解本身。

springmvc對@RequestBody注解的依賴

就拿當前自定義的參數解析器來說,如果對請求參數加上了 @RequestBody 注解,對參數的反序列化會提前被RequestResponseBodyMethodProcessor攔截,自定義的參數解析器會失效。

具體源代碼位置: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

實戰篇:解決 Swagger 和自定義參數解析器的功能沖突

可以看到,該參數解析器對加上@ReuqestBody注解的參數都支持解析,然后做序列化的操作。然而它在參數解析器列表中的優先級比較高,自定義的參數解析器添加到參數解析器列表之后會排在它的后面,所以如果加上@RequestBody注解,自定義的參數解析器就失效了。

因此使用自定義參數解析器一定不能使用@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

實戰篇:解決 Swagger 和自定義參數解析器的功能沖突

此案例中用到的自定義參數解析器為HdxArgumentResolver

swagger對@Requestbody的依賴

經過調用棧追蹤,最終發現在兩個地方的功能會對@RequestBody注解有單獨判定!(感興趣的可以自行追蹤??)

  • 請求類型判定:也就是說POST請求類型是哪種類型,這決定了入參是否會作為Request Parameter被展開參數,也就是文中的第一張圖,整個model都被視為ModelAttribute展開了。
  • Definition屬性值填充:這確保被@RequestBody注解修飾的入參會被正常顯示,如文中第二張圖片所示。

請求類型判定

源代碼位置:https://github.com/springfox/springfox/blob/2.9.2/springfox-spring-web/src/main/java/springfox/documentation/spring/web/readers/operation/OperationParameterReader.java#L151

實戰篇:解決 Swagger 和自定義參數解析器的功能沖突

這里對RequestBody等常用注解進行了單獨的判定,確保這些注解修飾的入參不會被作為RequestParam展開。

Definition屬性值填充

Definition屬性中填充了入參、出參等參數類型,如果沒有相應的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

實戰篇:解決 Swagger 和自定義參數解析器的功能沖突

可以看到,只有被RequestBody注解和RequestPart注解修飾的入參才會被接收進入Definition屬性。

綜合以上兩張圖的源代碼分析,可以看到,swagger功能依賴于@RequestBody注解,入參如果不被該注解修飾,則swagger功能就會不完整,這和在springmvc中使用獨立的參數解析器功能不得使用@RequestBody注解矛盾。

解決問題

從以上分析可以得到結論,這里的根本問題是springmvc中獨立的參數解析器功能和swagger功能上的沖突,一個要求不能加上@RequestBody注解,一個要求必須加上@RequestBody注解,所以解決方法上可以使用兩種方式

  • 從springmvc入手,想辦法提高自定義參數解析器的優先級,只要自定義的參數解析器優先級比RequestResponseBodyMethodProcessor高,則就可以在自定義的參數上加上@RequestBody注解,swagger功能自然而然就能正常了。
  • 從swagger入手,想辦法解決掉上面兩部分對@RequestBody的單獨判定,不修改springmvc相關功能也可以讓swagger功能正常。

考慮到修改springmvc功能可能會對以后的版本升級造成較大影響,這里決定利用切面修改原有的swagger對@RequestBody的兩個地方的行為,從而讓swagger功能正常。

請求類型判定的邏輯調整

首先,定義一個注解

  1. @Documented 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Target({ElementType.PARAMETER}) 
  4. public @interface NoSwaggerExpand { 
  5.  
  6.     /** 
  7.      * default swagger expand disable 
  8.      * @see OperationParameterReader#shouldExpand(springfox.documentation.service.ResolvedMethodParameter, com.fasterxml.classmate.ResolvedType) 
  9.      */ 
  10.     boolean expand() default false

將其加到入參上

  1. @ApiOperation(value = "demo", notes = "demo"
  2.   @PostMapping(value = "/test"
  3.   public Result<boolean> test(@HdxDecrypt @NoSwaggerExpand @ApiParam(required = true) ReqDTO reqDTO) { 
  4.       try { 
  5.           log.info(ObjectMapperFactory.getObjectMapper().writeValueAsString(reqDTO)); 
  6.       } catch (JsonProcessingException e) { 
  7.           log.error("", e); 
  8.       } 
  9.       return null
  10.   } 

然后定義切面

  1. @Slf4j 
  2. @Aspect 
  3. @Component 
  4. public class SwaggerExpandAspect { 
  5.  
  6.     private final ModelAttributeParameterExpander expander; 
  7.     private final EnumTypeDeterminer enumTypeDeterminer; 
  8.  
  9.     @Autowired 
  10.     private DocumentationPluginsManager pluginsManager; 
  11.  
  12.     @Autowired 
  13.     public SwaggerExpandAspect( 
  14.             ModelAttributeParameterExpander expander, 
  15.             EnumTypeDeterminer enumTypeDeterminer) { 
  16.         this.expander = expander; 
  17.         this.enumTypeDeterminer = enumTypeDeterminer; 
  18.     } 
  19.  
  20.     @Around("execution(* springfox.documentation.spring.web.readers.operation.OperationParameterReader.apply(..))"
  21.     public Object pointCut(ProceedingJoinPoint point) throws Throwable { 
  22.         Object[] args = point.getArgs(); 
  23.         OperationContext context = (OperationContext) args[0]; 
  24.         context.operationBuilder().parameters(context.getGlobalOperationParameters()); 
  25.         context.operationBuilder().parameters(readParameters(context)); 
  26.         return null
  27.     } 
  28.  
  29.     private List<parameter> readParameters(final OperationContext context) { 
  30.  
  31.         List<resolvedmethodparameter> methodParameters = context.getParameters(); 
  32.         List<parameter> parameters = newArrayList(); 
  33.  
  34.         for (ResolvedMethodParameter methodParameter : methodParameters) { 
  35.             ResolvedType alternate = context.alternateFor(methodParameter.getParameterType()); 
  36.             if (!shouldIgnore(methodParameter, alternate, context.getIgnorableParameterTypes())) { 
  37.  
  38.                 ParameterContext parameterContext = new ParameterContext(methodParameter, 
  39.                         new ParameterBuilder(), 
  40.                         context.getDocumentationContext(), 
  41.                         context.getGenericsNamingStrategy(), 
  42.                         context); 
  43.  
  44.                 if (shouldExpand(methodParameter, alternate)) { 
  45.                     parameters.addAll( 
  46.                             expander.expand( 
  47.                                     new ExpansionContext("", alternate, context))); 
  48.                 } else { 
  49.                     parameters.add(pluginsManager.parameter(parameterContext)); 
  50.                 } 
  51.             } 
  52.         } 
  53.         return FluentIterable.from(parameters).filter(not(hiddenParams())).toList(); 
  54.     } 
  55.  
  56.  
  57.     private Predicate<parameter> hiddenParams() { 
  58.         return new Predicate<parameter>() { 
  59.             @Override 
  60.             public boolean apply(Parameter input) { 
  61.                 return input.isHidden(); 
  62.             } 
  63.         }; 
  64.     } 
  65.  
  66.     private boolean shouldIgnore( 
  67.             final ResolvedMethodParameter parameter, 
  68.             ResolvedType resolvedParameterType, 
  69.             final Set<class> ignorableParamTypes) { 
  70.  
  71.         if (ignorableParamTypes.contains(resolvedParameterType.getErasedType())) { 
  72.             return true
  73.         } 
  74.         return FluentIterable.from(ignorableParamTypes) 
  75.                 .filter(isAnnotation()) 
  76.                 .filter(parameterIsAnnotatedWithIt(parameter)).size() > 0; 
  77.  
  78.     } 
  79.  
  80.     private Predicate<class> parameterIsAnnotatedWithIt(final ResolvedMethodParameter parameter) { 
  81.         return new Predicate<class>() { 
  82.             @Override 
  83.             public boolean apply(Class input) { 
  84.                 return parameter.hasParameterAnnotation(input); 
  85.             } 
  86.         }; 
  87.     } 
  88.  
  89.     private Predicate<class> isAnnotation() { 
  90.         return new Predicate<class>() { 
  91.             @Override 
  92.             public boolean apply(Class input) { 
  93.                 return Annotation.class.isAssignableFrom(input); 
  94.             } 
  95.         }; 
  96.     } 
  97.  
  98.     private boolean shouldExpand(final ResolvedMethodParameter parameter, ResolvedType resolvedParamType) { 
  99.         return !parameter.hasParameterAnnotation(RequestBody.class) 
  100.                 && !parameter.hasParameterAnnotation(RequestPart.class) 
  101.                 && !parameter.hasParameterAnnotation(RequestParam.class) 
  102.                 && !parameter.hasParameterAnnotation(PathVariable.class) 
  103.                 && !isBaseType(typeNameFor(resolvedParamType.getErasedType())) 
  104.                 && !enumTypeDeterminer.isEnum(resolvedParamType.getErasedType()) 
  105.                 && !isContainerType(resolvedParamType) 
  106.                 && !isMapType(resolvedParamType) 
  107.                 && !noExpandAnnotaion(parameter); 
  108.  
  109.     } 
  110.  
  111.     private boolean noExpandAnnotaion(ResolvedMethodParameter parameter) { 
  112.         log.info("開始決定是否展開問題"); 
  113.         if (!parameter.hasParameterAnnotation(NoSwaggerExpand.class)) { 
  114.             return false
  115.         } 
  116.         NoSwaggerExpand noSwaggerExpand = (NoSwaggerExpand) parameter.getAnnotations().stream().filter(item -> item instanceof NoSwaggerExpand).findAny().orElse(null); 
  117.         if (noSwaggerExpand.expand()) { 
  118.             return false
  119.         } 
  120.         return true
  121.     } 
  122.  

最重要的是這里的修改

實戰篇:解決 Swagger 和自定義參數解析器的功能沖突

這里加上對自定義注解修飾的入參進行了判定,使得被自定義注解修飾的入參可以被Swagger當做@RequestBody一樣處理。

Definition屬性值填充的邏輯調整

再定義一個切面

  1. @Slf4j 
  2. @Aspect 
  3. @Component 
  4. public class SwaggerDefinitionAspect { 
  5.  
  6.     private static final Logger LOG = LoggerFactory.getLogger(OperationModelsProvider.class); 
  7.     private final TypeResolver typeResolver; 
  8.  
  9.     @Autowired 
  10.     public SwaggerDefinitionAspect(TypeResolver typeResolver) { 
  11.         this.typeResolver = typeResolver; 
  12.     } 
  13.  
  14.      
  15.     @Around("execution(* springfox.documentation.spring.web.readers.operation.OperationModelsProvider.apply(..))"
  16.     public Object pointCut(ProceedingJoinPoint point) throws Throwable { 
  17.         Object[] args = point.getArgs(); 
  18.         RequestMappingContext context = (RequestMappingContext) args[0]; 
  19.         collectFromReturnType(context); 
  20.         collectParameters(context); 
  21.         collectGlobalModels(context); 
  22.         return null
  23.     } 
  24.      
  25.     private void collectGlobalModels(RequestMappingContext context) { 
  26.         for (ResolvedType each : context.getAdditionalModels()) { 
  27.             context.operationModelsBuilder().addInputParam(each); 
  28.             context.operationModelsBuilder().addReturn(each); 
  29.         } 
  30.     } 
  31.  
  32.     private void collectFromReturnType(RequestMappingContext context) { 
  33.         ResolvedType modelType = context.getReturnType(); 
  34.         modelType = context.alternateFor(modelType); 
  35.         LOG.debug("Adding return parameter of type {}", resolvedTypeSignature(modelType).or("<null>")); 
  36.         context.operationModelsBuilder().addReturn(modelType); 
  37.     } 
  38.  
  39.     private void collectParameters(RequestMappingContext context) { 
  40.  
  41.  
  42.         LOG.debug("Reading parameters models for handlerMethod |{}|", context.getName()); 
  43.  
  44.         List<resolvedmethodparameter> parameterTypes = context.getParameters(); 
  45.         for (ResolvedMethodParameter parameterType : parameterTypes) { 
  46.             if (parameterType.hasParameterAnnotation(RequestBody.class) 
  47.                     || parameterType.hasParameterAnnotation(RequestPart.class) 
  48.             || parameterType.hasParameterAnnotation(NoSwaggerExpand.class) 
  49.             ) { 
  50.                 ResolvedType modelType = context.alternateFor(parameterType.getParameterType()); 
  51.                 LOG.debug("Adding input parameter of type {}", resolvedTypeSignature(modelType).or("<null>")); 
  52.                 context.operationModelsBuilder().addInputParam(modelType); 
  53.             } 
  54.         } 
  55.         LOG.debug("Finished reading parameters models for handlerMethod |{}|", context.getName()); 
  56.     } 

在這里只改動了一處代碼,使得被自定義注解修飾的入參能夠被添加到Definition屬性中去。

做完以上兩步,即可修復springmvc獨立的參數解析器功能和swagger功能沖突的問題。

原文鏈接:https://mp.weixin.qq.com/s/1DySbQnacMFzaOxzUJFnjw

延伸 · 閱讀

精彩推薦
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7482021-02-04
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
主站蜘蛛池模板: 毛片小视频 | 国产在线看片护士免费视频 | 精品第一国产综合精品蜜芽 | 蘑菇香蕉茄子绿巨人丝瓜草莓 | 国产毛片一级aaaaa片 | 精品国产欧美一区二区五十路 | 91亚洲精品第一综合不卡播放 | 国产免费小视频 | 亚洲第一区二区快射影院 | 亚洲第6页 | 久久中文字幕乱码免费 | 欧美第一视频 | 99热这里只有精品国产免费 | 国产精品视频在线观看 | 成3d漫二区三区四区 | 国产一区二区三区毛片 | 92国产福利视频一区二区 | 亚洲国产成人久久精品hezyo | www在线免费观看 | 2020韩国r级理论片在线观看 | 99久久精品国产免费 | 91手机看片国产永久免费 | 青草香蕉精品视频在线观看 | 精品日韩欧美一区二区三区在线播放 | freefron性中国国产高清 | ts人妖国产一区 | 欧美 亚洲 一区 | 日本在线不卡免 | 国内精品视频九九九九 | 亚洲国产精品综合久久一线 | 欧美日韩精品一区二区三区视频 | 四虎成人免费大片在线 | 爆操女友 | 福利久草 | 天堂网站天堂小说 | 国产精品吹潮香蕉在线观看 | 欧美亚洲另类在线观看 | pron在线观看| 美女操批 | 亚久久伊人精品青青草原2020 | 欧美成人aletta ocean |