001/** 002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.jboot.support.swagger; 017 018import com.jfinal.core.ActionKey; 019import com.jfinal.core.Controller; 020import io.jboot.web.controller.JbootControllerManager; 021import io.swagger.models.Operation; 022import io.swagger.models.Path; 023import io.swagger.models.Response; 024import io.swagger.models.Swagger; 025import io.swagger.util.PathUtils; 026import io.swagger.util.ReflectionUtils; 027 028import java.lang.annotation.Annotation; 029import java.lang.reflect.Method; 030import java.lang.reflect.Type; 031import java.util.HashMap; 032import java.util.List; 033import java.util.Map; 034 035/** 036 * 参考 : https://github.com/swagger-api/swagger-core 的 servlet 模块 037 */ 038public class Reader { 039 040 private final Swagger swagger; 041 042 private Reader(Swagger swagger) { 043 this.swagger = swagger; 044 } 045 046 /** 047 * Scans a set of classes for Swagger annotations. 048 * 049 * @param swagger is the Swagger instance 050 * @param classes are a set of classes to scan 051 */ 052 public static void read(Swagger swagger, List<Class> classes) { 053 final Reader reader = new Reader(swagger); 054 for (Class cls : classes) { 055 ReaderContext context = new ReaderContext(cls, "", null, false); 056 reader.read(context); 057 } 058 } 059 060 private void read(ReaderContext context) { 061 062 for (Method method : context.getCls().getDeclaredMethods()) { 063 if (ReflectionUtils.isOverriddenMethod(method, context.getCls())) { 064 continue; 065 } 066 final Operation operation = new Operation(); 067 068 069 final Type[] genericParameterTypes = method.getGenericParameterTypes(); 070 final Annotation[][] paramAnnotations = method.getParameterAnnotations(); 071 072 ControllerReaderExtension extension = new ControllerReaderExtension(); 073 074 String methodPath = "index".equals(method.getName()) ? "" : "/" + method.getName(); 075 String operationPath = JbootControllerManager.me().getPathByController((Class<? extends Controller>) context.getCls()) + methodPath; 076 //如果有ActionKey注解的URL路径,则使用该路径而不是方法名 077 ActionKey actionKeyAnnotation = ReflectionUtils.getAnnotation(method, ActionKey.class); 078 if (actionKeyAnnotation != null && !actionKeyAnnotation.value().isEmpty()) { 079 if (actionKeyAnnotation.value().startsWith("./")) { 080 String actionName = actionKeyAnnotation.value().substring(2); 081 String pathByController = JbootControllerManager.me().getPathByController((Class<? extends Controller>) context.getCls()); 082 operationPath = pathByController.endsWith("/") ? (pathByController + actionName) : (pathByController + "/" + actionName); 083 } else { 084 operationPath = actionKeyAnnotation.value(); 085 } 086 } 087 String httpMethod = extension.getHttpMethod(context, method); 088 089 if (operationPath == null || httpMethod == null) { 090 continue; 091 } 092 093 if (extension.isReadable(context)) { 094 extension.setDeprecated(operation, method); 095 extension.applyConsumes(context, operation, method); 096 extension.applyProduces(context, operation, method); 097 extension.applyOperationId(operation, method); 098 extension.applySummary(operation, method); 099 extension.applyDescription(operation, method); 100 extension.applySchemes(context, operation, method); 101 extension.applySecurityRequirements(context, operation, method); 102 extension.applyTags(context, operation, method); 103 extension.applyResponses(swagger, context, operation, method); 104 extension.applyImplicitParameters(swagger, context, operation, method); 105 extension.applyExtensions(context, operation, method); 106 for (int i = 0; i < genericParameterTypes.length; i++) { 107 extension.applyParameters(httpMethod, context, operation, paramAnnotations[i]); 108 } 109 110 if ("post".equalsIgnoreCase(httpMethod) && operation.getConsumes() == null) { 111 operation.addConsumes("application/x-www-form-urlencoded"); 112 } 113 } 114 115 if (operation.getResponses() == null) { 116 operation.defaultResponse(new Response().description("successful operation")); 117 } 118 119 final Map<String, String> regexMap = new HashMap<String, String>(); 120 final String parsedPath = PathUtils.parsePath(operationPath, regexMap); 121 122 Path path = swagger.getPath(parsedPath); 123 if (path == null) { 124 path = new SwaggerPath(); 125 swagger.path(parsedPath, path); 126 } 127 path.set(httpMethod.toLowerCase(), operation); 128 } 129 } 130 131 132}