View Javadoc
1   package cn.home1.oss.lib.security.swagger;
2   
3   import static com.google.common.collect.Sets.newHashSet;
4   import static springfox.documentation.schema.ResolvedTypes.modelRefFactory;
5   
6   import cn.home1.oss.lib.errorhandle.api.ResolvedError;
7   import cn.home1.oss.lib.swagger.ManualRequestHandler;
8   import cn.home1.oss.lib.swagger.model.ApiOperationInfo;
9   
10  import com.fasterxml.classmate.TypeResolver;
11  
12  import lombok.extern.slf4j.Slf4j;
13  
14  import org.springframework.beans.factory.annotation.Autowired;
15  import org.springframework.http.HttpStatus;
16  
17  import springfox.documentation.RequestHandler;
18  import springfox.documentation.builders.OperationBuilder;
19  import springfox.documentation.builders.ResponseMessageBuilder;
20  import springfox.documentation.schema.ModelReference;
21  import springfox.documentation.schema.TypeNameExtractor;
22  import springfox.documentation.service.ResponseMessage;
23  import springfox.documentation.spi.DocumentationType;
24  import springfox.documentation.spi.schema.contexts.ModelContext;
25  import springfox.documentation.spi.service.OperationBuilderPlugin;
26  import springfox.documentation.spi.service.contexts.OperationContext;
27  import springfox.documentation.spi.service.contexts.RequestMappingContext;
28  
29  import java.lang.reflect.Field;
30  import java.util.Set;
31  
32  /**
33   * Created on 16/11/1. Desc : Run after scanning operationBuilder plugin
34   */
35  @Slf4j
36  abstract public class AbstractAfterOperationBuilderBuildPlugin implements OperationBuilderPlugin {
37  
38    private static final String MANUAL_REQUEST_HANDLER = ManualRequestHandler.class.getSimpleName();
39  
40    @Autowired
41    private TypeNameExtractor nameExtractor;
42  
43    @Autowired
44    protected TypeResolver typeResolver;
45  
46    @Override
47    public void apply(final OperationContext context) {
48      try {
49        // 处理自定义的请求处理器
50        if (MANUAL_REQUEST_HANDLER.equals(context.getName())) {
51  
52          // get requestContext
53          Field privateStringField = OperationContext.class.getDeclaredField("requestContext");
54          privateStringField.setAccessible(true);
55          final RequestMappingContext requestMappingContext = //
56            (RequestMappingContext) privateStringField.get(context);
57  
58          // get handler
59          privateStringField = RequestMappingContext.class.getDeclaredField("handler");
60          privateStringField.setAccessible(true);
61          final RequestHandler requestHandler = (RequestHandler) privateStringField.get(requestMappingContext);
62  
63          if (requestHandler instanceof ManualRequestHandler) {
64            this.processRequestHandler(context, (ManualRequestHandler) requestHandler);
65          }
66        }
67  
68        // 处理响应的错误码对应的ResolveError
69        processResponseModel(context);
70      } catch (final ReflectiveOperationException ex) {
71        log.error("swagger plugin error.", ex);
72      }
73    }
74  
75    private void processRequestHandler(final OperationContext context, final ManualRequestHandler manualRequestHandler) {
76      final ApiOperationInfo apiOperationInfo = manualRequestHandler.getApiOperationInfo();
77  
78      if (apiOperationInfo != null) {
79        // 接口描述
80        context.operationBuilder().notes(apiOperationInfo.getNotes());
81        // 请求参数处理
82        if (apiOperationInfo.getApiRequest().getParameters() != null) {
83          context.operationBuilder().parameters(apiOperationInfo.getApiRequest().getParameters());
84        }
85      }
86    }
87  
88    /**
89     * 为错误码的响应增加Model的映射 @cn.home1.oss.lib.errorhandle.api.ResolvedError
90     */
91    private void processResponseModel(final OperationContext context) {
92      try {
93        final OperationBuilder operationBuilder = context.operationBuilder();
94        final Field privateStringField = OperationBuilder.class.getDeclaredField("responseMessages");
95        privateStringField.setAccessible(true);
96        final Set<ResponseMessage> responseMessages = (Set<ResponseMessage>) privateStringField.get(operationBuilder);
97  
98        final ModelContext modelContext = this.modelContext(context);
99        final ModelReference responseModel = modelRefFactory(modelContext, this.nameExtractor) //
100         .apply(context.alternateFor(this.typeResolver.resolve(ResolvedError.class)));
101 
102       final Set<ResponseMessage> responseMessagesProcessed = newHashSet();
103       responseMessages.forEach(responseMessage -> {
104         if (!isSuccessful(responseMessage.getCode())) {
105           responseMessagesProcessed.add(new ResponseMessageBuilder()
106             .code(responseMessage.getCode())
107             .message(responseMessage.getMessage())
108             .responseModel(responseModel)
109             .build());
110         }
111       });
112       if (!responseMessagesProcessed.isEmpty()) {
113         context.operationBuilder().responseMessages(responseMessagesProcessed);
114       }
115     } catch (final NoSuchFieldException | IllegalAccessException ex) {
116       log.info("error processResponseModel.", ex);
117     }
118   }
119 
120   abstract protected ModelContext modelContext(OperationContext context);
121 
122   /**
123    * 判断是否是成功的响应.
124    */
125   private static boolean isSuccessful(final int code) {
126     boolean result;
127     try {
128       result = HttpStatus.Series.SUCCESSFUL.equals(HttpStatus.Series.valueOf(code));
129     } catch (final Exception ignored) {
130       log.info("error check httpStatus {}.", code, ignored);
131       result = false;
132     }
133     return result;
134   }
135 
136   @Override
137   public boolean supports(final DocumentationType delimiter) {
138     return DocumentationType.SWAGGER_2.equals(delimiter);
139   }
140 }