io.springfox
springfox-swagger2
2.9.2
io.springfox
springfox-swagger-ui
2.9.2
com.google.guava
guava
27.0.1-jre
package top.lidaoyuan.hamster.api.config.swagger;
import java.util.Arrays;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMethod;
import io.swagger.annotations.ApiOperation;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket customDocket() {
// 配置全局参数返回状态
java.util.List resMsgList = Arrays.asList(
new ResponseMessageBuilder().code(200).message("成功!").build(),
new ResponseMessageBuilder().code(-1).message("失败!").build(),
new ResponseMessageBuilder().code(401).message("参数校验错误!").build(),
new ResponseMessageBuilder().code(403).message("没有权限操作,请后台添加相应权限!").build(),
new ResponseMessageBuilder().code(500).message("服务器内部异常,请稍后重试!").build(),
new ResponseMessageBuilder().code(501).message("请登录!").build());
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build()
.globalResponseMessage(RequestMethod.GET, resMsgList)
.globalResponseMessage(RequestMethod.POST, resMsgList)
.globalResponseMessage(RequestMethod.PUT, resMsgList)
.globalResponseMessage(RequestMethod.DELETE, resMsgList);
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Hamster接口文档")
.description("接口文档说明")
.version("1.0.0")
.build();
}
}
application.properties
中添加配置logging.level.io.swagger.models.parameters.AbstractSerializableParameter=ERROR
UserController
package top.lidaoyuan.hamster.api.web;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import top.lidaoyuan.hamster.api.dto.JsonResultDTO;
import top.lidaoyuan.hamster.api.dto.PageInputDTO;
import top.lidaoyuan.hamster.api.dto.UserInputDTO;
import top.lidaoyuan.hamster.api.entity.User;
import top.lidaoyuan.hamster.api.service.IUserService;
/**
* 用户 前端控制器
* @author Lidy
* @since 2019-03-06
*/
@Api(tags = {"用户接口"})
@RestController
@RequestMapping("/v1/user")
@Slf4j
public class UserController {
@Autowired
private IUserService userService;
@ApiOperation(value = "新增用户")
@PostMapping
public JsonResultDTO create(@RequestBody @Valid UserInputDTO inputDTO, BindingResult bindingResult) {
log.info("create.inputDTO:" + inputDTO);
boolean hasError = bindingResult.hasErrors();
log.info("hasError:" + hasError);
if(hasError) {
List errMsgList = bindingResult.getAllErrors().stream()
.map(ObjectError::getDefaultMessage)
.collect(Collectors.toList());
return JsonResultDTO.errorArgument(errMsgList.toString());
}
User userDb = userService.save(inputDTO.convertToEntity());
log.info("create.userDb:" + userDb );
return JsonResultDTO.ok(inputDTO.convertFor(userDb));
}
@ApiOperation(value = "获取单个用户")
@GetMapping("{id}")
public JsonResultDTO getOne(@ApiParam(value = "用户Id", example = "1", required = true)
@PathVariable Integer id) throws Exception {
log.info("getOne.id:" + id);
User userDb = userService.getOne(id);
log.info("getOne.userDB:" + userDb);
return JsonResultDTO.ok(new UserInputDTO().convertFor(userDb));
}
@ApiOperation(value = "更新单个用户")
@PutMapping("{id}")
public JsonResultDTO update(@ApiParam(value = "用户Id", example = "1", required = true)
@PathVariable Integer id, @RequestBody UserInputDTO inputDTO) {
log.info("update.id:" + id + " inputDTO:" + inputDTO );
User userDb = userService.update(inputDTO.convertToEntity(), id);
log.info("update.userDb:" + userDb );
return JsonResultDTO.ok(inputDTO.convertFor(userDb));
}
@ApiOperation(value = "获取用户列表")
@GetMapping("listExample")
public JsonResultDTO listExample(UserInputDTO inputDTO) {
log.info("listExample.inputDTO:" + inputDTO);
List listDb = null;
if(inputDTO == null) {
listDb = userService.list();
}else {
listDb = userService.list(inputDTO.convertToEntity());
}
log.info("listExample.listDb:" + listDb);
if(listDb == null || listDb.size() ==0) return JsonResultDTO.ok();
List inputDTOList = new ArrayList<>();
for(User user: listDb) {
inputDTOList.add(inputDTO.convertFor(user));
}
return JsonResultDTO.ok(inputDTOList);
}
@ApiOperation(value = "分页获取用户列表")
@GetMapping("listPageExample")
public JsonResultDTO listPageExample(UserInputDTO inputDTO, PageInputDTO pageDTO) {
log.info("listPageExample.inputDTO:" + inputDTO + " pageDTO:" + pageDTO);
Page pageDb = null;
if(inputDTO == null) {
pageDb = userService.page(pageDTO);
}else {
pageDb = userService.page(inputDTO.convertToEntity(), pageDTO);
}
log.info("listPageExample.pageDb:" + pageDb);
if(pageDb == null || pageDb.getSize() ==0) {
return JsonResultDTO.ok();
}
List inputDTOList = new ArrayList<>();
for(User user: pageDb) {
inputDTOList.add(inputDTO.convertFor(user));
}
return JsonResultDTO.page(inputDTOList,pageDb.getTotalElements());
}
@ApiOperation(value = "删除用户")
@DeleteMapping("{id}")
public JsonResultDTO deleteById(@ApiParam(value = "用户Id", example = "1", required = true)
@PathVariable Integer id) {
log.info("del.id:" + id);
userService.deleteById(id);
return JsonResultDTO.ok();
}
}
package top.lidaoyuan.hamster.api.dto;
import java.io.Serializable;
import com.google.common.base.Converter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import lombok.experimental.Accessors;
import javax.validation.constraints.*;
import top.lidaoyuan.hamster.api.entity.User;
import top.lidaoyuan.hamster.utils.BeanUtils;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* 用户DTO
* @author Lidy
* @since 2019-03-06
*/
@ApiModel(value = "用户Model")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@ToString
@JsonInclude(Include.NON_NULL)
public class UserInputDTO implements Serializable {
private static final long serialVersionUID = 1L;
/** ID */
@ApiModelProperty(hidden = true)
private Integer id;
/** 是否激活 */
@ApiModelProperty(value = "是否激活", position = 1)
private Boolean active;
/** 年龄 */
@ApiModelProperty(value = "年龄", position = 2)
private Integer age;
/** email */
@ApiModelProperty(value = "email", position = 3)
@Email(message = "邮箱格式不正确")
@NotBlank(message = "【email】不能为空")
@Size(max = 255, message = "【email】字段长度应<255")
private String email;
/** 姓 */
@ApiModelProperty(value = "姓", position = 4)
@NotBlank(message = "【姓】不能为空")
@Size(max = 10, message = "【姓】字段长度应<10")
private String firstname;
/** 名 */
@ApiModelProperty(value = "名", position = 5)
@Size(max = 20, message = "【名】字段长度应<20")
private String lastname;
/** 开始时间 */
@ApiModelProperty(value = "开始时间", example = "2019-03-06 17:09:10", position = 6)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date startDate;
/** 结束时间 */
@ApiModelProperty(value = "结束时间", example = "2019-03-06 17:09:10", position = 7)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date endDate;
/** 转换成实体类 */
public User convertToEntity() {
return new UserInputDTOConver().convert(this);
}
/** 转换成InputDTO */
public UserInputDTO convertFor(User bean) {
return new UserInputDTOConver().reverse().convert(bean);
}
/** Conver转换类 */
private static class UserInputDTOConver extends Converter {
@Override
protected User doForward(UserInputDTO dto) {
User bean = new User();
BeanUtils.copyProperties(dto, bean);
return bean;
}
@Override
protected UserInputDTO doBackward(User bean) {
UserInputDTO dto = new UserInputDTO();
BeanUtils.copyProperties(bean, dto);
return dto;
}
}
}
package top.lidaoyuan.hamster.api.dto;
import java.util.Arrays;
import java.util.List;
import org.springframework.data.domain.Page;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* Json结果DTO
* @author ex-lidy001
*
*/
@ApiModel(value = "返回JSON对象")
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(Include.NON_NULL)
public class JsonResultDTO {
@ApiModelProperty(value = "状态码;200:成功")
private Integer code;
@ApiModelProperty(value = "状态说明")
private String msg;
@ApiModelProperty(value = "总记录数")
private Long total;
@ApiModelProperty(value = "返回数据")
private Object data;
public JsonResultDTO(Integer code, String msg) {
this.code = code;
this.msg = msg;
this.data = Arrays.asList();
}
public JsonResultDTO(Integer code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
if(data == null) {
this.data = Arrays.asList();
}
}
public static JsonResultDTO ok() {
return new JsonResultDTO(200, "成功");
}
public static JsonResultDTO ok(String msg) {
return new JsonResultDTO(200, msg);
}
public static JsonResultDTO ok(Object data) {
return new JsonResultDTO(200, "成功", data);
}
public static JsonResultDTO page(Page> page) {
JsonResultDTO jrDTO = new JsonResultDTO(200, "成功", page.getContent());
jrDTO.setTotal(page.getTotalElements());
return jrDTO;
}
public static JsonResultDTO page(List> list, Long total) {
JsonResultDTO jrDTO = new JsonResultDTO(200, "成功", list);
jrDTO.setTotal(total);
return jrDTO;
}
public static JsonResultDTO error() {
return new JsonResultDTO(-1, "失败!");
}
public static JsonResultDTO error(String msg) {
return new JsonResultDTO(-1, msg);
}
public static JsonResultDTO errorArgument(String msg) {
return new JsonResultDTO(401, "参数校验:" + msg);
}
public static JsonResultDTO unAuth() {
return new JsonResultDTO(403, "没有权限!");
}
public static JsonResultDTO unlogin() {
return new JsonResultDTO(501, "请登录!");
}
public static JsonResultDTO exception(String msg) {
return new JsonResultDTO(500, "系统内部错误:" + msg);
}
}
请求路径http://localhost:8080/swagger-ui.html
在使用最新版的springfox-swagger2 2.9.2``position
排序的时候没有生效,后来在网上找了相关资料,需要重写swagger的两个类,具体如下。
package top.lidaoyuan.hamster.api.config.swagger;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import io.swagger.models.parameters.Parameter;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2MapperImpl;
/**
* Created by wujie on 2019/2/16.
* 重写 将Document转换成Swagger 类, 根据order进行排序
*/
@Primary //同一个接口,可能会有几种不同的实现类,而默认只会采取其中一种的情况下
@Component("ServiceModelToSwagger2Mapper")
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomModelToSwaggerMapper extends ServiceModelToSwagger2MapperImpl {
@Override
protected List parameterListToParameterList(List list) {
//list需要根据order|postion排序
list = list.stream().sorted((p1, p2) -> Integer.compare(p1.getOrder(), p2.getOrder())).collect(Collectors.toList());
// log.debug("************************************list:{}", list.toString());
return super.parameterListToParameterList(list);
}
}
package top.lidaoyuan.hamster.api.config.swagger;
import static springfox.documentation.swagger.common.SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiParam;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.service.AllowableListValues;
import springfox.documentation.service.AllowableValues;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.EnumTypeDeterminer;
import springfox.documentation.spi.service.ExpandedParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterExpansionContext;
import springfox.documentation.spring.web.DescriptionResolver;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger.readers.parameter.Examples;
import springfox.documentation.swagger.schema.ApiModelProperties;
/**
* Created by wujie on 2019/2/16.
* 自定义ExpandedParameterBuilderPlugin,主要是修正源码query传入请求参数postion无效
* 这里,将postion赋值给order
*/
@Primary
@Component
public class CustomSwaggerParameterBuilder implements ExpandedParameterBuilderPlugin {
private final DescriptionResolver descriptions;
private final EnumTypeDeterminer enumTypeDeterminer;
@Autowired
public CustomSwaggerParameterBuilder(
DescriptionResolver descriptions,
EnumTypeDeterminer enumTypeDeterminer) {
this.descriptions = descriptions;
this.enumTypeDeterminer = enumTypeDeterminer;
}
@Override
public void apply(ParameterExpansionContext context) {
Optional apiModelPropertyOptional = context.findAnnotation(ApiModelProperty.class);
if (apiModelPropertyOptional.isPresent()) {
fromApiModelProperty(context, apiModelPropertyOptional.get());
}
Optional apiParamOptional = context.findAnnotation(ApiParam.class);
if (apiParamOptional.isPresent()) {
fromApiParam(context, apiParamOptional.get());
}
}
@Override
public boolean supports(DocumentationType delimiter) {
return SwaggerPluginSupport.pluginDoesApply(delimiter);
}
private void fromApiParam(ParameterExpansionContext context, ApiParam apiParam) {
String allowableProperty = Strings.emptyToNull(apiParam.allowableValues());
AllowableValues allowable = allowableValues(
Optional.fromNullable(allowableProperty),
context.getFieldType().getErasedType());
maybeSetParameterName(context, apiParam.name())
.description(descriptions.resolve(apiParam.value()))
.defaultValue(apiParam.defaultValue())
.required(apiParam.required())
.allowMultiple(apiParam.allowMultiple())
.allowableValues(allowable)
.parameterAccess(apiParam.access())
.hidden(apiParam.hidden())
.scalarExample(apiParam.example())
.complexExamples(Examples.examples(apiParam.examples()))
.order(SWAGGER_PLUGIN_ORDER)
.build();
}
private void fromApiModelProperty(ParameterExpansionContext context, ApiModelProperty apiModelProperty) {
String allowableProperty = Strings.emptyToNull(apiModelProperty.allowableValues());
AllowableValues allowable = allowableValues(
Optional.fromNullable(allowableProperty),
context.getFieldType().getErasedType());
maybeSetParameterName(context, apiModelProperty.name())
.description(descriptions.resolve(apiModelProperty.value()))
.required(apiModelProperty.required())
.allowableValues(allowable)
.parameterAccess(apiModelProperty.access())
.hidden(apiModelProperty.hidden())
.scalarExample(apiModelProperty.example())
.order(apiModelProperty.position()) //源码这里是: SWAGGER_PLUGIN_ORDER,需要修正
.build();
}
private ParameterBuilder maybeSetParameterName(ParameterExpansionContext context, String parameterName) {
if (!Strings.isNullOrEmpty(parameterName)) {
context.getParameterBuilder().name(parameterName);
}
return context.getParameterBuilder();
}
private AllowableValues allowableValues(final Optional optionalAllowable, Class> fieldType) {
AllowableValues allowable = null;
if (enumTypeDeterminer.isEnum(fieldType)) {
allowable = new AllowableListValues(getEnumValues(fieldType), "LIST");
} else if (optionalAllowable.isPresent()) {
allowable = ApiModelProperties.allowableValueFromString(optionalAllowable.get());
}
return allowable;
}
private List getEnumValues(final Class> subject) {
return Lists.transform(Arrays.asList(subject.getEnumConstants()), new Function