用postman能用,但是swagger的datatype不知道怎么寫
隨着互聯網技術的發展,現在的網站架構基本都由原來的后端渲染,變成了:前端渲染、前后端分離的形態,而且前端和后端在各自的技術道路上越走越遠。
前端和后端的唯一聯系,變成了API接口;API文檔成了前后端開發人員聯系的紐帶,變得越來越重要, swagger就是一款讓你更好的書寫API文檔的框架。
文檔工具
沒有API文檔工具之前,大家都是手寫API文檔的,在什么地方書寫的都有,有在 Word上寫的,有在對應的項目目錄下 readme.md上寫的,每個公司都有每個公司的玩法,無所謂好壞。
現如今市場上書寫API文檔的工具有很多,常見的有 postman、yapi、阿里的RAP 但是能稱之為框架的,估計也只有 swagger了。
swagger 優缺點
- 集成方便,功能強大
- 在線調試與文檔生成
- 代碼耦合,需要注解支持,但不影響程序性能
實體類
swagger
提供了非常齊全的注解,為 POJO
提供了 @ApiModel
、 @ApiModelProperty
,以便更好的渲染最終結果
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.hibernate.validator.constraints.NotEmpty; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; /** * @author tangcheng * 2018/05/02 */ @ApiModel("Swagger使用示例") @Data public class SayHelloReq { @ApiModelProperty(required = true, value = "UserId", dataType = "int", example = "123") @NotNull @Min(value = 1, message = "最小值必須大於1") private Integer userId; @ApiModelProperty(required = true, value = "內容", example = "Best Wish!") @NotEmpty private String content; /** * 普通的數組 * example 中 數組中字符串會自動加上雙引號 */ @ApiModelProperty(example = "[http://1.com,http://2.com]") @NotEmpty private String[] pics; @ApiModelProperty(example = "\"{name:開心}\"") @NotEmpty private String mood; @ApiModelProperty(value = "普通的數組。使用@RequestBody注解,會將對象全部轉換成JSON。" + "如果請求參數不是JSON格式會報錯HttpMessageNotReadableException:\n" + " JSON parse error: Can not deserialize instance of java.lang.Integer[] out of VALUE_STRING token;" , example = "[1,2]", dataType = "type:array") public Integer[] classIds; }
Why does @ApiModelProperty “name” attribute has no effect?
更改Swagger網頁上展示的字段名:
@JsonProperty("phonenumbers") //生成swagger接口參數時用到 @JSONField(name="phonenumbers") //處理請求時用到,如果沒有這個注解,RequstMappingAdapter在將HTTP請求參數set到Request對象中時,會仍然使用以前的phoneNumberDTOList,然后導致phoneNumberDTOList字段值為空 @ApiModelProperty(value = "list of phone numbers") List<PhoneNumberDTO> phoneNumberDTOList = new ArrayList<>();
https://github.com/springfox/springfox/issues/1289
https://stackoverflow.com/questions/53082934/why-does-apimodelproperty-name-attribute-has-no-effect
package com.tangcheng.learning.web.dto.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; /** * spring-boot-cookbook * * @author tangcheng * @date 6/17/2018 11:32 AM */ @ApiModel("用戶信息") @Data @AllArgsConstructor public class UserVO { @ApiModelProperty("主鍵ID") private Long id; @ApiModelProperty("用戶名") private String username; @ApiModelProperty("密碼") private String password; }
@Api:用在請求的類上,表示對類的說明 tags="說明該類的作用,可以在UI界面上看到的注解" value="該參數沒什么意義,在UI界面上也看到,所以不需要配置" 示例: @Api(tags="APP用戶注冊Controller") @ApiOperation:用在請求的方法上,說明方法的用途、作用 value="說明方法的用途、作用" notes="方法的備注說明" 示例: @ApiOperation(value="用戶注冊",notes="手機號、密碼都是必輸項,年齡隨邊填,但必須是數字") @ApiImplicitParams:用在請求的方法上,表示一組參數說明 @ApiImplicitParam:用在@ApiImplicitParams注解中,指定一個請求參數的各個方面 name:參數名 value:參數的漢字說明、解釋 required:參數是否必須傳 paramType:參數放在哪個地方 · header --> 請求參數的獲取:@RequestHeader · query --> 請求參數的獲取:@RequestParam · path(用於restful接口)--> 請求參數的獲取:@PathVariable · body(不常用) · form(不常用) dataType:參數類型,默認String,其它值dataType="Integer" defaultValue:參數的默認值 示例: @ApiImplicitParams({ @ApiImplicitParam(name="mobile",value="手機號",required=true,paramType="form"), @ApiImplicitParam(name="password",value="密碼",required=true,paramType="form"), @ApiImplicitParam(name="age",value="年齡",required=true,paramType="form",dataType="Integer") }) @ApiResponses:用在請求的方法上,表示一組響應 @ApiResponse:用在@ApiResponses中,一般用於表達一個錯誤的響應信息 code:數字,例如400 message:信息,例如"請求參數沒填好" response:拋出異常的類 示例: @ApiOperation(value = "select1請求",notes = "多個參數,多種的查詢參數類型") @ApiResponses({ @ApiResponse(code=400,message="請求參數沒填好"), @ApiResponse(code=404,message="請求路徑沒有或頁面跳轉路徑不對") }) @ApiModel:用於響應類上,表示一個返回響應數據的信息(這種一般用在post創建的時候,使用@RequestBody這樣的場景,請求參數無法使用@ApiImplicitParam注解進行描述的時候) @ApiModelProperty:用在屬性上,描述響應類的屬性 示例: import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; @ApiModel(description= "返回響應數據") public class RestMessage implements Serializable{ @ApiModelProperty(value = "是否成功") private boolean success=true; @ApiModelProperty(value = "返回對象") private Object data; @ApiModelProperty(value = "錯誤編號") private Integer errCode; @ApiModelProperty(value = "錯誤信息") private String message; /* getter/setter */ }
restful 風格接口
注解描述
-
@Api
: 描述Controller
-
@ApiIgnore
: 忽略該Controller
,指不對當前類做掃描 -
@ApiOperation
: 描述Controller
類中的method
接口 -
@ApiParam
: 單個參數描述,與@ApiImplicitParam
不同的是,他是寫在參數左側的。如(@ApiParam(name="username",value="用戶名")Stringusername
) -
@ApiModel
: 描述POJO
對象 -
@ApiProperty
: 描述POJO
對象中的屬性值 -
@ApiImplicitParam
: 描述單個入參信息 -
@ApiImplicitParams
: 描述多個入參信息 -
@ApiResponse
: 描述單個出參信息 -
@ApiResponses
: 描述多個出參信息 -
@ApiError
: 接口錯誤所返回的信息
package com.tangcheng.learning.web.api; import com.tangcheng.learning.web.dto.req.SayHelloReq; import com.tangcheng.learning.web.dto.vo.UserVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.validation.Valid; /** * @author tangcheng * 2018/05/02 */ @Api(tags = "Case:MVC參數校驗DSL ", description = "MVC參數校驗DSL") @Slf4j @RestController @RequestMapping("dsl") public class MVC_DSL_TestController { @GetMapping @ApiOperation(value = "條件查詢") @ApiImplicitParams({ @ApiImplicitParam(name = "username", value = "用戶名", dataType = "string", paramType = "query"), @ApiImplicitParam(name = "password", value = "密碼", dataType = "string", paramType = "query"), }) public UserVO query(String username, String password) { log.info("多個參數用 @ApiImplicitParams"); return new UserVO(1L, username, password); } @GetMapping("/{id}") @ApiOperation(value = "獲取單條信息詳情") @ApiImplicitParam(name = "id", value = "用戶編號", dataType = "long", paramType = "path") public ResponseEntity<UserVO> get(@PathVariable Long id) { log.info("單個參數用 @ApiImplicitParam"); UserVO userVO = new UserVO(id, "swagger", "swagger"); return ResponseEntity.ok(userVO); } @ApiOperation(value = "RequestBody 校驗DSL", notes = "RequestBody 校驗DSL") @PostMapping("/say/hello") public ResponseEntity<String> sayHello(@Valid @RequestBody SayHelloReq request) { log.info("如果是 POST PUT 這種帶 @RequestBody 的可以不用寫 @ApiImplicitParam ,swagger 也會使用默認的參數名作為描述信息"); Integer[] classIds = request.getClassIds(); if (classIds == null) { throw new IllegalArgumentException("classIds is null"); } return ResponseEntity.ok(request.getUserId() + request.getContent() + request.getMood()); } @ApiOperation(value = "upload", notes = "如果添加@RequestBody注解,會報錯:org.springframework.web.HttpMediaTypeNotSupportedException" + "Content type 'multipart/form-data;boundary=----WebKitFormBoundaryKz1zt5cbBPPXkMrt;charset=UTF-8' not supported") @PostMapping("/upload") public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile multipartFile) { log.info("name:{}", multipartFile.getName());//@RequestParam("file") ,引自會返回 file return ResponseEntity.ok(multipartFile.getName() + " " + multipartFile.getOriginalFilename() + " " + multipartFile.getContentType() + " " + multipartFile.getSize()); } /** * attribute 'value' and its alias 'name' are present with values of [headerArg] and [Header中傳的參數], but only one is permitted. * * @param headerArg * @return */ @ApiOperation(value = "@RequestHeader", notes = "@RequestHeader") @GetMapping("header") public ResponseEntity<String> sayHello(@RequestHeader(value = "headerArg", defaultValue = "helloworld") String headerArg) { return ResponseEntity.ok("headerArg:" + headerArg); } }
Spring Boot下,一個好用的starter
<dependency> <groupId>com.spring4all</groupId> <artifactId>swagger-spring-boot-starter</artifactId> <version>1.7.1.RELEASE</version> </dependency>
https://github.com/SpringForAll/spring-boot-starter-swagger
@Data @NoArgsConstructor public class TodoDetailReqVO { private Long categoryId = 1L; @Min(1) @ApiModelProperty(required = true, value = "權重") private Integer weight = 1; @NotEmpty @Size(min = 2, max = 200) @ApiModelProperty(required = true, value = "摘要") private String digest; @Size(max = 1000) @ApiModelProperty(required = true, value = "詳細內容") private String content; }
Swagger中設置默認值:
@JSONField(jsonDirect = true) @ApiModelProperty(value = "標簽", dataType = "json") private String tags; @ApiModelProperty(value = "經驗", example = "{}") private String exps;
Swagger-ui.html中展示情況:
Data Types
The data type of a schema is defined by the type
keyword, for example, type: string
.
OpenAPI defines the following basic types:
These types exist in most programming languages, though they may go by different names. Using these types, you can describe any data structures.
Note that there is no null
type; instead, the nullable
attribute is used as a modifier of the base type.
Additional type
-specific keywords can be used to refine the data type, for example, limit the string length or specify an enum
of possible values.
https://swagger.io/docs/specification/data-models/data-types/
前言
Swagger 是一款RESTFUL接口的文檔在線自動生成+功能測試功能軟件。本文簡單介紹了在項目中集成swagger的方法和一些常見問題。如果想深入分析項目源碼,了解更多內容,見參考資料。Swagger 是一個規范和完整的框架,用於生成、描述、調用和可視化 RESTful 風格的 Web 服務。總體目標是使客戶端和文件系統作為服務器以同樣的速度來更新。文件的方法,參數和模型緊密集成到服務器端的代碼,允許API來始終保持同步。Swagger 讓部署管理和使用功能強大的API從未如此簡單。
一、使用介紹
什么是 Swagger?
Swagger™的目標是為REST APIs 定義一個標准的,與語言無關的接口,使人和計算機在看不到源碼或者看不到文檔或者不能通過網絡流量檢測的情況下能發現和理解各種服務的功能。當服務通過Swagger定義,消費者就能與遠程的服務互動通過少量的實現邏輯。類似於低級編程接口,Swagger去掉了調用服務時的很多猜測。
瀏覽 Swagger-Spec 去了解更多關於Swagger 項目的信息,包括附加的支持其他語言的庫。
如何集成Swagger-springmvc到我們的項目中?
依賴:
Maven
<dependency> <groupId>com.mangofactory</groupId> <artifactId>swagger-springmvc</artifactId> <version>0.9.4</version> </dependency>
Gradle
repositories { jcenter() } compile "com.mangofactory:swagger-springmvc:0.9.4"
使用:
要最快捷地啟動swagger-springmvc項目並且使用默認設置,推薦的方式是使用SwaggerSpringMvc插件
Spring Java Configuration
@Configuration @EnableWebMvc @EnableSwagger @ComponentScan("com.myapp.packages") public class WebAppConfig { ... }
Spring xml Configuration
<mvc:annotation-driven/> <!-- Required so swagger-springmvc can access spring's RequestMappingHandlerMapping --> <bean class="com.mangofactory.swagger.configuration.SpringSwaggerConfig" />
SwaggerSpringMvcPlugin XML Configuration
為了使用這個插件,你需要創造一個spring Java配置類。使用spring的 @Configuration ,這個配置類必須被定義到你的xml上下文
<!-- Required so swagger-springmvc can access spring's RequestMappingHandlerMapping --> <mvc:annotation-driven/> <bean class="com.yourapp.configuration.MySwaggerConfig"/>
@Configuration @EnableSwagger //Loads the spring beans required by the framework public class MySwaggerConfig { private SpringSwaggerConfig springSwaggerConfig; /** * Required to autowire SpringSwaggerConfig */ @Autowired public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) { this.springSwaggerConfig = springSwaggerConfig; } /** * Every SwaggerSpringMvcPlugin bean is picked up by the swagger-mvc framework - allowing for multiple * swagger groups i.e. same code base multiple swagger resource listings. */ @Bean public SwaggerSpringMvcPlugin customImplementation(){ return new SwaggerSpringMvcPlugin(this.springSwaggerConfig) .includePatterns(".*pet.*"); } }
SwaggerSpringMvcPlugin Spring Java Configuration
使用@EnableSwagger注解
自動注入SpringSwaggerConfig
定義一個或多個SwaggerSpringMvcPlugin實例,通過springs @Bean注解
@Configuration @EnableWebMvc @EnableSwagger @ComponentScan("com.myapp.controllers") public class CustomJavaPluginConfig { private SpringSwaggerConfig springSwaggerConfig; @Autowired public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) { this.springSwaggerConfig = springSwaggerConfig; } @Bean //Don't forget the @Bean annotation public SwaggerSpringMvcPlugin customImplementation(){ return new SwaggerSpringMvcPlugin(this.springSwaggerConfig) .apiInfo(apiInfo()) .includePatterns(".*pet.*"); } private ApiInfo apiInfo() { ApiInfo apiInfo = new ApiInfo( "My Apps API Title", "My Apps API Description", "My Apps API terms of service", "My Apps API Contact Email", "My Apps API Licence Type", "My Apps API License URL" ); return apiInfo; } }
二、碰到的問題
關於@API注解
在Swagger Annotation中:
API表示一個開放的API,可以通過description簡要描述該API的功能。在一個@API下,可有多個@ApiOperation,表示針對該API的CRUD操作。在ApiOperation Annotation中可以通過value,notes描述該操作的作用,response描述正常情況下該請求的返回對象類型。
在一個ApiOperation下,可以通過ApiResponses描述該API操作可能出現的異常情況。
ApiParam用於描述該API操作接受的參數類型
再接着,為項目的Model對象添加Swagger Annotation,這樣Swagger Scanner可以獲取更多關於Model對象的信息。
@ApiModel(value = "A SayingRepresentation is a representation of greeting") @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) public class SayingRepresentation { private long id; @ApiModelProperty(value = "greeting content", required = true) private String content; public SayingRepresentation(long id, String content) { this.id = id; this.content = content; } public long getId() { return id; } public String getContent() { return content; }
通過上面的步驟,項目已經具備了提供Swagger格式的API信息的能力,接下來,我們把這些信息和Swagger UI集成,以非常美觀,實用的方式把這些API信息展示出來。
和Swagger UI的集成
首先,從github(https://github.com/wordnik/swagger-ui)上下載Swagger-UI, 把該項目dist目錄下的內容拷貝到項目的resources的目錄assets下。
然后,修改index.html, 把Swagger UI對象中的URL替換為自己的API路徑。
window.swaggerUi = new SwaggerUi({ url: "/api/api-docs", dom_id: "swagger-ui-container",
最后,為了能訪問到該頁面,還需要在Service的Initialize方法中,添加AssetsBundle
public void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) { //指定配置文件的名字 bootstrap.setName("helloWorld"); bootstrap.addBundle(new AssetsBundle("/assets", "/", "index.html")); }
最后的效果圖:
三、評價
Swagger可以充當前后端交流的重要橋梁,方便快捷。很實用。
Swagger項目允許你生產,顯示和消費你自己的RESTful服務。不需要代理和第三方服務。是一個依賴自由的資源集合,它能通過Swagger API動態的生成漂亮的文檔和沙盒,因為Swagger UI沒有依賴,你可以把他部署到任何服務器環境或者是你自己的機器。
四、參考資料
GitHub:
swagger-springmvc:https://github.com/martypitt/swagger-springmvc
swagger-ui:https://github.com/swagger-api/swagger-ui
swagger-core:https://github.com/swagger-api/swagger-core
swagger-spec:https://github.com/swagger-api/swagger-spec
http://blog.csdn.net/wangnan9279/article/details/44541665
supportHeaderParams默認為false,而我用的是swagger2,不需要配置靜態的那些東西,所以我在SwaggerConfig添加了幾行代碼:
- @EnableSwagger2
- @EnableWebMvc
- @ComponentScan("com.g.web")
- public class SwaggerConfig {
- @Bean
- public Docket api(){
- ParameterBuilder tokenPar = new ParameterBuilder();
- List<Parameter> pars = new ArrayList<Parameter>();
- tokenPar.name("x-access-token").description("令牌").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
- pars.add(tokenPar.build());
- return new Docket(DocumentationType.SWAGGER_2)
- .select()
- .apis(RequestHandlerSelectors.any())
- .paths(PathSelectors.regex("/api/.*"))
- .build()
- .globalOperationParameters(pars)
- .apiInfo(apiInfo());
- }
- private ApiInfo apiInfo() {
- return new ApiInfoBuilder()
- .title("后台接口文檔與測試")
- .description("這是一個給app端人員調用server端接口的測試文檔與平台")
- .version("1.0.0")
- .termsOfServiceUrl("http://terms-of-services.url")
- //.license("LICENSE")
- //.licenseUrl("http://url-to-license.com")
- .build();
- }
- }
前四行代碼是添加head參數的,前台效果是這樣的: