# 02.7-WebServic and APIs-Lesson7: Documentation ###### tags: `Udacity` [ToC] # 01 Introduction {%youtube Bh2uVKl0AOs%} ==ShannonNote== > * 通常寫程式需要documentation, documentation is a good way to communicate to others how they can use your APIs/ > * This Lesson: 目的在學使用Swagger自動產生documetation from your java code and some of its components like Swagger UI. # 02 Importance It’s very important to document your REST APIs. There could be other developers that need to use your APIs or you may have customers that want to use your APIs to help speed up their development time. Documentation is a good way to communicate to others how they can use your APIs. Documentation communicates the operations available, the format for requesting data and the format data will be returned in. Typically, the initial creation of documentation is easy, but the on-going maintenance of keeping that documentation up to date is hard. Imagine having to manually update documentation every time your code changes! It's good to use tools that automatically inspect your code and update the documentation. ==ShannonNote== > * What Documentation do? * 有哪些操作來接收什麼樣的data? * requesting data的format是甚麼樣? * API回傳的format是甚麼? > * Swagger 可以檢查your code和更新documentation for you. # 03 Swagger {%youtube iQhT4-7GCJU%} One of the most popular API documentation specifications is OpenApi, formerly known as the Swagger Specification. OpenAPI allows you to describe your API using JSON or YAML. Swagger is the name associated with some of the most well-known, and widely used tools for implementing the OpenAPI specification. Swagger helps you design, build, document and consume REST APIs. SpringFox is a Swagger integration for the Spring Framework. * Swagger Editor – A browser-based editor where you can write OpenAPI specs. * Swagger UI – A web application that renders OpenAPI specs as interactive API documentation. * Swagger Codegen – A tool that generates server stubs and client libraries from an OpenAPI spec. # 04 Swagger UI {%youtube %} Dynamically generated documentation from a Swagger-compliant API is displayed in the Swagger UI, which consists of HTML, JavaScript, and CSS files. The documentation files are bundled by Swagger UI to display the API in a web browser. Besides rendering documentation, Swagger UI allows other API developers or consumers to interact with the API’s resources without having any of their implementation logic in place. **Dependencies** ```xml <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> <scope>compile</scope> </dependency> ``` ==ShannonNote== * 參考: Swagger五分鐘搞懂 https://blog.csdn.net/i6448038/article/details/77622977 # 05 Swagger Configuration {%youtube gUdz6elCaCE%} The configuration of Swagger mainly centers around the Docket bean. ```java @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) .build(); } } ``` Swagger also provides some default values in its response that you can customize, such as “Api Documentation”, “Created by Contact Email”, “Apache 2.0”. To change these values, you can use the `apiInfo(ApiInfo apiInfo)` method. # 06 Case Study: Swagger Configuration {%youtube 8PQOmdh5bnQ%} The case study to be documented is a REST API that retrieves a list of locations from a database. The code can be cloned from [GitLab](https://gitlab.com/videolearning/udacity-java/tree/master/Lesson7-documenting). The REST API has the following components: * Controller - LocationController.java * Service - LocationService.java and LocationServiceImpl.java * CRUD Repository - LocationRepository.java * Domain Entity/Model - Location.java * Swagger Config - SwaggerConfig.java * H2 Database accessible via http://localhost:8080/h2/ * Tomcat Server accessible via http://localhost:8080 * Swagger UI via http://localhost:8080/swagger-ui.html # 07 Customizing Swagger {%youtube 929vxDvLCUY%} ==ShannonNote== >* 如果你想要客製化去設定一些Swagger像是 * 不想要expose entire API * 想要restict Swagger's response * Passing parameter to apis() and paths() method > 下一章舉例 # 08 Case Study: Error Messages ==ShannonNote== Swagger允許複寫globally response messages of Http methods. 因此Springfox會提供default的訊息給特定的error codes像是404 error code。但你可能不會想用default response messages for your api. * 如何克制化default response message? 1. 關閉defaultResponseMessage`.useDefaultResponseMessages(false)` ```java @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) //this returns an instance of the API selector builder //which essentially controls the endpoints exposed by Swagger .select() //for the request handlers and the past selectors for any .apis(RequestHandlerSelectors.any()) //documentation available for the entire locations API .paths(PathSelectors.any()) .build() .useDefaultResponseMessages(false); } ``` 2. 到`LocationController.java`去新增你自己的客製化Response message * 新增`@ApiResponses`標籤,填寫`@ApiResponse`內容 ```java @RestController @ApiResponses(value = { @ApiResponse(code=400, message = "This is a bad request, please follow the API documentation for the proper request format."), @ApiResponse(code=401, message = "Due to security constraints, your access request cannot be authorized. "), @ApiResponse(code=500, message = "The server is down. Please make sure that the Location microservice is running.") }) public class LocationController { private LocationService locationService; @Autowired public void setLocationService(LocationService locationService) { this.locationService = locationService; } @GetMapping("/location") public ResponseEntity<List<Location>> getAllLocations() { List<Location> list = locationService.retrieveLocations(); return new ResponseEntity<List<Location>>(list, HttpStatus.OK); } @GetMapping("/location/{id}") public ResponseEntity<Location> getLocation(@PathVariable("id") long id) { return new ResponseEntity<Location>(locationService.retrieveLocation(id), HttpStatus.OK); } } ``` # 09 Lab: Document Your Dog API **Lab: Document Your Dog API** Reuse your Dog REST API code from Lesson 2 and document it using Swagger. * Step 1: Add the necessary dependencies to the Maven POM file that include Springfox’s implementation of Swagger. * Step 2: Configure Swagger using a Docket Bean. * Step 3: Utilize Swagger UI to review and test your API. # 10 Lab Solution: Document Your Dog API **Solution: Document Your Dog API** Below, we'll walk through each step of the lab and look at one potential way to implement the lab. Even if you get stuck, you should always first try to work through the lab without the solution before coming here, so that you can best learn the related skills and be ready for the project at the end of the course. **Step 1: Add the necessary dependencies for Swagger.** First, I have started by copying over my code from the Dog REST API (I've excluded the security piece from earlier for simplicity). From there, I just need to add two dependencies: ```xml <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> <scope>compile</scope> </dependency> ``` **Step 2: Configure Swagger using a Docket Bean.** Now, I'll add a `config` package to my API and add a `SwaggerConfig` class within it. This file can be pretty much the same as the Case Study, although I'll alter it so the API Info matches the Dog API. I have not added any custom error messages here, although they can always be useful in your own implementations! ```xml package com.udacity.DogRestApi.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.Collections; @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) .build() .apiInfo(apiInfo()); } private ApiInfo apiInfo() { return new ApiInfo( "Dog REST API", "This API returns a list of dogs.", "1.0", "http://www.udacity.com/tos", new Contact("Udacious Student", "www.udacity.com", "myeaddress@udacity.com"), "License of API", "http://www.udacity.com/license", Collections.emptyList()); } } ``` **Step 3: Utilize Swagger UI to review and test your API.** Now, you should be able to head to http://localhost:8080/swagger-ui.html#/ and test out your API. > An example of the Dog REST API Swagger documentation page ![](https://i.imgur.com/I2MFx91.png) > Swagger has documented the available GET requests for us ![](https://i.imgur.com/s9xY1Cj.png) If you click on one of the available commands for your API, you should be able to Try it out and test that it works correctly. Depending on which you test, you may need to provide information (such as the `id` for `getBreedByID`), or be able to immediately Execute the query. > An example from an execution of `getAllDogs` in Swagger ![](https://i.imgur.com/GG2OpcU.png) As you can see, automatically documenting your API with Swagger requires only a few lines of code, yet provides some incredibly useful functionality. **Full Solution** If you'd like the full solution code all in one place, you can download it through the link below. Supporting Materials [DocumentDogApi](https://video.udacity-data.com/topher/2019/August/5d4c7199_documentdogapi/documentdogapi.zip) # 結論 > 建立在你已經建立好REST API才可以使用 請參考location REST API ## 1. 建立Swagger-ui的方法 * step1 新增swagger dependency ```xml <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> <scope>compile</scope> </dependency> ``` * step2 建立config package,在裡面新增`SwaggerConfig.java` 1. 新增標籤來啟用Swagger `@EnableSwagger2` 2. 新增 `api()`目的在整合swagger到目前的spring boot project 3. 可以客製化一些default values作為回應 ```java @Configuration @EnableSwagger2 public class SwaggerConfig { //這是為了integrate Swagger into any existing spring boot project @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) //this returns an instance of the API selector builder //which essentially controls the endpoints exposed by Swagger .select() //for the request handlers and the past selectors for any .apis(RequestHandlerSelectors.any()) //documentation available for the entire locations API .paths(PathSelectors.any()) .build() .useDefaultResponseMessages(false); } //Swagger provide some default values in its reponse that you can customize here private ApiInfo apiInfo() { return new ApiInfo( "Location API", "This API returns a list of airport locations.", "1.0", "http://www.udacity.com/tos", new Contact("Kesha Williams", "www.udacity.com", "myeaddress@udacity.com"), "License of API", "http://www.udacity.com/license", Collections.emptyList()); } } ``` * step3 執行後 查詢 http://localhost:8080/swagger-ui.html ## 2. 針對API Error Code 制定訊息 1. 關閉defaultResponseMessage`.useDefaultResponseMessages(false)` ```java @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) //this returns an instance of the API selector builder //which essentially controls the endpoints exposed by Swagger .select() //for the request handlers and the past selectors for any .apis(RequestHandlerSelectors.any()) //documentation available for the entire locations API .paths(PathSelectors.any()) .build() .useDefaultResponseMessages(false); } ``` 2. 到`LocationController.java`去新增你自己的客製化Response message * 新增`@ApiResponses`標籤,填寫`@ApiResponse`內容 ```java @RestController @ApiResponses(value = { @ApiResponse(code=400, message = "This is a bad request, please follow the API documentation for the proper request format."), @ApiResponse(code=401, message = "Due to security constraints, your access request cannot be authorized. "), @ApiResponse(code=500, message = "The server is down. Please make sure that the Location microservice is running.") }) public class LocationController { private LocationService locationService; @Autowired public void setLocationService(LocationService locationService) { this.locationService = locationService; } @GetMapping("/location") public ResponseEntity<List<Location>> getAllLocations() { List<Location> list = locationService.retrieveLocations(); return new ResponseEntity<List<Location>>(list, HttpStatus.OK); } @GetMapping("/location/{id}") public ResponseEntity<Location> getLocation(@PathVariable("id") long id) { return new ResponseEntity<Location>(locationService.retrieveLocation(id), HttpStatus.OK); } } ```