關(guān)于swagger配置及踩坑@Api參數(shù)postion無(wú)效解決接口排序問(wèn)題
添加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è)類庫(kù)--> <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("沒(méi)有權(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("接口文檔說(shuō)明") .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 = "分頁(yè)獲取用戶列表") @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; /** 開(kāi)始時(shí)間 */ @ApiModelProperty(value = "開(kāi)始時(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)說(shuō)明") 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, "沒(méi)有權(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í)候沒(méi)有生效,后來(lái)在網(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無(wú)效 * 這里,將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ù)庫(kù)使用出現(xiàn)問(wèn)題解決辦法
這篇文章主要介紹了java 中JFinal getModel方法和數(shù)據(jù)庫(kù)使用出現(xiàn)問(wèn)題解決辦法的相關(guān)資料,需要的朋友可以參考下2017-04-04springboot?整合dubbo3開(kāi)發(fā)rest應(yīng)用的場(chǎng)景分析
這篇文章主要介紹了springboot?整合dubbo3開(kāi)發(fā)rest應(yīng)用,本文通過(guò)實(shí)際的案例演示下基于?dubbo的2.7.X的版本,整合springboot完成一個(gè)rest應(yīng)用開(kāi)發(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-07Java 實(shí)現(xiàn)一個(gè)漢諾塔實(shí)戰(zhàn)練習(xí)
漢諾塔是源于印度一個(gè)古老傳說(shuō)的益智玩具。大梵天創(chuàng)造世界時(shí)做了三根石柱,在一根柱子上從下往上按大小順序摞著64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開(kāi)始按大小順序重新擺放在另一根柱子上。并且規(guī)定,在小圓盤上不能放大圓盤,三根柱子之間一次只能移動(dòng)一個(gè)圓盤2021-10-10詳談springboot過(guò)濾器和攔截器的實(shí)現(xiàn)及區(qū)別
今天小編就為大家分享一篇詳談springboot過(guò)濾器和攔截器的實(shí)現(xiàn)及區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08SpringBoot整合screw實(shí)現(xiàn)自動(dòng)生成數(shù)據(jù)庫(kù)設(shè)計(jì)文檔
使用navicat工作的話,導(dǎo)出的格式是excel不符合格式,還得自己整理。所以本文將用screw工具包,整合到springboot的項(xiàng)目中便可以自動(dòng)生成數(shù)據(jù)庫(kù)設(shè)計(jì)文檔,非常方便,下面就分享一下教程2022-11-11WMTS中TileMatrix與ScaleDenominator淺析
這篇文章主要為大家介紹了WMTS中TileMatrix與ScaleDenominator淺析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03java實(shí)現(xiàn)多人多牌數(shù)比較游戲
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)多人多牌數(shù)比較游戲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01