天天看點

Spring Boot 內建Swagger

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/catoop/article/details/50668896

Swagger 是一個規範和完整的架構,用于生成、描述、調用和可視化 RESTful 風格的 Web 服務。總體目标是使用戶端和檔案系統作為伺服器以同樣的速度來更新。檔案的方法,參數和模型緊密內建到伺服器端的代碼,允許API來始終保持同步。Swagger 讓部署管理和使用功能強大的API從未如此簡單。

更多關于Swagger的作用,相信大家百度一下能了解的更全面,本文以SpringBoot中內建Swagger為例做介紹說明。

一、修改pom.xml,添加maven依賴

<!-- Swagger -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.6.1</version>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.6.1</version>
        </dependency>           

二、添加Swagger配置類

package com.example.swaggerdemo;

import static com.google.common.base.Predicates.or;
import static springfox.documentation.builders.PathSelectors.regex;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.async.DeferredResult;

import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * SwaggerConfig
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig {

    /**
     * SpringBoot預設已經将classpath:/META-INF/resources/和classpath:/META-INF/resources/webjars/映射
     * 是以該方法不需要重寫,如果在SpringMVC中,可能需要重寫定義(我沒有嘗試)
     * 重寫該方法需要 extends WebMvcConfigurerAdapter
     * 
     */
//    @Override
//    public void addResourceHandlers(ResourceHandlerRegistry registry) {
//        registry.addResourceHandler("swagger-ui.html")
//                .addResourceLocations("classpath:/META-INF/resources/");
//
//        registry.addResourceHandler("/webjars/**")
//                .addResourceLocations("classpath:/META-INF/resources/webjars/");
//    }

    /**
     * 可以定義多個組,比如本類中定義把test和demo區分開了
     * (通路頁面就可以看到效果了) 
     *
     */
    @Bean
    public Docket testApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("test")
                .genericModelSubstitutes(DeferredResult.class)
//                .genericModelSubstitutes(ResponseEntity.class)
                .useDefaultResponseMessages(false)
                .forCodeGeneration(true)
                .pathMapping("/")// base,最終調用接口後會和paths拼接在一起
                .select()
                .paths(or(regex("/api/.*")))//過濾的接口
                .build()
                .apiInfo(testApiInfo());
    }

    @Bean
    public Docket demoApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("demo")
                .genericModelSubstitutes(DeferredResult.class)
//              .genericModelSubstitutes(ResponseEntity.class)
                .useDefaultResponseMessages(false)
                .forCodeGeneration(false)
                .pathMapping("/")
                .select()
                .paths(or(regex("/demo/.*")))//過濾的接口
                .build()
                .apiInfo(demoApiInfo());
    }

    private ApiInfo testApiInfo() {
        return new ApiInfoBuilder()
            .title("Electronic Health Record(EHR) Platform API")//大标題
            .description("EHR Platform's REST API, all the applications could access the Object model data via JSON.")//較長的描述
            .version("1.0")//版本
            .termsOfServiceUrl("NO terms of service")
            .contact(new Contact("小單", "http://blog.csdn.net/catoop", "[email protected]"))//作者
            .license("The Apache License, Version 2.0")
            .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
            .build();
    }

    private ApiInfo demoApiInfo() {
        return new ApiInfoBuilder()
            .title("Electronic Health Record(EHR) Platform API")//大标題
            .description("EHR Platform's REST API, all the applications could access the Object model data via JSON.")//較長的描述
            .version("1.0")//版本
            .termsOfServiceUrl("NO terms of service")
            .contact(new Contact("小單", "http://blog.csdn.net/catoop", "[email protected]"))//作者
            .license("The Apache License, Version 2.0")
            .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
            .build();

        return apiInfo;
    }
}
           

經過這2步配置後,我們啟動服務後,通路:

http://localhost:8080/swagger-ui.html

就完成了內建。

Swagger會預設把所有Controller中的RequestMapping方法都生成API出來,實際上我們一般隻需要标準接口的(像傳回頁面的那種Controller方法我們并不需要),所有你可以按下面的方法來設定要生成API的方法的要求。

如下我針對RestController注解的類和ResponseBody注解的方法才生成Swaager的API,并且排除了特定的類,代碼如下:

@Configuration
@EnableSwagger2 // 啟用 Swagger
public class SwaggerConfig {

    @Bean
    public Docket createRestApi() {
        Predicate<RequestHandler> predicate = new Predicate<RequestHandler>() {
            @Override
            public boolean apply(RequestHandler input) {
                Class<?> declaringClass = input.declaringClass();
                if (declaringClass == BasicErrorController.class)// 排除
                    return false;
                if(declaringClass.isAnnotationPresent(RestController.class)) // 被注解的類
                    return true;
                if(input.isAnnotatedWith(ResponseBody.class)) // 被注解的方法
                    return true;
                return false;
            }
        };
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .useDefaultResponseMessages(false)
                .select()
                .apis(predicate)
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
            .title("包含媒體、咨詢、搜尋引擎關鍵字、廣告等類型接口的服務")//大标題
            .version("1.0")//版本
            .build();
    }
}           

三、常見swagger注解一覽與使用

最常用的5個注解

@Api:修飾整個類,描述Controller的作用
@ApiOperation:描述一個類的一個方法,或者說一個接口
@ApiParam:單個參數描述
@ApiModel:用對象來接收參數
@ApiProperty:用對象接收參數時,描述對象的一個字段           

其它若幹

@ApiResponse:HTTP響應其中1個描述
@ApiResponses:HTTP響應整體描述
@ApiIgnore:使用該注解忽略這個API 

@ApiClass
@ApiError
@ApiErrors

@ApiParamImplicit
@ApiParamsImplicit           

下面建立2個Controller來測試:

1、TestController.java

@Controller
@RequestMapping("/api/test")
public class TestController {

    @ResponseBody
    @RequestMapping(value = "/show", method=RequestMethod.POST, produces=MediaType.APPLICATION_JSON_VALUE)// 這裡指定RequestMethod,如果不指定Swagger會把所有RequestMethod都輸出,在實際應用中,具體指定請求類型也使接口更為嚴謹。
    @ApiOperation(value="測試接口", notes="測試接口較長的描述")
    public String show(
            @ApiParam(required=true, name="name", value="姓名")
            @RequestParam(name = "name", required=true) String stuName){
        return "success";
    }
}           

2、DemoController.java

/**
 * DemoController
 * 
 */
@Controller
@RequestMapping(value = "/demo")
public class DemoController {

    private Logger logger = LoggerFactory.getLogger(DemoController.class);

    /**
     * 可以直接使用@ResponseBody響應JSON
     * 
     * @param request
     * @param response
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/getcount", method = RequestMethod.POST)
    @ApiOperation(value="測試-getCount", notes="getCount更多說明")
    public ModelMap getCount(HttpServletRequest request,
            HttpServletResponse response) {
        logger.info(">>>>>>>> begin getCount >>>>>>>>");
        ModelMap map = new ModelMap();
        map.addAttribute("count", 158);

        // 背景擷取的國際化資訊
        map.addAttribute("xstest", "測試");
        return map;
    }

    /**
     * 可以直接使用@ResponseBody響應JSON
     * 
     * @param request
     * @param response
     * @return
     */
    @ApiIgnore//使用該注解忽略這個API
    @ResponseBody
    @RequestMapping(value = "/jsonTest1", method = RequestMethod.POST)
    public ModelMap jsonTest(HttpServletRequest request,
            HttpServletResponse response) {
        ModelMap map = new ModelMap();
        map.addAttribute("hello", "你好");
        map.addAttribute("veryGood", "很好");

        return map;
    }

    /**
     * 可以直接使用@ResponseBody響應JSON
     * 
     * @param request
     * @param response
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/jsonTest3", method = RequestMethod.POST)
    public List<String> jsonTest3(HttpServletRequest request,
            HttpServletResponse response) {
        List<String> list = new ArrayList<String>();
        list.add("hello");
        list.add("你好");
        return list;
    }

    /**
     * JSON請求一個對象<br/>
     * (Ajax Post Data:{"name":"名稱","content":"内容"})
     * 
     * @param version
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/jsonTest2", method = RequestMethod.POST)
    public ModelMap jsonTest2(@RequestBody Demo demo) {
        logger.info("demoName:" + demo.getName());
        logger.info("demoContent:" + demo.getContent());
        ModelMap map = new ModelMap();
        map.addAttribute("result", "ok");
        return map;
    }

    /**
     * 直接讀取URL參數值<br/>
     * /demo/jsonTest6.do?name=Hello&content=World
     * 
     * @param demoName
     * @param content
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/jsonTest6", method = RequestMethod.POST)
    public ModelMap jsonTest6(@RequestParam("name") String demoName, @RequestParam String content) {
        logger.info("demoName:" + demoName);
        ModelMap map = new ModelMap();
        map.addAttribute("name",demoName + "AAA");
        map.addAttribute("content",content + "BBB");
        map.addAttribute("date",new java.util.Date());
        return map;
    }

    /**
     * JSON請求一個對象,将RequestBody自動轉換為JSONObject對象<br/>
     * (Ajax Post Data:{"name":"名稱","content":"内容"})
     * 
     * 使用JSONObject請添加依賴
     *  <dependency>
     *      <groupId>net.sf.json-lib</groupId>
     *      <artifactId>json-lib</artifactId>
     *      <version>2.4</version>
     *      <!--指定jdk版本 -->
     *      <classifier>jdk15</classifier>
     *  </dependency>
     * 
     * @param version
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/jsonTest5", method = RequestMethod.POST)
    public ModelMap jsonTest5(@RequestBody JSONObject jsonObject) {
        String name = jsonObject.getString("name");
        logger.info("demoName:" + name);
        ModelMap map = new ModelMap();
        map.addAttribute("demoName",name);
        return map;
    }

    /**
     * 輸入 和輸出為JSON格式的資料的方式 HttpEntity<?> ResponseEntity<?>
     * 
     * @param u
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/jsonTest4", method = RequestMethod.POST)
    public ResponseEntity<String> jsonTest4(HttpEntity<Demo> demo,
            HttpServletRequest request, HttpSession session) {
        //擷取Headers方法
        HttpHeaders headers = demo.getHeaders();

        // 擷取内容
        String demoContent = demo.getBody().getContent();

        // 這裡直接new一個對象(HttpHeaders headers = new HttpHeaders();)
        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.add("MyHeaderName", "SHANHY");

        ResponseEntity<String> responseResult = new ResponseEntity<String>(
                demoContent, responseHeaders, HttpStatus.OK);
        return responseResult;
    }

}           

Swagger2預設将所有的Controller中的RequestMapping方法都會暴露,然而在實際開發中,我們并不一定需要把所有API都提現在文檔中檢視,這種情況下,使用注解@ApiIgnore來解決,如果應用在Controller範圍上,則目前Controller中的所有方法都會被忽略,如果應用在方法上,則對應用的方法忽略暴露API。

注解@ApiOperation和@ApiParam可以了解為API說明,多動手嘗試就很容易了解了。

如果我們不使用這樣注解進行說明,Swagger2也是有預設值的,沒什麼可說的試試就知道了。

顯示頁面的右上角有api_key ,springfox-swagger 2.2.2 版本并沒有進行處理,我們可以自己添加攔截器攔截 /v2/api-docs 來處理我們API文檔的通路權限,如果要更嚴格更靈活的控制,可能需要修改源碼來實作了。相信 springfox-swagger 的後期版本應該會支援更全面的應用需求的。