关于swagger配置及踩坑@Api参数postion无效解决接口排序问题

2022-11-13 10:11:49 接口 排序 无效

添加maven依赖

<!-- 集成swagger2 -->
<dependency>
	<groupId>io.springfox</groupId>
	<artifactId>springfox-swagger2</artifactId>
	<version>2.9.2</version>
</dependency>
<dependency>
	<groupId>io.springfox</groupId>
	<artifactId>springfox-swagger-ui</artifactId>
	<version>2.9.2</version>
</dependency>
<!--Google很好用的一个类库-->
<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>27.0.1-jre</version>
</dependency>

添加配置类

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<ResponseMessage> 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;
 

@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<String> 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<User> 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<UserInputDTO> 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<User> 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<UserInputDTO> 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();
	}	
}
 

请求对象类DTO

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;
 

@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;
 
	
	@ApiModelProperty(hidden = true)
	private Integer id;
 
	
	@ApiModelProperty(value = "是否激活", position = 1)
	private Boolean active;
 
	
	@ApiModelProperty(value = "年龄", position = 2)
	private Integer age;
 
	
	@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);
	}
 
	
	public UserInputDTO convertFor(User bean) {
		return new UserInputDTOConver().reverse().convert(bean);
	}
	
	
	private static class UserInputDTOConver extends Converter<UserInputDTO, User> {
		@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;
 

@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;
 

@Primary //同一个接口,可能会有几种不同的实现类,而默认只会采取其中一种的情况下
@Component("ServiceModelToSwagger2Mapper")
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomModelToSwaggerMapper extends ServiceModelToSwagger2MapperImpl {
 
    @Override
    protected List<Parameter> parameterListToParameterList(List<springfox.documentation.service.Parameter> 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;
 

@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<ApiModelProperty> apiModelPropertyOptional = context.findAnnotation(ApiModelProperty.class);
        if (apiModelPropertyOptional.isPresent()) {
            fromApiModelProperty(context, apiModelPropertyOptional.get());
        }
        Optional<ApiParam> 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<String> 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<String> getEnumValues(final Class<?> subject) {
        return Lists.transform(Arrays.asList(subject.getEnumConstants()), new Function<Object, String>() {
            @Override
            public String apply(final Object input) {
                return input.toString();
            }
        });
    }
}
 

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。 

相关文章