Spring Boot integrates Swagger2 project practice, springswagger2
1. Swagger Introduction
In the previous article, we introduced Spring Boot's support for Restful APIs. We will continue to discuss this topic. However, we will not discuss how to implement Restful APIs here, the maintenance of Restful API documentation is discussed.
In daily work, we often need to provide interfaces to the front-end (WEB, IOS, Android) or third parties. At this time, we need to provide them with a detailed API Description document. However, maintaining a detailed document is not a simple task. First, writing a detailed document is a very time-consuming and laborious task. On the other hand, because the code and the document are separated, it is easy to cause inconsistency between the document and the code. In this article, we will share a way to maintain API documentation, that is, automatically generate Restuful API documentation through Swagger.
So what is Swagger? Let's take a look at the official description:
THE WORLD'S MOST POPULAR API TOOLINGSwagger is the world's largest framework of API developer tools for the OpenAPI Specification(OAS),enabling development across the entire API lifecycle, from design and documentation, to test and deployment.
This section tells us that Swagger is the most popular API Tool in the world. Swagger aims to support the development of the entire API lifecycle, including design, documentation, testing, and deployment. In this article, we will use Swagger's document management and testing functions.
After having a basic understanding of the role of Swagger, let's take a look at how to use it.
Ii. Integrate Swagger with Spring boot
Step 1: introduce the corresponding jar package:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.6.0</version></dependency><dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.6.0</version></dependency>
Step 2: Configure basic information:
@ Configuration @ EnableSwagger2public class Swagger2Config {@ Bean public Docket createRestApi () {return new Docket (DocumentationType. SWAGGER_2 ). apiInfo ()). select (). apis (RequestHandlerSelectors. basePackage ("com. pandy. blog. rest ")). paths (PathSelectors. regex ("/rest /. *")). build ();} private ApiInfo apiInfo () {return new ApiInfoBuilder (). title ("Blog system Restful API "). description ("Blog system Restful API "). termsOfServiceUrl ("http: // 127.0.0.1: 8080 /"). contact ("liuxiaopeng "). version ("1.0 "). build ();}}
The basic configuration is the description of the entire API document and some global configurations, which work for all interfaces. Two annotations are involved:
@ Configuration indicates that this is a Configuration class and is an annotation provided by JDK. It has been described in the previous article.
@ EnableSwagger2 is used to enable Swagger2 functions.
In this configuration class, I instantiate a Docket object, which mainly includes three aspects of information:
(1) The description of the entire API, that is, the information contained in the ApiInfo object, which will be displayed on the page.
(2) Specify the package name for generating the API document.
(3) Specify the path for generating the API. API generation by path supports four modes. For details, refer to the source code:
public class PathSelectors { private PathSelectors() { throw new UnsupportedOperationException(); } public static Predicate<String> any() { return Predicates.alwaysTrue(); } public static Predicate<String> none() { return Predicates.alwaysFalse(); } public static Predicate<String> regex(final String pathRegex) { return new Predicate<String>() { public boolean apply(String input) { return input.matches(pathRegex); } }; } public static Predicate<String> ant(final String antPattern) { return new Predicate<String>() { public boolean apply(String input) { AntPathMatcher matcher = new AntPathMatcher(); return matcher.match(antPattern, input); } }; }}
From the source code, Swagger supports four methods: All paths are generated, no paths are generated, and regular expression matching and ant pattern matching. You may be familiar with the first three methods and the last ant match. If you are not familiar with ant, ignore them. The first three methods should be enough for your daily work.
With the above configuration, we can see the effect. I have an ArticleRestController class under the com. pandy. blog. rest package. The source code is as follows:
Start Spring boot and access: http: // 127.0.0.1: 8080/swagger-ui.html to see the following results:
On this page, you can see that all interfaces except the last API/test/{id} generate the corresponding document, the last interface does not meet the configured path -- "/rest /. * ", so no document is generated.
We can also look at each specific interface. Here we take the "POST/rest/article" interface as an example:
As you can see, Swagger generates examples of returned results and request parameters for each interface, and can directly access the interface through the "try it out" below. You can test the interface. Swagger is powerful and easy to configure.
@RestControllerpublic class ArticleRestController { @Autowired private ArticleService articleService; @RequestMapping(value = "/rest/article", method = POST, produces = "application/json") public WebResponse<Map<String, Object>> saveArticle(@RequestBody Article article) { article.setUserId(1L); articleService.saveArticle(article); Map<String, Object> ret = new HashMap<>(); ret.put("id", article.getId()); WebResponse<Map<String, Object>> response = WebResponse.getSuccessResponse(ret); return response; } @RequestMapping(value = "/rest/article/{id}", method = DELETE, produces = "application/json") public WebResponse<?> deleteArticle(@PathVariable Long id) { Article article = articleService.getById(id); article.setStatus(-1); articleService.updateArticle(article); WebResponse<Object> response = WebResponse.getSuccessResponse(null); return response; } @RequestMapping(value = "/rest/article/{id}", method = PUT, produces = "application/json") public WebResponse<Object> updateArticle(@PathVariable Long id, @RequestBody Article article) { article.setId(id); articleService.updateArticle(article); WebResponse<Object> response = WebResponse.getSuccessResponse(null); return response; } @RequestMapping(value = "/rest/article/{id}", method = GET, produces = "application/json") public WebResponse<Article> getArticle(@PathVariable Long id) { Article article = articleService.getById(id); WebResponse<Article> response = WebResponse.getSuccessResponse(article); return response; } @RequestMapping(value = "/test/{id}", method = GET, produces = "application/json") public WebResponse<?> getNoApi(){ WebResponse<?> response = WebResponse.getSuccessResponse(null); return response; }}
Iii. Detailed Swagger API Configuration
However, you may have some questions:
The first problem: the returned results and request parameters do not have a textual description. Can this be configured?
The second problem: this request parameter should be directly reflected Based on the object, but not every attribute of the object is required. In addition, the parameter value does not necessarily meet our needs, can this be configured?
The answer is yes. Now let's solve these two problems. Let's look at the configuration code:
Package com. pandy. blog. rest; import com. pandy. blog. dto. webResponse; import com. pandy. blog. po. article; import com. pandy. blog. service. articleService; import io. swagger. annotations. apiImplicitParam; import io. swagger. annotations. apiImplicitParams; import io. swagger. annotations. apiOperation; import io. swagger. annotations. apiResponse; import io. swagger. annotations. apiResponses; import org. springframework. beans. f Acloud. annotation. autowired; import org. springframework. context. annotation. profile; import org. springframework. web. bind. annotation. pathVariable; import org. springframework. web. bind. annotation. requestBody; import org. springframework. web. bind. annotation. requestMapping; import org. springframework. web. bind. annotation. restController; import java. util. hashMap; import java. util. list; import java. util. map; import Static org. springframework. web. bind. annotation. requestMethod. DELETE; import static org. springframework. web. bind. annotation. requestMethod. GET; import static org. springframework. web. bind. annotation. requestMethod. POST; import static org. springframework. web. bind. annotation. requestMethod. PUT; @ RestController @ RequestMapping ("/rest") public class ArticleRestController {@ Autowired private ArticleService artic LeService; @ RequestMapping (value = "/article", method = POST, produces = "application/json") @ ApiOperation (value = "add article ", notes = "Add new Article", tags = "Article", httpMethod = "POST") @ ApiImplicitParams ({@ ApiImplicitParam (name = "title ", value = "article title", required = true, dataType = "String"), @ ApiImplicitParam (name = "summary", value = "Article summary", required = true, dataType = "String"), @ ApiImplicitParam (na Me = "status", value = "Publish status", required = true, dataType = "Integer")}) @ ApiResponses ({@ ApiResponse (code = 200, message = "successful", response = WebResponse. class),}) public WebResponse <Map <String, Object> saveArticle (@ RequestBody Article article) {articleService. saveArticle (article); Map <String, Object> ret = new HashMap <> (); ret. put ("id", article. getId (); WebResponse <Map <String, Object> response = WebRespon Se. getSuccessResponse (ret); return response ;}@ ApiOperation (value = "DELETE an Article", notes = "DELETE an Article by ID", tags = "Article", httpMethod = "DELETE ") @ ApiImplicitParams ({@ ApiImplicitParam (name = "id", value = "Article ID", required = true, dataType = "Long ")}) @ RequestMapping (value = "/{id}", method = DELETE, produces = "application/json") public WebResponse <?> DeleteArticle (@ PathVariable Long id) {Article article = articleService. getById (id); article. setStatus (-1); articleService. saveArticle (article); return WebResponse. getSuccessResponse (new HashMap <> () ;}@ ApiOperation (value = "Get Article list", notes = "fuzzy search by title", tags = "Article ", httpMethod = "GET") @ ApiImplicitParams ({@ ApiImplicitParam (name = "title", value = "article title", required = false, dataType = "Stri Ng "), @ ApiImplicitParam (name =" pageSize ", value =" number of articles per page ", required = false, dataType =" Integer "), @ ApiImplicitParam (name = "pageNum", value = "page number", required = false, dataType = "Integer ")}) @ RequestMapping (value = "/article/list", method = GET, produces = "application/json") public WebResponse <?> ListArticles (String title, Integer pageSize, Integer pageNum) {if (pageSize = null) {pageSize = 10;} if (pageNum = null) {pageNum = 1 ;} int offset = (pageNum-1) * pageSize; List <Article> articles = articleService. getArticles (title, 1L, offset, pageSize); return WebResponse. getSuccessResponse (articles) ;}@ ApiOperation (value = "Update Article", notes = "Update Article content", tags = "Article", httpMethod = "PUT ") @ ApiImplicitParams ({@ ApiImplicitParam (name = "id", value = "Article ID", required = true, dataType = "Long"), @ ApiImplicitParam (name = "title ", value = "article title", required = false, dataType = "String"), @ ApiImplicitParam (name = "summary", value = "Article summary", required = false, dataType = "String"), @ ApiImplicitParam (name = "status", value = "Publish status", required = false, dataType = "Integer")}) @ RequestMapping (val Ue = "/article/{id}", method = PUT, produces = "application/json") public WebResponse <?> UpdateArticle (@ PathVariable Long id, @ RequestBody Article article) {article. setId (id); articleService. updateArticle (article); return WebResponse. getSuccessResponse (new HashMap <> ());}}
Let's explain the specific functions of several annotations and related attributes in the Code:
@ ApiOperation: configuration of the entire interface property:
Value: interface description, which is displayed in the interface list.
Notes: Interface Details are displayed on the Interface Details page.
HttpMethod: supported HTTP methods.
@ ApiImplicitParams, @ ApiImplicitParam container, which can contain multiple @ ApiImplicitParam annotations
@ ApiImplicitParam, request parameter attribute Configuration:
Name: Parameter name
Value: parameter description
Required: required
DataType: Data Type
@ ApiResponses, @ ApiResponse container, which can contain multiple @ ApiResponse annotations
@ ApiResponse, returned result property Configuration:
Code: the encoding of the returned result.
Message: Description of the returned results.
Response: class corresponding to the returned result.
After completing the above configuration, let's look at the page effect:
List page:
As you can see, all interfaces are now under the Article tag, and we have configured instructions later. Let's look at the details page of the "POST/rest/article" interface:
The image is too large. Only the title attribute is displayed. The other parameters are similar. We can see the descriptions of the Request Parameters on the page, but this is not our expected effect. If our parameters are just simple types, this method should be fine, but now the problem is that our request parameters are an object. How can we configure them? This involves two other annotations: @ ApiModel and @ ApiModelProperty. Let's take a look at the code and then explain it, which makes it easier to understand:
@ ApiModel (value = "article object", description = "add & Update Document Object description") public class Article {@ Id @ GeneratedValue @ ApiModelProperty (name = "id ", value = "Article ID", required = false, example = "1") private Long id; @ ApiModelProperty (name = "title", value = "article title ", required = true, example = "test article title") private String title; @ ApiModelProperty (name = "summary", value = "document summary", required = true, example = "test article summary") private String summary; @ ApiModelProperty (hidden = true) private Date createTime; @ ApiModelProperty (hidden = true) private Date publicTime; @ ApiModelProperty (hidden = true) private Date updateTime; @ ApiModelProperty (hidden = true) private Long userId; @ ApiModelProperty (name = "status", value = "article publishing status ", required = true, example = "1") private Integer status; @ ApiModelProperty (name = "type", value = "document category", required = true, example = "1 ") private Integer type ;}
@ ApiModel: Configure attributes of the entire class:
Value: Class description
Description: Detailed description
@ ApiModelProperty is the attribute configuration for each field:
Name: field name
Value: Field description
Required: required
Example: Sample Value
Hidden: whether to display
After completing the above configuration, let's look at the effect:
Now we can see that all the field descriptions have been displayed, and the Field Values in the example have changed to the values corresponding to the example attribute we configured. In this way, a complete API document is generated, and the document is closely linked with the Code, rather than the two parts separated. In addition, we can also easily perform tests through this document. We only need to click the yellow box under Example Value, and the content in it will be automatically copied to the value box corresponding to the article, then, click "Try it out" to initiate an http request.
After clicking Try it out, we can see the returned results:
The operation is still very convenient. Compared with Junit and postman, testing through Swagger is more convenient. Of course, the testing of Swagger cannot replace unit testing. However, it still plays a very important role in joint debugging.
Iv. Summary
In general, the Swagger configuration is relatively simple, and Swagger can automatically help us generate documents, which indeed saves us a lot of work and will also provide great help for subsequent maintenance. In addition, Swagger can automatically generate test data for US based on the configuration and provide the corresponding HTTP method, which is also helpful for our self-testing and joint debugging work, therefore, I recommend that you use Swagger in daily development, which can help you improve your work efficiency to a certain extent. Finally, let's leave a question for everyone to think about, that is, this document can be accessed directly through the page, so we cannot directly expose the interface to the production environment, how can we disable this function in the production process, especially for systems that provide external services? There are many ways to do this. You can try it yourself.
The above is the practice of integrating Spring Boot with Swagger2. I hope it will help you. If you have any questions, please leave a message and I will reply to you in a timely manner. Thank you very much for your support for the help House website!