1. 程式人生 > >使用swagger作為restful api的doc文件生成

使用swagger作為restful api的doc文件生成

初衷

記得以前寫介面,寫完後會整理一份API介面文件,而文件的格式如果沒有具體要求的話,最終展示的文件則完全決定於開發者的心情。也許多點,也許少點。甚至,介面總是需要適應新需求的,修改了,增加了,這份文件維護起來就很困難了。於是發現了swagger,自動生成文件的工具。

swagger介紹

首先,官網這樣寫的:

Swagger – The World's Most Popular Framework for APIs.

因為自強所以自信。swagger官方更新很給力,各種版本的更新都有。swagger會掃描配置的API文件格式自動生成一份json資料,而swagger官方也提供了ui來做通常的展示,當然也支援自定義ui的。不過對後端開發者來說,能用就可以了,官方就可以了。

最強的是,不僅展示API,而且可以呼叫訪問,只要輸入引數既可以try it out.

效果為先,最終展示doc介面,也可以設定為中文:
686418-20160914235935805-777612461.png

在spring-boot中使用

以前總是看各種部落格來配置,這次也不例外。百度了千篇一律卻又各有細微的差別,甚至時間上、版本上各有不同。最終還是去看官方文件,終於發現了官方的sample。針對於各種option的操作完全在demo中了,所以clone照抄就可以用了。

配置

1.需要依賴兩個包:


        <dependency>
            <groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId> <version>${springfox-version}</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId>
<version>${springfox-version}</version> </dependency>

第一個是API獲取的包,第二是官方給出的一個ui介面。這個介面可以自定義,預設是官方的,對於安全問題,以及ui路由設定需要著重思考。

2.swagger的configuration

需要特別注意的是swagger scan base package,這是掃描註解的配置,即你的API介面位置。


@Configuration
@EnableSwagger2
public class SwaggerConfig {

    public static final String SWAGGER_SCAN_BASE_PACKAGE = "com.test.web.controllers";
    public static final String VERSION = "1.0.0";

    ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Swagger API")
                .description("This is to show api description")
                .license("Apache 2.0")
                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
                .termsOfServiceUrl("")
                .version(VERSION)
                .contact(new Contact("","", "[email protected]"))
                .build();
    }

    @Bean
    public Docket customImplementation(){
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage(SWAGGER_SCAN_BASE_PACKAGE))
                .build()
                .directModelSubstitute(org.joda.time.LocalDate.class, java.sql.Date.class)
                .directModelSubstitute(org.joda.time.DateTime.class, java.util.Date.class)
                .apiInfo(apiInfo());
    }
}

當然,scan package 也可以換成別的條件,比如:

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .build();
    }

3.在API上做一些宣告

//本controller的功能描述
@Api(value = "pet", description = "the pet API")
public interface PetApi {

    //option的value的內容是這個method的描述,notes是詳細描述,response是最終返回的json model。其他可以忽略
    @ApiOperation(value = "Add a new pet to the store", notes = "", response = Void.class, authorizations = {
        @Authorization(value = "petstore_auth", scopes = {
            @AuthorizationScope(scope = "write:pets", description = "modify pets in your account"),
            @AuthorizationScope(scope = "read:pets", description = "read your pets")
            })
    }, tags={ "pet", })

    //這裡是顯示你可能返回的http狀態,以及原因。比如404 not found, 303 see other
    @ApiResponses(value = { 
        @ApiResponse(code = 405, message = "Invalid input", response = Void.class) })
    @RequestMapping(value = "/pet",
        produces = { "application/xml", "application/json" }, 
        consumes = { "application/json", "application/xml" },
        method = RequestMethod.POST)
    ResponseEntity<Void> addPet(
    //這裡是針對每個引數的描述
    @ApiParam(value = "Pet object that needs to be added to the store" ,required=true ) @RequestBody Pet body);

案例:

package com.test.mybatis.web.controllers;

import com.test.mybatis.domain.entity.City;
import com.test.mybatis.domain.entity.Hotel;
import com.test.mybatis.domain.mapper.CityMapper;
import com.test.mybatis.domain.mapper.HotelMapper;
import com.test.mybatis.domain.model.common.BaseResponse;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * Created by miaorf on 2016/9/10.
 */

@Api(value = "Test", description = "test the swagger API")
@RestController
public class TestController {

    @Autowired
    private CityMapper cityMapper;
    @Autowired
    private HotelMapper hotelMapper;

    @ApiOperation(value = "get city by state", notes = "Get city by state", response = City.class)
    @ApiResponses(value = {@ApiResponse(code = 405, message = "Invalid input", response = City.class) })
    @RequestMapping(value = "/city", method = RequestMethod.GET)
    public ResponseEntity<BaseResponse<City>>  getCityByState(
            @ApiParam(value = "The id of the city" ,required=true ) @RequestParam String state){

        City city = cityMapper.findByState(state);
        if (city!=null){
            BaseResponse response = new BaseResponse(city,true,null);
            return new ResponseEntity<>(response, HttpStatus.OK);
        }
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ApiOperation(value = "save city", notes = "", response = City.class)
    @RequestMapping(value = "/city", method = RequestMethod.POST)
    public ResponseEntity<BaseResponse<City>> saveCity(
            @ApiParam(value = "The id of the city" ,required=true ) @RequestBody City city){

        int save = cityMapper.save(city);
        if (save>0){
            BaseResponse response = new BaseResponse(city,true,null);
            return new ResponseEntity<>(response, HttpStatus.OK);
        }
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ApiOperation(value = "save hotel", notes = "", response = Hotel.class)
    @RequestMapping(value = "/hotel", method = RequestMethod.POST)
    public ResponseEntity<BaseResponse<Hotel>> saveHotel(
            @ApiParam(value = "hotel" ,required=true ) @RequestBody Hotel hotel){

        int save = hotelMapper.save(hotel);
        if (save>0){
            BaseResponse response = new BaseResponse(hotel,true,null);
            return new ResponseEntity<>(response, HttpStatus.OK);
        }
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ApiOperation(value = "get the hotel", notes = "get the hotel by the city id", response = Hotel.class)
    @RequestMapping(value = "/hotel", method = RequestMethod.GET)
    public ResponseEntity<BaseResponse<Hotel>> getHotel(
            @ApiParam(value = "the hotel id" ,required=true ) @RequestParam Long cid){

        List<Hotel> hotels = hotelMapper.selectByCityId(cid);
        return new ResponseEntity<>(new BaseResponse(hotels,true,null), HttpStatus.OK);
    }

    @ApiOperation(value = "update the hotel", notes = "update the hotel", response = Hotel.class)
    @RequestMapping(value = "/hotel", method = RequestMethod.PUT)
    public ResponseEntity<BaseResponse<Hotel>> updateHotel(
            @ApiParam(value = "the hotel" ,required=true ) @RequestBody Hotel hotel){

        int result = hotelMapper.update(hotel);
        return new ResponseEntity<>(new BaseResponse(result,true,null), HttpStatus.OK);
    }


    @ApiOperation(value = "delete the  hotel", notes = "delete the hotel by the hotel id", response = City.class)
    @RequestMapping(value = "/hotel", method = RequestMethod.DELETE)
    public ResponseEntity<BaseResponse<Hotel>> deleteHotel(
            @ApiParam(value = "the hotel id" ,required=true ) @RequestParam Long htid){

        int result = hotelMapper.delete(htid);
        return new ResponseEntity<>(new BaseResponse(result,true,null), HttpStatus.OK);
    }


}

4.設定訪問API doc的路由

在配置檔案中,application.yml中宣告:

springfox.documentation.swagger.v2.path: /api-docs

這個path就是json的訪問request mapping.可以自定義,防止與自身程式碼衝突。

API doc的顯示路由是:http://localhost:8080/swagger-ui.html

如果專案是一個webservice,通常設定home / 指向這裡:

@Controller
public class HomeController {

    @RequestMapping(value = "/swagger")
    public String index() {
        System.out.println("swagger-ui.html");
        return "redirect:swagger-ui.html";
    }
}

5.訪問

就是上面的了。但是,注意到安全問題就會感覺困擾。首先,該介面請求有幾個:

http://localhost:8080/swagger-resources/configuration/ui
http://localhost:8080/swagger-resources
http://localhost:8080/api-docs
http://localhost:8080/swagger-resources/configuration/security

除卻自定義的url,還有2個ui顯示的API和一個安全問題的API。關於安全問題的配置還沒去研究,但目前發現一個問題是在我的一個專案中,所有的url必須帶有query htid=xxx,這是為了sso portal驗證的時候需要。這樣這個幾個路由就不符合要求了。

如果不想去研究安全問題怎麼解決,那麼可以自定ui。只需要將ui下面的檔案拷貝出來,然後修改請求資料方式即可。

參考: