com.lastb7.swagger.controller.SwaggerController Maven / Gradle / Ivy
package com.lastb7.swagger.controller;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.jfinal.core.Action;
import com.jfinal.core.Controller;
import com.jfinal.core.JFinal;
import com.jfinal.kit.Base64Kit;
import com.jfinal.kit.Kv;
import com.jfinal.kit.StrKit;
import com.jfinal.template.Engine;
import com.lastb7.swagger.annotation.ApiRes;
import com.lastb7.swagger.annotation.ApiResProperty;
import com.lastb7.swagger.common.SwaggerConst;
import com.lastb7.swagger.enumeration.ApiEnum;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
/**
* Swagger UI Controller
*
* @author: lbq
* 联系方式: [email protected]
* 创建日期: 2020/9/16
*/
public class SwaggerController extends Controller {
/**
* swagger tag
*/
private List tagsList = new ArrayList<>();
/**
* swagger models
*/
private Map definitionsMap = new LinkedHashMap<>();
/**
* swagger path
*/
private List pathList = new ArrayList();
/**
* 通用返回类 名称
*/
private String commonResName;
/**
* 通用返回类 返回值
*/
private List commonResProperties;
/**
* 默认转发
*/
public void index() {
this.redirect("swagger-ui/doc.html");
}
/**
* 获取分组信息
*/
public void resources() {
Kv kv = new Kv();
kv.set("swagger_resources", SwaggerConst.CONFIG.get("swagger_resources"));
kv.set("swaggerVersion", SwaggerConst.CONFIG.get("swaggerVersion"));
renderJson(Engine.use("swagger").getTemplate("/swagger-resources.jf").renderToString(kv));
}
/**
* 获取api接口解析JSON
*/
public void api() throws IOException {
if (!this.basicAuth()) {
this.response401();
this.renderNull();
return;
}
// 解析通用返回
Kv commonResKv = this.parseSwaggerModel(SwaggerConst.COMMON_RES);
this.commonResName = commonResKv.getStr("name");
this.commonResProperties = (List) commonResKv.get("properties");
// 解析JSON
this.parseGroupPackage(this.getPara("group", ""));
Kv kv = new Kv();
kv.set("swagger", SwaggerConst.CONFIG.getProperties());
kv.set("host", this.getHost());
kv.set("tags", this.tagsList);
kv.set("paths", this.pathList);
kv.set("definitions", this.toDefinitionList(this.definitionsMap));
renderJson(Engine.use("swagger").getTemplate("/api-docs.jf").renderToString(kv));
}
/**
* 解析分组包
*
* @param groupPackage 分组包名
*/
private void parseGroupPackage(String groupPackage) {
Map, List> classMap = this.getApiAction(groupPackage);
classMap.keySet().forEach((Class extends Controller> clazz) -> {
List actions = classMap.get(clazz);
// 解析controller
this.parseController(clazz, actions);
});
}
/**
* 从JFinal中获取全部Action
*/
private Map, List> getApiAction(String basePackage) {
Map, List> apiMap = new HashMap<>(16);
JFinal.me().getAllActionKeys().forEach(actionKey -> {
Action action = JFinal.me().getAction(actionKey, new String[1]);
Class extends Controller> controller = action.getControllerClass();
if (!controller.getName().startsWith(basePackage)) {
return;
}
if (apiMap.containsKey(controller)) {
if (action.getMethod().isAnnotationPresent(ApiOperation.class)) {
List actions = apiMap.get(controller);
if (!actions.contains(action)) {
actions.add(action);
apiMap.put(controller, actions);
}
}
} else {
if (controller.isAnnotationPresent(Api.class)) {
if (action.getMethod().isAnnotationPresent(ApiOperation.class)) {
List actions = new ArrayList<>();
actions.add(action);
apiMap.put(controller, actions);
}
}
}
});
List> ctlList = new ArrayList<>(apiMap.keySet());
ctlList.sort((clazz1, clazz2) -> clazz1.getAnnotation(Api.class).position() - clazz2.getAnnotation(Api.class).position());
Map, List> result = new LinkedHashMap<>();
ctlList.forEach(i -> {
List actions = apiMap.get(i);
actions.sort((action1, action2) -> action1.getMethod().getAnnotation(ApiOperation.class).position() - action2.getMethod().getAnnotation(ApiOperation.class).position());
result.put(i, actions);
});
return result;
}
/**
* 解析controller
*/
private void parseController(Class extends Controller> clazz, List actions) {
// controller 信息
Api api = clazz.getAnnotation(Api.class);
boolean hidden = api.hidden();
if (hidden) {
return;
}
for (String tags : api.tags()) {
Kv tag = new Kv();
tag.set("name", tags);
tag.set("controllerKey", actions.get(0).getControllerKey());
tag.set("controllerName", clazz.getSimpleName());
this.tagsList.add(tag);
}
// 解析action
this.parseAction(actions);
}
/**
* 解析action
*/
private void parseAction(List actions) {
actions.forEach((Action action) -> {
Method method = action.getMethod();
ApiOperation apiAction = method.getAnnotation(ApiOperation.class);
if (apiAction.hidden()) {
return;
}
String controllerKey = this.getControllerKey(action.getControllerKey());
String actionName = action.getMethodName();
Kv actionKv = new Kv();
actionKv.set("tags", StrKit.notBlank(apiAction.tags()) ? apiAction.tags() : actions.get(0).getControllerClass().getAnnotation(Api.class).tags())
.set("summary", apiAction.value())
.set("description", apiAction.notes())
.set("deprecated", method.isAnnotationPresent(Deprecated.class))
.set("parameters", this.parseActionParameters(method))
.set("responses", this.parseActionResponse(controllerKey, actionName, method))
.set("methods", StrKit.isBlank(apiAction.httpMethod()) ? ApiEnum.METHOD_GET : apiAction.httpMethod())
.set("consumes", StrKit.isBlank(apiAction.consumes()) ? ApiEnum.CONSUMES_URLENCODED : apiAction.consumes())
.set("produces", StrKit.isBlank(apiAction.produces()) ? ApiEnum.PRODUCES_DEFAULT : apiAction.produces())
.set("controllerKey", controllerKey)
.set("actionName", actionName);
pathList.add(actionKv);
});
}
/**
* 解析action 参数文档
*/
private List parseActionParameters(Method method) {
// 获取参数注解信息
List params = new ArrayList<>();
if (method.isAnnotationPresent(ApiImplicitParams.class)) {
params.addAll(Arrays.asList(method.getAnnotation(ApiImplicitParams.class).value()));
}
if (method.isAnnotationPresent(ApiImplicitParams.class)) {
ApiImplicitParam[] paramArray = method.getAnnotationsByType(ApiImplicitParam.class);
params.addAll(Arrays.asList(paramArray));
}
// 构建参数列表(包含全局参数)
List paramList = new ArrayList<>();
params.forEach(param -> {
Kv kv = Kv.by("name", param.name())
.set("description", param.value())
.set("required", param.required())
.set("format", param.format())
.set("defaultValue", param.defaultValue())
.set("allowMultiple", param.allowMultiple())
.set("schema", this.toParameterSchema(param))
.set("dataType", StrKit.isBlank(param.dataType()) ? ApiEnum.STRING : param.dataType())
.set("paramType", StrKit.isBlank(param.paramType()) ? ApiEnum.PARAM_TYPE_QUERY : param.paramType());
paramList.add(kv);
});
return paramList;
}
/**
* 解析action 返回文档
*/
private List parseActionResponse(String controllerKey, String actionName, Method method) {
List responseList = new ArrayList();
SwaggerConst.HTTP_CODE.forEach((key, value) -> {
if (key == 200) {
responseList.add(Kv.by("name", key).set("description", value)
.set("schema", this.parseResponse(controllerKey, actionName, method)));
} else {
responseList.add(Kv.by("name", key).set("description", value));
}
});
return responseList;
}
/**
* 解析返回值
*/
private String parseResponse(String controllerKey, String actionName, Method method) {
// swagger model 引用
String swaggerModelName;
List responses = new ArrayList<>();
if (method.isAnnotationPresent(ApiRes.class)) {
responses.addAll(Arrays.asList(method.getAnnotation(ApiRes.class).value()));
}
if (method.isAnnotationPresent(ApiRes.class)) {
ApiResProperty[] paramArray = method.getAnnotationsByType(ApiResProperty.class);
responses.addAll(Arrays.asList(paramArray));
}
if (responses.size() == 0) {
swaggerModelName = this.commonResName;
} else {
// 将参数放入commonRes中,作为新的swagger Model引用(knife4j 约定)
Kv swaggerModelKv = this.parseSwaggerModel(controllerKey, actionName, responses);
swaggerModelName = swaggerModelKv.getStr("name");
// 在data中返回参数
if (SwaggerConst.RESPONSE_IN_DATA) {
swaggerModelName = this.toResponseInData(swaggerModelName);
}
}
return swaggerModelName;
}
/**
* 在data中返回
*/
private String toResponseInData(String swaggerModelName) {
Kv fieldKv = new Kv();
List propertiesList = new ArrayList();
propertiesList.addAll(this.commonResProperties);
fieldKv.set("key", "data");
fieldKv.set("name", swaggerModelName);
fieldKv.set("description", "返回值");
fieldKv.set("type", ApiEnum.RES_OBJECT);
propertiesList.add(fieldKv);
swaggerModelName = this.commonResName + "«" + swaggerModelName + "»";
Kv kv = new Kv();
kv.set("properties", propertiesList);
kv.set("name", swaggerModelName);
this.definitionsMap.put(swaggerModelName, kv);
return swaggerModelName;
}
/**
* swagger models
*/
private List toDefinitionList(Map map) {
List list = new ArrayList();
map.forEach((key, value) -> list.add(value));
return list;
}
/**
* 将class解析为swagger model
*/
private Kv parseSwaggerModel(Class> clazz) {
String modelName = clazz.getSimpleName();
// 已存在,不重复解析
Kv modelKv = this.definitionsMap.get(modelName);
if (null != modelKv) {
return modelKv;
}
ApiModel apiModel = clazz.getAnnotation(ApiModel.class);
String title = apiModel.description();
List fieldList = new ArrayList();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
ApiModelProperty apiField = field.getAnnotation(ApiModelProperty.class);
// List 类型
if (field.getType() == List.class) {
// 如果是List类型,得到其Generic的类型
Type genericType = field.getGenericType();
if (genericType == null) {
continue;
}
// 如果是泛型参数的类型
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
//得到泛型里的class类型对象
Class> genericClazz = (Class>) pt.getActualTypeArguments()[0];
Kv swaggerModel = this.parseSwaggerModel(genericClazz);
Kv fieldKv = new Kv();
fieldKv.set("key", field.getName());
fieldKv.set("name", swaggerModel.getStr("name"));
fieldKv.set("description", apiField.value());
fieldKv.set("type", ApiEnum.RES_OBJECT);
fieldKv.set("allowMultiple", true);
fieldList.add(fieldKv);
}
continue;
}
Class> typeClazz = field.getType();
if (typeClazz.isAnnotationPresent(ApiModel.class)) {
Kv swaggerModel = this.parseSwaggerModel(typeClazz);
Kv fieldKv = new Kv();
fieldKv.set("key", field.getName());
fieldKv.set("name", swaggerModel.getStr("name"));
fieldKv.set("description", apiField.value());
fieldKv.set("type", ApiEnum.RES_OBJECT);
fieldList.add(fieldKv);
} else {
Kv fieldKv = new Kv();
fieldKv.set("name", field.getName());
fieldKv.set("description", apiField.value());
fieldKv.set("type", StrKit.isBlank(apiField.dataType()) ? field.getType().getSimpleName().toLowerCase() : apiField.dataType());
fieldKv.set("example", apiField.example());
fieldList.add(fieldKv);
}
}
Kv kv = new Kv();
kv.set("properties", fieldList);
kv.set("name", modelName);
kv.set("title", title);
this.definitionsMap.put(modelName, kv);
return kv;
}
/**
* 将action response解析为swagger model
*/
private Kv parseSwaggerModel(String controllerKey, String actionName, List responses) {
String modelName = controllerKey + "_" + actionName;
List propertiesList = new ArrayList();
// 不在Data中返回参数
if (!SwaggerConst.RESPONSE_IN_DATA) {
propertiesList.addAll(this.commonResProperties);
}
responses.forEach(apiResponse -> {
if (apiResponse.dataTypeClass() != Void.class) {
Kv swaggerModel = this.parseSwaggerModel(apiResponse.dataTypeClass());
Kv fieldKv = new Kv();
fieldKv.set("key", apiResponse.name());
fieldKv.set("name", swaggerModel.getStr("name"));
fieldKv.set("description", apiResponse.value());
fieldKv.set("type", ApiEnum.RES_OBJECT);
fieldKv.set("allowMultiple", apiResponse.allowMultiple());
propertiesList.add(fieldKv);
} else {
Kv fieldKv = new Kv();
fieldKv.set("name", apiResponse.name());
fieldKv.set("description", apiResponse.value());
fieldKv.set("type", StrKit.isBlank(apiResponse.dataType()) ? ApiEnum.RES_STRING : apiResponse.dataType());
fieldKv.set("format", StrKit.isBlank(apiResponse.format()) ? ApiEnum.FORMAT_STRING : apiResponse.format());
fieldKv.set("example", apiResponse.example());
fieldKv.set("exampleEnum", apiResponse.exampleEnum());
fieldKv.set("allowMultiple", apiResponse.allowMultiple());
propertiesList.add(fieldKv);
}
});
Kv kv = new Kv();
kv.set("properties", propertiesList);
kv.set("name", modelName);
this.definitionsMap.put(modelName, kv);
return kv;
}
/**
* 解析对象参数
*/
private String toParameterSchema(ApiImplicitParam apiParam) {
if (apiParam.dataTypeClass() != Void.class) {
Kv swaggerModel = this.parseSwaggerModel(apiParam.dataTypeClass());
return swaggerModel.getStr("name");
}
return null;
}
/**
* 获取host配置
*/
private String getHost() {
String host = SwaggerConst.CONFIG.get("host");
if (StrKit.isBlank(host)) {
host = getRequest().getServerName();
if (this.getRequest().getServerPort() != 80) {
host += ":" + getRequest().getServerPort();
}
}
return host;
}
/**
* 避免JFinal ControllerKey 设置前缀后,与swagger basePath 设置导致前端生成2次
*/
private String getControllerKey(String actionKey) {
return actionKey.replaceFirst(SwaggerConst.CONFIG.get("basePath"), "")
.substring(1);
}
/**
* WWW-Authenticate 简单认证
*/
private boolean basicAuth() throws IOException {
String basicAuth = SwaggerConst.CONFIG.get("basicAuth");
if (StrKit.isBlank(basicAuth)) {
// 未启用简单认证
return true;
}
String authorization = this.getHeader("Authorization");
if (StrKit.isBlank(authorization)) {
// 请求头无认证信息
return false;
}
Map baseAuthMap = new HashMap<>(16);
String[] baseAuthArr = basicAuth.split(",");
for (String auth : baseAuthArr) {
baseAuthMap.put(auth.split("#")[0], auth.split("#")[1]);
}
String nameAndPwd = Base64Kit.decodeToStr(authorization.substring(6));
String[] upArr = nameAndPwd.split(":");
if (upArr.length != 2) {
return false;
}
String iptName = upArr[0];
String iptPwd = upArr[1];
return iptPwd.equals(baseAuthMap.get(iptName));
}
private void response401() throws IOException {
this.getResponse().setStatus(401);
this.getResponse().setHeader("WWW-Authenticate", "Basic realm=\"请输入Swagger文档访问账号密码\"");
this.getResponse().getWriter().write("无权限访问");
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy