關(guān)于swagger配置及踩坑@Api參數(shù)postion無效解決接口排序問題

添加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很好用的一個(gè)類庫--> <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() {
// 配置全局參數(shù)返回狀態(tài)
java.util.List<ResponseMessage> resMsgList = Arrays.asList(
new ResponseMessageBuilder().code(200).message("成功!").build(),
new ResponseMessageBuilder().code(-1).message("失敗!").build(),
new ResponseMessageBuilder().code(401).message("參數(shù)校驗(yàn)錯(cuò)誤!").build(),
new ResponseMessageBuilder().code(403).message("沒有權(quán)限操作,請(qǐng)后臺(tái)添加相應(yīng)權(quán)限!").build(),
new ResponseMessageBuilder().code(500).message("服務(wù)器內(nèi)部異常,請(qǐng)稍后重試!").build(),
new ResponseMessageBuilder().code(501).message("請(qǐng)登錄!").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<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 = "獲取單個(gè)用戶")
@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 = "更新單個(gè)用戶")
@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();
}
}
請(qǐng)求對(duì)象類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;
/**
* 用戶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】字段長(zhǎng)度應(yīng)<255")
private String email;
/** 姓 */
@ApiModelProperty(value = "姓", position = 4)
@NotBlank(message = "【姓】不能為空")
@Size(max = 10, message = "【姓】字段長(zhǎng)度應(yīng)<10")
private String firstname;
/** 名 */
@ApiModelProperty(value = "名", position = 5)
@Size(max = 20, message = "【名】字段長(zhǎng)度應(yīng)<20")
private String lastname;
/** 開始時(shí)間 */
@ApiModelProperty(value = "開始時(shí)間", 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;
/** 結(jié)束時(shí)間 */
@ApiModelProperty(value = "結(jié)束時(shí)間", 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;
/** 轉(zhuǎn)換成實(shí)體類 */
public User convertToEntity() {
return new UserInputDTOConver().convert(this);
}
/** 轉(zhuǎn)換成InputDTO */
public UserInputDTO convertFor(User bean) {
return new UserInputDTOConver().reverse().convert(bean);
}
/** Conver轉(zhuǎn)換類 */
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;
}
}
}
響應(yīng)對(duì)象類
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結(jié)果DTO
* @author ex-lidy001
*
*/
@ApiModel(value = "返回JSON對(duì)象")
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(Include.NON_NULL)
public class JsonResultDTO {
@ApiModelProperty(value = "狀態(tài)碼;200:成功")
private Integer code;
@ApiModelProperty(value = "狀態(tài)說明")
private String msg;
@ApiModelProperty(value = "總記錄數(shù)")
private Long total;
@ApiModelProperty(value = "返回?cái)?shù)據(jù)")
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, "參數(shù)校驗(yàn):" + msg);
}
public static JsonResultDTO unAuth() {
return new JsonResultDTO(403, "沒有權(quán)限!");
}
public static JsonResultDTO unlogin() {
return new JsonResultDTO(501, "請(qǐng)登錄!");
}
public static JsonResultDTO exception(String msg) {
return new JsonResultDTO(500, "系統(tǒng)內(nèi)部錯(cuò)誤:" + msg);
}
}
最后,上效果圖
請(qǐng)求路徑http://localhost:8080/swagger-ui.html
請(qǐng)求

響應(yīng)

最后還有個(gè)坑
在使用最新版的springfox-swagger2 2.9.2``position排序的時(shí)候沒有生效,后來在網(wǎng)上找了相關(guān)資料,需要重寫swagger的兩個(gè)類,具體如下。
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轉(zhuǎn)換成Swagger 類, 根據(jù)order進(jìn)行排序
*/
@Primary //同一個(gè)接口,可能會(huì)有幾種不同的實(shí)現(xiàn)類,而默認(rèn)只會(huì)采取其中一種的情況下
@Component("ServiceModelToSwagger2Mapper")
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomModelToSwaggerMapper extends ServiceModelToSwagger2MapperImpl {
@Override
protected List<Parameter> parameterListToParameterList(List<springfox.documentation.service.Parameter> list) {
//list需要根據(jù)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傳入請(qǐng)求參數(shù)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<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();
}
});
}
}
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java 中JFinal getModel方法和數(shù)據(jù)庫使用出現(xiàn)問題解決辦法
這篇文章主要介紹了java 中JFinal getModel方法和數(shù)據(jù)庫使用出現(xiàn)問題解決辦法的相關(guān)資料,需要的朋友可以參考下2017-04-04
springboot?整合dubbo3開發(fā)rest應(yīng)用的場(chǎng)景分析
這篇文章主要介紹了springboot?整合dubbo3開發(fā)rest應(yīng)用,本文通過實(shí)際的案例演示下基于?dubbo的2.7.X的版本,整合springboot完成一個(gè)rest應(yīng)用開發(fā)和調(diào)用的案例,需要的朋友可以參考下2022-09-09
基于Comparator對(duì)象集合實(shí)現(xiàn)多個(gè)條件按照優(yōu)先級(jí)的比較
這篇文章主要介紹了基于Comparator對(duì)象集合實(shí)現(xiàn)多個(gè)條件按照優(yōu)先級(jí)的比較,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Java 實(shí)現(xiàn)一個(gè)漢諾塔實(shí)戰(zhàn)練習(xí)
漢諾塔是源于印度一個(gè)古老傳說的益智玩具。大梵天創(chuàng)造世界時(shí)做了三根石柱,在一根柱子上從下往上按大小順序摞著64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。并且規(guī)定,在小圓盤上不能放大圓盤,三根柱子之間一次只能移動(dòng)一個(gè)圓盤2021-10-10
詳談springboot過濾器和攔截器的實(shí)現(xiàn)及區(qū)別
今天小編就為大家分享一篇詳談springboot過濾器和攔截器的實(shí)現(xiàn)及區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-08-08
SpringBoot整合screw實(shí)現(xiàn)自動(dòng)生成數(shù)據(jù)庫設(shè)計(jì)文檔
使用navicat工作的話,導(dǎo)出的格式是excel不符合格式,還得自己整理。所以本文將用screw工具包,整合到springboot的項(xiàng)目中便可以自動(dòng)生成數(shù)據(jù)庫設(shè)計(jì)文檔,非常方便,下面就分享一下教程2022-11-11
WMTS中TileMatrix與ScaleDenominator淺析
這篇文章主要為大家介紹了WMTS中TileMatrix與ScaleDenominator淺析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
java實(shí)現(xiàn)多人多牌數(shù)比較游戲
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)多人多牌數(shù)比較游戲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01

