Swagger2解决通用返回结果是泛型的问题:swagger2.9.2+ swagger-bootstrap-ui-1.8.5

Swagger2解决通用返回结果是泛型的问题:swagger2.9.2+ swagger-bootstrap-ui-1.8.5 

依赖关系


		    
			io.springfox
			springfox-swagger2
			${springfox-swagger2}
		
		
			io.springfox
			springfox-swagger-ui
			${springfox-swagger2}
		
		
			com.google.collections
			google-collections
			1.0
		
		
			com.github.xiaoymin
			swagger-bootstrap-ui
			${swagger-bootstrap-ui}
		

通用的返回结果类:主要是要解决data属性的参照关系

@ApiModel(description = "通用响应返回对象")
public class JsonResult extends BaseResult {
	protected static Logger logger = LoggerFactory.getLogger(JsonResult.class);
	/**
	 * 返回结果代码
	 */
	@ApiModelProperty(value = "结果代码", position = 0)
	private int code;
	/**
	 * 返回结果类型
	 */
	@ApiModelProperty(value = "结果类型", position = 1)
	private String type;
	/**
	 * 具体的错误信息
	 */
	@ApiModelProperty(value = "错误信息", position = 2)
	private String message;
	/**
	 * Exception类
	 */
	@ApiModelProperty(value = "异常类", position = 3)
	private String exception;
	/**
	 * 返回结果数据
	 */
	@ApiModelProperty(value = "结果数据", position = 4 )
	private T data;

	/**
	 * 是否成功,0表示成功,其他都是失败
	 */
	@ApiModelProperty(value = "是否成功", position = 5, example = "true")
	private boolean success = true;

	/**
	 * 具体的异常类 
* JSON序列化时,将该字段忽略 */ @JSONField(serialize = false) @JsonIgnore @ApiModelProperty(value = "具体的异常类", position = 6) private Exception realException; .... .... }

我用了最简单的方法,重写了该类:

com.github.xiaoymin.swaggerbootstrapui.web.SwaggerBootstrapUiController

我的类:

package cn.boot4j.core.support.swagger;

import static com.google.common.base.Strings.isNullOrEmpty;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
import static springfox.documentation.swagger.common.HostNameProvider.componentsFrom;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.util.UriComponents;

import com.github.xiaoymin.swaggerbootstrapui.model.SwaggerBootstrapUiPathInstance;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiSort;
import io.swagger.models.Model;
import io.swagger.models.Swagger;
import io.swagger.models.SwaggerBootstrapUi;
import io.swagger.models.SwaggerBootstrapUiPath;
import io.swagger.models.SwaggerBootstrapUiTag;
import io.swagger.models.SwaggerExt;
import io.swagger.models.properties.Property;
import springfox.documentation.annotations.ApiIgnore;
import springfox.documentation.service.Documentation;
import springfox.documentation.service.Tag;
import springfox.documentation.spring.web.DocumentationCache;
import springfox.documentation.spring.web.json.Json;
import springfox.documentation.spring.web.json.JsonSerializer;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper;

@Controller
@ApiIgnore
public class SwaggerUiController {

	/***
	 * sort排序接口
	 */
	public static final String DEFAULT_SORT_URL = "/v2/api-docs-ext";

	private static final String HAL_MEDIA_TYPE = "application/hal+json";
	private static final Logger LOGGER = LoggerFactory.getLogger(SwaggerUiController.class);
	private final ServiceModelToSwagger2Mapper mapper;
	private final DocumentationCache documentationCache;
	private final JsonSerializer jsonSerializer;
	private final String hostNameOverride;

	@Autowired
	public SwaggerUiController(Environment environment, ServiceModelToSwagger2Mapper mapper,
			DocumentationCache documentationCache, JsonSerializer jsonSerializer) {
		this.mapper = mapper;
		this.documentationCache = documentationCache;
		this.jsonSerializer = jsonSerializer;
		this.hostNameOverride = environment.getProperty("springfox.documentation.swagger.v2.host", "DEFAULT");
		;
	}

	@RequestMapping(value = DEFAULT_SORT_URL, method = RequestMethod.GET, produces = { APPLICATION_JSON_VALUE,
			HAL_MEDIA_TYPE })
	@ResponseBody
	public ResponseEntity apiSorts(@RequestParam(value = "group", required = false) String swaggerGroup,
			HttpServletRequest request) {
		String groupName = Optional.fromNullable(swaggerGroup).or(Docket.DEFAULT_GROUP_NAME);
		Documentation documentation = documentationCache.documentationByGroup(groupName);
		if (documentation == null) {
			LOGGER.warn("Unable to find specification for grouRp {}", groupName);
			return new ResponseEntity(HttpStatus.NOT_FOUND);
		}
		Swagger swagger = mapper.mapDocumentation(documentation);
		UriComponents uriComponents = componentsFrom(request, swagger.getBasePath());
		swagger.basePath(Strings.isNullOrEmpty(uriComponents.getPath()) ? "/" : uriComponents.getPath());
		if (isNullOrEmpty(swagger.getHost())) {
			swagger.host(hostName(uriComponents));
		}
		// 扩展
		swagger = extend(swagger);
		SwaggerExt swaggerExt = new SwaggerExt(swagger);
		// swaggerBootstrapUi.setTagSortLists(getSortTag(request,documentation));
		swaggerExt.setSwaggerBootstrapUi(initSwaggerBootstrapUi(request, documentation));
		// Method 层排序
		return new ResponseEntity(jsonSerializer.toJson(swaggerExt), HttpStatus.OK);
	}

	@SuppressWarnings("deprecation")
	private SwaggerBootstrapUi initSwaggerBootstrapUi(HttpServletRequest request, Documentation documentation) {
		SwaggerBootstrapUi swaggerBootstrapUi = new SwaggerBootstrapUi();
		WebApplicationContext wc = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());
		Iterator tags = documentation.getTags().iterator();
		List targetTagLists = Lists.newArrayList();
		// Ctl层排序
		Map beansWithAnnotation = wc.getBeansWithAnnotation(Controller.class);
		// path排序
		List targetPathLists = Lists.newArrayList();
		while (tags.hasNext()) {
			Tag sourceTag = tags.next();
			String tagName = sourceTag.getName();
			boolean exists = false;
			Class aClass = null;
			Api api = null;
			for (Map.Entry entry : beansWithAnnotation.entrySet()) {
				aClass = entry.getValue().getClass();
				api = aClass.getAnnotation(Api.class);
				if (api != null) {
					// 是否相等
					if (Lists.newArrayList(api.tags()).contains(tagName)) {
						exists = true;
						break;
					}
				}
			}
			// 获取order值
			int order = Integer.MAX_VALUE;
			SwaggerBootstrapUiTag tag = new SwaggerBootstrapUiTag(order);
			tag.name(sourceTag.getName()).description(sourceTag.getDescription());
			if (exists) {
				// 优先获取api注解的position属性,如果不等于0,则取此值,否则获取apiSort注解,判断是否为空,如果不为空,则获取apisort的值,优先级:@Api-position>@ApiSort-value
				int post = api.position();
				if (post == 0) {
					ApiSort annotation = aClass.getAnnotation(ApiSort.class);
					if (annotation != null) {
						order = annotation.value();
					}
				} else {
					order = post;
				}
				// targetTagLists.add(new
				// Tag(sourceTag.getName(),sourceTag.getDescription(),order,sourceTag.getVendorExtensions()));
				tag.setOrder(order);
				// 获取父级path
				String parentPath = "";
				RequestMapping parent = aClass.getAnnotation(RequestMapping.class);
				if (parent != null) {
					parentPath = parent.value()[0];
				}
				Method[] methods = aClass.getDeclaredMethods();
				for (Method method : methods) {
					List paths = new SwaggerBootstrapUiPathInstance(parentPath, method).match();
					if (paths != null && paths.size() > 0) {
						targetPathLists.addAll(paths);
					}
				}
			}
			targetTagLists.add(tag);

		}
		Collections.sort(targetTagLists, new Comparator() {
			@Override
			public int compare(SwaggerBootstrapUiTag o1, SwaggerBootstrapUiTag o2) {
				return o1.getOrder().compareTo(o2.getOrder());
			}
		});
		Collections.sort(targetPathLists, new Comparator() {
			@Override
			public int compare(SwaggerBootstrapUiPath o1, SwaggerBootstrapUiPath o2) {
				return o1.getOrder().compareTo(o2.getOrder());
			}
		});

		swaggerBootstrapUi.setTagSortLists(targetTagLists);
		swaggerBootstrapUi.setPathSortLists(targetPathLists);
		return swaggerBootstrapUi;
	}

	private String hostName(UriComponents uriComponents) {
		if ("DEFAULT".equals(hostNameOverride)) {
			String host = uriComponents.getHost();
			int port = uriComponents.getPort();
			if (port > -1) {
				return String.format("%s:%d", host, port);
			}
			return host;
		}
		return hostNameOverride;
	}

	private Swagger extend(Swagger swagger) {
		// 响应返回参数增强
		Iterator> it = swagger.getDefinitions().entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry entry = it.next();
			Model model = entry.getValue();
			String key = entry.getKey();
			if (key.contains("JsonResult")) {
				Map props = model.getProperties();
				Property dataProp = props.get("data");
				Property newProp = SwaggerUtil.getNewProp(dataProp, SwaggerUtil.getRealType(key), swagger.getDefinitions());
				props.put("data", newProp);
			}
		}
		return swagger;
	}

}

 关键代码只有1行:

// >> 扩展~~~~~~~
		swagger = extend(swagger);
        // >> end

 运行效果:

Swagger2解决通用返回结果是泛型的问题:swagger2.9.2+ swagger-bootstrap-ui-1.8.5_第1张图片

ArrayRefProperty:

package cn.boot4j.core.support.swagger;

import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.RefProperty;
import io.swagger.models.refs.GenericRef;
import io.swagger.models.refs.RefType;

/**
 * 同时拥有ArrayProperty和RefProperty的特性
 * @author ChenZhiPing 2018年10月30日 下午6:08:57
 */
public class ArrayRefProperty extends ArrayProperty {
	private GenericRef genericRef;

	public String get$ref() {
		return genericRef.getRef();
	}

	public void set$ref(String ref) {
		this.genericRef = new GenericRef(RefType.DEFINITION, ref);

		// $ref
		RefProperty items = new RefProperty();
		items.setType(ref);
		items.set$ref(ref);
		this.items(items);
	}
}

 

注意Controller的写法:约定**你我约定**(具体的JsonResult泛型必须指定)

package cn.boot4j.product.demo.web.ui;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import cn.boot4j.core.base.BaseController;
import cn.boot4j.core.support.db.DBResult;
import cn.boot4j.core.support.service.JsonResult;
import cn.boot4j.product.demo.model.customer.Customer;
import cn.boot4j.product.demo.model.customer.CustomerDBParam;
import cn.boot4j.product.demo.model.operator.Operator;
import cn.boot4j.product.demo.model.operator.OperatorType;
import cn.boot4j.product.demo.service.customer.ICustomerService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

/**
 * @author ChenZhiPing 2017年9月26日 上午11:14:38
 */
@RestController
@RequestMapping("demo")
@Api(value = "DemoController", tags = { "10.JsonResult响应结果" }, hidden = true)
@SuppressWarnings("unchecked")
public class DemoController extends BaseController {
	@Autowired
	private ICustomerService customerService;

	@ApiOperation(value = "JsonResult«List«T»»", notes = "List类型")
	@RequestMapping(value = "list", method = RequestMethod.GET)
	public JsonResult> list() {
		try {
			Object data = customerService.findAll();
			return success(data);
		} catch (Exception e) {
			return exception(e);
		}
	}

	@ApiOperation(value = "JsonResult«Map«B, B»»", notes = "Map类型")
	@RequestMapping(value = "mapOnly", method = RequestMethod.GET)
	public JsonResult> mapOnly() {
		try {
			List data = (List) customerService.findAll();
			Map map = new HashMap();
			if (data.size() > 0) {
				for (Customer c : data) {
					map.put(c.getName(), c.getId());
				}
			}
			return success(map);
		} catch (Exception e) {
			return exception(e);
		}
	}

	@ApiOperation(value = "JsonResult«Map«B, T»»", notes = "Map类型")
	@RequestMapping(value = "map", method = RequestMethod.GET)
	public JsonResult> map() {
		try {
			List data = (List) customerService.findAll();
			Map map = new HashMap();
			if (data.size() > 0) {
				for (Customer c : data) {
					map.put(c.getName(), c);
				}
			}
			return success(map);
		} catch (Exception e) {
			return exception(e);
		}
	}

	@ApiOperation(value = "JsonResult«Set«T»»", notes = "Set类型")
	@RequestMapping(value = "set", method = RequestMethod.GET)
	public JsonResult> set() {
		try {
			List data = (List) customerService.findAll();
			Set set = new TreeSet<>();
			if (data.size() > 0) {
				for (Customer c : data) {
					set.add(c);
				}
			}
			return success(set);
		} catch (Exception e) {
			return exception(e);
		}
	}

	@ApiOperation(value = "JsonResult«DBResult«T»»", notes = "DBResult类型")
	@RequestMapping(value = "query", method = RequestMethod.GET)
	public JsonResult> query() {
		try {
			Object data = customerService.query(new CustomerDBParam());
			return success(data);
		} catch (Exception e) {
			return exception(e);
		}
	}

	@ApiOperation(value = "JsonResult«B»", notes = "只有基础类型")
	@RequestMapping(value = "deleteByPk", method = RequestMethod.GET)
	public JsonResult deleteByPk() {
		try {
			Object data = customerService.deleteByPk(0);
			return success(data);
		} catch (Exception e) {
			return exception(e);
		}
	}

	@ApiOperation(value = "复杂T", notes = "复杂类型")
	@RequestMapping(value = "complex", method = RequestMethod.GET)
	public JsonResult, List>>> complex() {
		try {
			Map m1 = new HashMap();
			Operator o = new Operator();
			o.setOwnerIdentityId((long) 20087);
			o.setDisplayName("很漂亮的操作员");
			o.setLastUpdateDate(new Date());
			o.setOperatorType(OperatorType.SP_OPERATOR);
			o.setRemark("测试复杂的Swagger泛型展示");
			m1.put(o.getOwnerIdentityId(), o);

			Map m2 = (Map) this.map().getData();
			List list = new ArrayList();
			list.add(m2);

			@SuppressWarnings("rawtypes")
			Map map = new HashMap();
			map.put(m1, list);
			return success(map);
		} catch (Exception e) {
			return exception(e);
		}
	}
}
 
  

 

SwaggerUtil:

package cn.boot4j.core.support.swagger;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.BeanUtils;

import io.swagger.models.Model;
import io.swagger.models.properties.AbstractProperty;
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.BooleanProperty;
import io.swagger.models.properties.DateTimeProperty;
import io.swagger.models.properties.IntegerProperty;
import io.swagger.models.properties.LongProperty;
import io.swagger.models.properties.ObjectProperty;
import io.swagger.models.properties.Property;
import io.swagger.models.properties.PropertyBuilder;
import io.swagger.models.properties.PropertyBuilder.PropertyId;
import io.swagger.models.properties.RefProperty;
import io.swagger.models.properties.StringProperty;

/**
 * @author ChenZhiPing 2018年10月31日 下午1:44:50
 */
public class SwaggerUtil {

	/**
	 * 判断是否Swagger基本类型
	 * @param type
	 * @return
	 */
	public static boolean isBaseType(String type) {
		return SwaggerUtil.getSwaggerProperty(type) != null;
	}

	/**
	 * 获取Swagger支持的类型
	 * @return
	 */
	public static Map getPropMap() {
		Map map = new HashMap();
		map.put("integer", new IntegerProperty());
		map.put("int", new IntegerProperty());
		map.put("long", new LongProperty());
		map.put("string", new StringProperty());
		map.put("object", new ObjectProperty());
		map.put("array", new ArrayProperty());
		map.put("boolean", new BooleanProperty());
		map.put("date", new DateTimeProperty());
		return map;
	}

	/**
	 * 通过java类型获取Swagger类型
	 * @param type javaType
	 * @return swaggerType
	 */
	public static AbstractProperty getSwaggerProperty(String type) {
		type = type.toLowerCase();
		return SwaggerUtil.getPropMap().get(type);
	}

	public static boolean isMap(String type) {
		type = type.toLowerCase();
		return type.startsWith("map");
	}

	public static boolean isIterable(String type) {
		type = type.toLowerCase();
		return type.startsWith("list") || type.startsWith("set");
	}

	/**
	 * 获取非基本类型的T
* new String[] { "A>", "A", "A>>>" } * @param type * @return C1,C2,C3,C4 */ public static String getRef(String type) { try { String m = type.substring(type.lastIndexOf("«") + 1, type.indexOf("»")); String[] cc = m.split(","); for (String c : cc) { if (!SwaggerUtil.isBaseType(c)) { return c; } } return type; } catch (Exception e) { } return "!!Unknown T!!"; } /** * 获取对象类型,主要是剥离第一层<> * @param type JsonResult>>> * @return Map>> */ public static String getRealType(String type) { try { String m = type.substring(type.indexOf("«") + 1, type.lastIndexOf("»")); return m; } catch (Exception e) { } return type; } /** * 判断是否存在非基本类型<参照类型> * @param type * @return */ public static boolean hasRef(String type) { if (type.indexOf("»") > 0) { try { String m = type.substring(type.lastIndexOf("«") + 1, type.indexOf("»")); String[] cc = m.split(","); for (String c : cc) { if (!SwaggerUtil.isBaseType(c)) { return true; } } return false; } catch (Exception e) { return false; } } else { return !SwaggerUtil.isBaseType(type); } } /** * 递归处理泛型类型
* JsonResult, List>>> * @param dataProp * @param type * @param definitions * @return */ public static Property getNewProp(Property dataProp, String type, Map definitions) { Property newProp = null; Model model = definitions.get(type); Map props = null; if (null != model) { props = model.getProperties(); } if (null == props) { props = new HashMap(); } String realType = SwaggerUtil.getRealType(type); if (SwaggerUtil.isMap(type)) { String[] realTypes = SwaggerUtil.splitByComma(realType); Map argsK = new HashMap(); argsK.put(PropertyBuilder.PropertyId.DESCRIPTION, "Map的键"); argsK.put(PropertyBuilder.PropertyId.TYPE, realTypes[0].toLowerCase()); AbstractProperty _prop0 = SwaggerUtil.getSwaggerProperty(realTypes[0]); Property propK = PropertyBuilder.build(null == _prop0 ? "object" : _prop0.getType(), null == _prop0 ? null : _prop0.getFormat(), argsK); propK.setName("key"); Map argsV = new HashMap(); argsV.put(PropertyBuilder.PropertyId.DESCRIPTION, "Map的值"); argsV.put(PropertyBuilder.PropertyId.TYPE, realTypes[1].toLowerCase()); AbstractProperty _prop1 = SwaggerUtil.getSwaggerProperty(realTypes[1]); Property propV = PropertyBuilder.build(null == _prop1 ? "object" : _prop1.getType(), null == _prop1 ? null : _prop1.getFormat(), argsV); propV.setName("value"); if (!realType.equals(type)) { propK = SwaggerUtil.getNewProp(propK, realTypes[0], definitions); propV = SwaggerUtil.getNewProp(propV, realTypes[1], definitions); } props.put(propK.getName(), propK); props.put(propV.getName(), propV); newProp = new RefProperty(); BeanUtils.copyProperties(dataProp, newProp); ((RefProperty) newProp).set$ref(type); } else if (SwaggerUtil.isIterable(type)) { String ref = SwaggerUtil.getRealType(type); newProp = new ArrayRefProperty(); BeanUtils.copyProperties(dataProp, newProp); ((ArrayRefProperty) newProp).set$ref(ref); ((ArrayRefProperty) newProp).setType(ArrayRefProperty.TYPE); if (!realType.equals(type)) { SwaggerUtil.getNewProp(dataProp, realType, definitions); } } else if (SwaggerUtil.isBaseType(type)) { Map args = new HashMap(); args.put(PropertyBuilder.PropertyId.DESCRIPTION, dataProp.getDescription()); args.put(PropertyBuilder.PropertyId.TYPE, type.toLowerCase()); AbstractProperty _prop = SwaggerUtil.getSwaggerProperty(type); newProp = PropertyBuilder.build(_prop.getType(), _prop.getFormat(), args); newProp.setName(dataProp.getName()); } else if (SwaggerUtil.hasRef(type)) { newProp = new RefProperty(); BeanUtils.copyProperties(dataProp, newProp); ((RefProperty) newProp).set$ref(type); } else { } if (null != model) { model.setProperties(props); } return newProp; } public static String[] splitByComma(String str) { int index = 0; int has = 0; for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if ("«".equals(c + "")) { has++; } if ("»".equals(c + "")) { has--; } if (",".equals(c + "") && has == 0) { index = i; } } String[] arr = new String[2]; arr[0] = str.substring(0, index); arr[1] = str.substring(index + 1); return arr; } public static void main(String[] args) { String[] ss = new String[] { "A«List«C1»»", "A«C2»", "A«B«String,«String,List«C4»»»»" }; for (String s : ss) { String c = SwaggerUtil.getRealType(s); System.out.println(c); } String[] s2 = new String[] { "A,B«List«C1»»", "Map«A,B»,C«List«D»»", "Map«Map«A,B»,C«List«D»»,Map«A,B»,C«List«D»»»,C«List«D»»" }; for (String s : s2) { String[] arr = SwaggerUtil.splitByComma(s); System.out.println(arr[0]); System.out.println(arr[1]); } } }

 

最复杂的情况:JsonResult, List>>> 

 

Swagger2解决通用返回结果是泛型的问题:swagger2.9.2+ swagger-bootstrap-ui-1.8.5_第2张图片

 

响应示例:

{
    "code": 0,
    "type": "",
    "message": "",
    "exception": "",
    "data": {
        "value": [
            {
                "value": {
                    "id": 0,
                    "addr": "",
                    "age": 0,
                    "name": "",
                    "nickName": "",
                    "password": "",
                    "registorTime": "",
                    "sex": ""
                },
                "key": ""
            }
        ],
        "key": {
            "value": {
                "displayName": "",
                "lastUpdateDate": "",
                "lastUpdateUser": "",
                "loginPassword": "",
                "operatorName": "",
                "operatorType": "",
                "ownerIdentityId": 0,
                "ownerIdentityName": "",
                "ownerIdentityType": "",
                "remark": "",
                "unionid": ""
            },
            "key": 0
        }
    },
    "success": true
}

 

你可能感兴趣的:(人工智能)