精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

誰家面試往死里問 Swagger 啊?

開發 前端
盡管在面試中不會過多考察Swagger這類工具,但作為開發者,養成良好的文檔規范習慣是非常重要的,無論使用Swagger還是其他文檔工具,編寫清晰、詳盡的API文檔都應該是我們的素養之一。

前言

說個挺奇葩的事,有個老鐵給我發私信吐槽了一下它的面試經歷,他去了個國企單位面試,然后面試官跟他就Swagger的問題聊了半個多小時。額~ 面試嘛這些都不稀奇,總能遇到是千奇百怪的人,千奇百怪的問題。不過,我分析這個面試官是不太好意思直接讓他走,哈哈哈!

圖片圖片

什么是Swagger?

Swagger目前是比較主流的RESTful風格的API文檔工具,做過開發的人應該都用過它吧!

圖片圖片

它提供了一套工具和規范,讓開發人員能夠更輕松地創建和維護可讀性強、易于使用和交互的API文檔(官方口吻)。

title: Swagger
desc: Swagger 官方網站
logo: https://static1.smartbear.co/swagger/media/assets/images/swagger_logo.svg
link: https://swagger.io/

為什么用Swagger?

以往在沒有這樣的API文檔工具,開發人員需要手動編寫和維護功能API的文檔。而且,由于API變更往往難以及時更新到文檔中,這可能會給依賴文檔的開發者帶來困惑。

說幾個Swagger的特點:

  • 最重要的一點可以根據代碼注解自動生成API文檔,能生成的絕對不手寫,而且API文檔與API定義會同步更新。
  • 它提供了一個可執行的Web界面,支持API在線測試,可以直接在界面上直接設置參數測試,不用額外的測試工具或插件。
  • 支持多種編程語言,Java、PHP、Python等語言都支持,喜歡什么語言構建API都行。

總的來說,Swagger可以讓我們更多時間在專注于編寫代碼(摸魚),而不是花費額外精力來維護文檔,實踐出真知先跑個demo試試。

Swagger搭建

maven 依賴

目前使用的版本是Swagger3.0、Springboot 2.7.6,Swagger2.0與3.0依賴包名稱的變化有些大,需要特別注意一下。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.7.6</version>
</dependency>

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>

配置類

首先我們創建一個控制器TestController類,里邊只有一個最簡單的請求 /test。

@RestController
public class TestController {

    @RequestMapping("/test")
    public String test(String name) {
        return name;
    }
}

接下來創建配置類SwaggerConfig,類標注@EnableSwagger2注解是關鍵,到這最簡單的Swagger文檔環境就搭建好了。

import org.springframework.context.annotation.Configuration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

}

啟動報錯

啟動時可能會報如下的錯誤,這是由于高版本的Springboot與Swagger版本使用的路徑匹配策略沖突導致的。

Springfox使用的路徑匹配規則為AntPathMatcher 的,而SpringBoot2.7.6使用的是PathPatternMatcher,兩者沖突了。

org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
 at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.24.jar:5.3.24]
 at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.24.jar:5.3.24]
 at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[spring-context-5.3.24.jar:5.3.24]
 at java.lang.Iterable.forEach(Iterable.java:75) ~[na:1.8.0_341]
 at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) ~[spring-context-5.3.24.jar:5.3.24]
 at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) ~[spring-context-5.3.24.jar:5.3.24]
 at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935) ~[spring-context-5.3.24.jar:5.3.24]
 at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.24.jar:5.3.24]
 at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.6.jar:2.7.6]
 at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) [spring-boot-2.7.6.jar:2.7.6]
 at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) [spring-boot-2.7.6.jar:2.7.6]
 at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-2.7.6.jar:2.7.6]
 at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) [spring-boot-2.7.6.jar:2.7.6]
 at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) [spring-boot-2.7.6.jar:2.7.6]
 at com.springboot101.SwaggerApplication.main(SwaggerApplication.java:10) [classes/:na]

解決方案

這個錯誤的解決辦法比較多,我整理了四種解決此問題的方案,你看哪個更合適你。

1、降低版本

SpringBoot版本降低到2.5.X 、springfox降到3.X 以下可以解決問題,不過不推薦這么做,畢竟降配置做兼容顯得有點傻。

2、統一路徑匹配策略

將SpringMVC的匹配URL路徑的策略改為ant_path_matcher,application.yml文件增加如下的配置:

spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher

3、@EnableWebMvc注解

在配置類SwaggerConfig上標注@EnableWebMvc注解也可以解決。

Swagger框架需要通過解析和掃描帶有注解的Controller類和方法來生成API文檔。@EnableWebMvc注解會注冊一個RequestMappingHandlerMapping的Bean,并將其作為默認的請求映射處理器,以確保這些Controller類和方法能夠被正確處理,可以與Swagger的路徑配置規則相匹配,從而使得Swagger能夠成功生成API文檔。

@EnableWebMvc
@Configuration
@EnableSwagger2
public class SwaggerConfig {

}

4、注冊 BeanPostProcessor

也可以自行實現兼容邏輯來解決這個問題,我們可以在Spring容器中注冊一個BeanPostProcessor,在該處理器中對 HandlerMappings 進行定制。

通過過濾掉已存在PatternParser的映射,意味著我們可以將Swagger特定的HandlerMappings添加到HandlerMappings列表中,從而使用自定義的設置來替代原有的HandlerMappings。

這樣修復了可能導致Swagger無法正常使用的問題。

@Bean
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
    return new BeanPostProcessor() {

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
                customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
            }
            return bean;
        }

        private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
            List<T> copy = mappings.stream()
                    .filter(mapping -> mapping.getPatternParser() == null)
                    .collect(Collectors.toList());
            mappings.clear();
            mappings.addAll(copy);
        }

        @SuppressWarnings("unchecked")
        private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
            try {
                Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
                field.setAccessible(true);
                return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                log.warn("修改WebMvcRequestHandlerProvider的屬性:handlerMappings出錯,可能導致swagger不可用", e);
                throw new IllegalStateException(e);
            }
        }
    };
}

訪問 swagger-ui

到這,問題解決!我們訪問Swagger文檔路徑 http://127.0.0.1:9002/swagger-ui/index.html ,能夠看到我們寫的 API 信息以及一些Swagger 文檔的默認配置信息。

圖片圖片

注意到我們只寫了一個 /test接口,但這里確把這個方法的所有請求方式都列了出來,因為我們在 controller 方法中使用了@RequestMapping注解,并沒有具體的指定接口的請求方式,所以避免文檔冗余,盡量指定請求方式或者使用指定請求方式的 @XXXMapping 注解。

指定請求方式后:

圖片圖片

API文檔配置

上邊我們訪問的文檔中展示的數據都是默認的配置,現在咱們來定制化一下文檔。

Springfox提供了一個Docket對象,供我們靈活的配置Swagger的各項屬性。Docket對象內提供了很多的方法來配置文檔,下邊介紹下常用的配置項。

select

select()返回一個ApiSelectorBuilder對象,是使用apis()、paths()兩個方法的前提,用于指定Swagger要掃描的接口和路徑。

apis

默認情況下,Swagger會掃描整個項目中的接口,通過 apis()方法,你可以傳入一個RequestHandlerSelector對象實例來指定要包含的接口所在的包路徑。

@Bean
public Docket docket(Environment environment) {
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.springboot101.controller"))
            .build();
}

paths

僅將某些特定請求路徑的API展示在Swagger文檔中,例如路徑中包含/test。可以使用 apis() 和 paths()方法一起來過濾接口。

@Bean
public Docket docket(Environment environment) {
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .paths(PathSelectors.ant("/test/**"))
            .build();
}

groupName

為生成的Swagger文檔指定分組的名稱,用來區分不同的文檔組。

@Bean
public Docket docket(Environment environment) {
    return new Docket(DocumentationType.SWAGGER_2)
            .groupName("用戶分組")
            .build();
}

圖片圖片

實現文檔的多個分組,則需創建多個 Docket 實例,設置不同的組名,和組內過濾 API 的條件。

@Bean
public Docket docket1(Environment environment) {
    return new Docket(DocumentationType.SWAGGER_2)
            .groupName("商家分組")
            .select()
            .paths(PathSelectors.ant("/test1/**"))
            .build();
}

圖片圖片

apiInfo

設置API文檔的基本信息,例如標題、描述、版本等。你可以使用ApiInfo對象自定義信息。

@Bean
public Docket docket(Environment environment) {
    return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo()); // 文檔基礎配置
}

private ApiInfo apiInfo() {
    Contact contact = new Contact("小富", "http://fire100.top", "email@example.com");
    return new ApiInfoBuilder()
            .title("Swagger學習")
            .description("程序員小富-帶你一起學習 Swagger")
            .version("v1.0.1")
            .termsOfServiceUrl("http://fire100.top")
            .contact(contact)
            .license("許可證")
            .licenseUrl("許可鏈接")
            .extensions(Arrays.asList(
                    new StringVendorExtension("我是", "小富"),
                    new StringVendorExtension("你是", "誰")
            ))
            .build();
}

對應的Swagger文檔頁面上展示的位置

圖片圖片

enable

啟用或禁用Swagger文檔的生成,有時測試環境會開放API文檔,但在生產環境則要禁用,可以根據環境變量控制是否顯示。

@Bean
public Docket docket(Environment environment) {
    // 可顯示 swagger 文檔的環境
    Profiles of = Profiles.of("dev", "test","pre");
    boolean enable = environment.acceptsProfiles(of);

    return new Docket(DocumentationType.SWAGGER_2)
            .enable(enable)
            .apiInfo(apiInfo()); // 文檔基礎配置
}

host

API文檔顯示的主機名稱或IP地址,即在測試執行接口時使用的IP或域名。

@Bean
public Docket docket(Environment environment) {
    return new Docket(DocumentationType.SWAGGER_2)
            .host("http://test.com") // 請求地址
            .apiInfo(apiInfo()); // 文檔基礎配置
}

securitySchemes

配置API安全認證方式,比如常見的在header中設置如Bearer、Authorization、Basic等鑒權字段,ApiKey對象中字段含義分別是別名、鑒權字段key、鑒權字段添加的位置。

@Bean
public Docket docket(Environment environment) {
    return new Docket(DocumentationType.SWAGGER_2)
            .securitySchemes(
                    Arrays.asList(
                            new ApiKey("Bearer鑒權", "Bearer", "header"),
                            new ApiKey("Authorization鑒權", "Authorization", "header"),
                            new ApiKey("Basic鑒權", "Basic", "header")
                    )
            );
}

這樣配置后,Swagger文檔將會包含一個Authorize按鈕,供用戶輸入我們設定的Bearer 、Authorization、Basic進行身份驗證。

圖片圖片

securityContexts

securitySchemes方法中雖然設置了鑒權字段,但此時在測試接口的時候不會自動在 header中加上鑒權字段和值,還要配置API的安全上下文,指定哪些接口需要進行安全認證。

@Bean
public Docket docket(Environment environment) {
    return new Docket(DocumentationType.SWAGGER_2)
            .securitySchemes(
                    Arrays.asList(
                            new ApiKey("Bearer鑒權", "Bearer", "header"),
                            new ApiKey("Authorization鑒權", "Authorization", "header"),
                            new ApiKey("Basic鑒權", "Basic", "header")
                    )
            )
            .securityContexts(Collections.singletonList(securityContext()));
}

private SecurityContext securityContext() {
    return SecurityContext.builder()
            .securityReferences(
                    Arrays.asList(
                            new SecurityReference("Authorization", new AuthorizationScope[0]),
                            new SecurityReference("Bearer", new AuthorizationScope[0]),
                            new SecurityReference("Basic", new AuthorizationScope[0])))
            .build();
}

現在在測試調用API接口時,header中可以正常加上鑒權字段和值了。

圖片圖片

tags

為API文檔中的接口添加標簽,標簽可以用來對API進行分類或分組,并提供更好的組織和導航功能。

@Bean
public Docket docket(Environment environment) {
    return new Docket(DocumentationType.SWAGGER_2)
            .tags(new Tag("小富接口", "小富相關的測試接口"))
}

授權登錄

出于對系統安全性的考慮,通常我們還會為API文檔增加登錄功能。

引入maven依賴

swagger的安全登錄是基于security實現的,引入相關的maven依賴。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

登錄配置

application.yml文件中配置登錄swagger的用戶名和密碼。

spring:
  security:
    user:
      name: admin
      password: 123456

再次訪問文檔就會出現如下的登錄頁

圖片圖片

文檔注解

當我們希望在Swagger文檔中提供詳細和完整的內容時,還可以使用其他許多Swagger內置注解來進一步豐富和定制API文檔。

@ApiIgnore

上邊我們提到可以根據指定路徑或者包路徑來提供API,也可以使用粒度更細的@ApiIgnore注解,來實現某個API在文檔中忽略。

@ApiIgnore
@GetMapping("/user2/{id}")
public User test2(@PathVariable Integer id, @RequestBody User user) {
    return user;
}

@ApiModel

在我們的接口中,只要使用實體作為參數或響應體,Swagger就會自動掃描到它們,但你會發現目前這些實體缺乏詳細的描述信息。為了讓使用者通俗易懂,需要使用swagger提供的注解為這些實體添加詳細的描述。

圖片圖片

@ApiModel注解的使用在實體類上,提供對Swagger Model額外信息的描述。

圖片圖片

@ApiModelProperty

@ApiModelProperty 注解為實體類中的屬性添加描述,提供了字段名稱、是否必填、字段示例等描述信息。

@ApiModel(value = "用戶實體類", description = "用于存放用戶登錄信息")
@Data
public class User {

    @ApiModelProperty(value = "用戶名字段", required = true, example = "#公眾號:程序員小富")
    private String name;

    @ApiModelProperty(value = "年齡", required = true, example = "19")
    private Integer age;

    @ApiModelProperty(value = "郵箱", required = true, example = "#公眾號:程序員小富")
    private String email;
}

@Api

@Api 注解用于標記一個控制器(controller)類,并提供接口的詳細信息和配置項。

  • value:API 接口的描述信息,由于版本swagger版本原因,value可能會不生效可以使用description
  • hidden:該 API 是否在 Swagger 文檔中隱藏
  • tags:API 的標簽,如果此處與 new Docket().tags 中設置的標簽一致,則會將該 API 放入到這個標簽組內
  • authorizations:鑒權配置,配合 @AuthorizationScope 注解控制權限范圍或者特定密鑰才能訪問該API。
  • produces:API的響應內容類型,例如 application/json。
  • consumes:API的請求內容類型,例如 application/json。
  • protocols:API支持的通信協議。
@Api(value = "用戶管理接口描述",
        description = "用戶管理接口描述",
        hidden = false,
        produces = "application/json",
        consumes = "application/json",
        protocols = "https",
        tags = {"用戶管理"},
        authorizations = {
                @Authorization(value = "apiKey", scopes = {
                        @AuthorizationScope(scope = "read:user", description = "讀權限"),
                        @AuthorizationScope(scope = "write:user", description = "寫權限")
                }),
                @Authorization(value = "basicAuth")
        })
@RestController
public class TestController {

}

@ApiOperation

@ApiOperation該注解作用在接口方法上,用來對一個操作或HTTP方法進行描述。

  • value:對接口方法的簡單說明
  • notes:對操作的詳細說明。
  • httpMethod:請求方式
  • code:狀態碼,默認為 200。可以傳入符合標準的HTTP Status Code Definitions。
  • hidden:在文檔中隱藏該接口
  • response:返回的對象
  • tags:使用該注解后,該接口方法會單獨進行分組
  • produces:API的響應內容類型,例如 application/json。
  • consumes:API的請求內容類型,例如 application/json。
  • protocols:API支持的通信協議。
  • authorizations:鑒權配置,配合 @AuthorizationScope 注解控制權限范圍或者特定密鑰才能訪問該API。
  • responseHeaders:響應的header內容
@ApiOperation(
        value = "獲取用戶信息",
        notes = "通過用戶ID獲取用戶的詳細信息",
        hidden = false,
        response = UserDto.class,
        tags = {"用戶管理"},
        produces = "application/json",
        consumes = "application/json",
        protocols = "https",
        authorizations = {
                @Authorization(value = "apiKey", scopes = {@AuthorizationScope(scope = "read:user", description = "讀權限")}),
                @Authorization(value = "Basic")
        },
        responseHeaders = {@ResponseHeader(name = "X-Custom-Header", description = "Custom header", response = String.class)},
        code = 200,
        httpMethod = "GET"
)
@GetMapping("/user1")
public UserDto user1(@RequestBody User user) {
    return new UserDto();
}

@ApiImplicitParams

@ApiImplicitParams注解用在方法上,以數組方式存儲,配合@ApiImplicitParam 注解使用。

@ApiImplicitParam

@ApiImplicitParam注解對API方法中的單一參數進行注解。

注意這個注解@ApiImplicitParam必須被包含在注解@ApiImplicitParams之內。

  • name:參數名稱
  • value:參數的簡短描述
  • required:是否為必傳參數
  • dataType:參數類型,可以為類名,也可以為基本類型(String,int、boolean等)
  • paramType:參數的傳入(請求)類型,可選的值有 path、query、body、header、form。
@ApiImplicitParams({
        @ApiImplicitParam(name = "用戶名", value = "用戶名稱信息", required = true, dataType = "String", paramType = "query")
})
@GetMapping("/user")
public String user(String name) {
    return name;
}

@ApiParam()

@ApiParam()也是對API方法中的單一參數進行注解,其內部屬性和@ApiImplicitParam注解相似。

@GetMapping("/user4")
public String user4(@ApiParam(name = "主鍵ID", value = "@ApiParam注解測試", required = true) String id) {
    return id;
}

@ApiResponses

@ApiResponses注解可用于描述請求的狀態碼,作用在方法上,以數組方式存儲,配合 @ApiResponse注解使用。

@ApiResponse

@ApiResponse注解描述一種請求的狀態信息。

  • code:HTTP請求響應碼。
  • message:響應的文本消息
  • response:返回類型信息。
  • responseContainer:如果返回類型為容器類型,可以設置相應的值。有效值為 "List"、 "Set"、"Map"其他任何無效的值都會被忽略。
@ApiResponses(value = {
        @ApiResponse(code = 200, message = "@ApiResponse注解測試通過", response = String.class),
        @ApiResponse(code = 401, message = "可能參數填的有問題", response = String.class),
        @ApiResponse(code = 404, message = "可能請求路徑寫的有問題", response = String.class)
})
@GetMapping("/user4")
public String user4(@ApiParam(name = "主鍵ID", value = "@ApiParam注解測試", required = true) String id) {
    return id;
}

......

總結

盡管在面試中不會過多考察Swagger這類工具,但作為開發者,養成良好的文檔規范習慣是非常重要的,無論使用Swagger還是其他文檔工具,編寫清晰、詳盡的API文檔都應該是我們的素養之一。

好了,今天的內容分享就到這里了,感謝大家的收看,我們下篇見。

代碼示例

https://github.com/chengxy-nds/Springboot-Notebook/tree/master/springboot101/%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3/springboot-swagger

我是小富,下期見~

責任編輯:武曉燕 來源: 程序員小富
相關推薦

2023-09-26 08:24:34

數據一致性事務

2022-06-28 10:58:48

協議通信加密

2013-07-10 14:30:29

2023-03-28 21:33:53

面試隔離MVCC

2021-07-21 09:15:27

MySQL數據庫面試

2009-06-15 17:05:02

IP通信

2021-02-05 12:34:33

線程池系統

2012-06-11 16:20:16

蘋果面試

2022-01-05 09:55:26

asynawait前端

2022-08-17 08:17:01

SPI機制接口

2023-06-07 08:08:43

JVM內存模型

2021-12-09 12:22:28

MyBatis流程面試

2020-07-28 08:59:22

JavahreadLocal面試

2021-07-30 16:16:54

網絡面試TCP

2020-07-06 11:53:08

TCP三次握手協議

2009-02-13 10:00:41

面試軟件開發程序員

2023-02-03 07:24:49

雙親委派模型

2021-12-25 22:31:10

MarkWord面試synchronize

2018-07-10 16:50:28

數據庫MySQL面試題

2009-03-01 21:13:27

iPhone蘋果3G
點贊
收藏

51CTO技術棧公眾號

日韩国产精品久久久久久亚洲| 欧美成a人免费观看久久| 亚洲图片你懂的| 成人免费在线看片| 免费看毛片网站| 婷婷伊人综合| 亚洲激情视频网| 北条麻妃在线一区| 18加网站在线| 国产日产精品1区| 999精品视频一区二区三区| 男人午夜免费视频| 五月天激情综合网| 亚洲欧美在线看| 久久久久久久久久久影视| 希岛爱理一区二区三区av高清| 亚洲欧美一区二区三区孕妇| 欧美黑人xxxxx| 99久久精品国产色欲| 美女日韩在线中文字幕| 欧美久久精品午夜青青大伊人| 美国黄色一级毛片| 日韩一区二区三区在线看| 欧洲人成人精品| 人妻无码久久一区二区三区免费| 一区二区三区视频网站| 成人高清在线视频| 18成人在线| 亚洲图片中文字幕| 麻豆亚洲精品| 91精品国产一区| 久青草视频在线观看| 久久社区一区| 在线精品高清中文字幕| 亚洲av无码一区二区二三区| 亚洲欧洲国产精品一区| 91精品国产入口在线| 精品999在线| 免费成人直播| 色综合一个色综合亚洲| 免费看黄在线看| 成人黄色网址| 亚洲天堂免费在线观看视频| 视频一区二区综合| av黄色在线观看| 国产日韩在线不卡| 色播五月综合| 国产区在线视频| 国产视频一区二区在线观看| 欧美一区二区福利| 免费观看成年在线视频网站| av男人天堂一区| 精品欧美一区二区精品久久| 视频污在线观看| av不卡在线观看| 精品欧美一区二区在线观看视频 | 91系列在线播放| 一级视频在线播放| 久久99久久精品欧美| 国产精品狠色婷| 最近中文字幕在线免费观看| 日本不卡一区二区| 国产精品主播视频| 97成人在线观看| 狠狠v欧美v日韩v亚洲ⅴ| 成人国产精品久久久| 99久久久国产精品无码免费| 国产成人一区二区精品非洲| 国产精品久久久久久久久婷婷| 丰满少妇被猛烈进入| 99精品久久久久久| 日本一区免费在线观看| 婷婷在线视频观看| 亚洲精品福利视频网站| 国产自产在线视频| 欧美美女日韩| 欧美日韩国产精选| 无码人妻久久一区二区三区蜜桃 | 亚洲第一偷拍网| 人妻精品久久久久中文字幕| 久久视频在线| 欧美激情奇米色| 一级黄色大片视频| 久久国产剧场电影| 国产精品入口免费| 成人jjav| 亚洲午夜国产一区99re久久| 免费午夜视频在线观看| 一区二区三区日本视频| 亚洲国产福利在线| 国产精品天天干| 欧美va天堂| 国产精品对白刺激| 亚洲av无码一区二区三区性色| 99久久久国产精品| 一本色道久久综合亚洲二区三区| 午夜av在线免费观看| 欧美日韩在线另类| 性久久久久久久久久久久久久| 国产精品调教| 精品国内产的精品视频在线观看| 久久精品女人毛片国产| 人人超碰91尤物精品国产| 春色成人在线视频| 在线毛片网站| 狠狠爱在线视频一区| 国产精品探花在线播放| 在线成人动漫av| 欧美精品国产精品日韩精品| 成人黄色片在线观看| 国产91精品入口| 亚洲在线不卡| 校园春色亚洲色图| 亚洲国产成人精品久久| 希岛爱理中文字幕| 久久一区二区三区四区五区| 动漫3d精品一区二区三区| 成人免费视频| 日韩欧美aⅴ综合网站发布| 亚洲成人激情小说| 日韩欧美精品一区| 国产成人中文字幕| 亚洲av毛片成人精品| 亚洲网友自拍偷拍| 捷克做爰xxxⅹ性视频| 久久av免费看| 欧美在线视频a| 欧美一级一区二区三区| 亚洲免费观看在线视频| 污色网站在线观看| 欧美三级美国一级| 韩剧1988在线观看免费完整版| av在线免费在线观看| 中文字幕免费不卡| 久久精品免费网站| 国产欧美日韩精品一区二区免费| 午夜精品久久久久久久久久久久 | 性色av一区二区三区四区| 91色九色蝌蚪| 久久精品免费一区二区| 爱高潮www亚洲精品| 欧美乱大交xxxxx另类电影| 91尤物国产福利在线观看| 国产欧美日本一区二区三区| 蜜臀av午夜一区二区三区| 精品在线播放| 日本精品免费观看| 你懂的视频在线观看| 一本色道久久加勒比精品 | 日韩欧美国产大片| 高清欧美电影在线| 欧美 日韩 国产 精品| 偷窥少妇高潮呻吟av久久免费| 人妻换人妻a片爽麻豆| 亚洲精品极品| 青青成人在线| 成人在线视频免费看| 日韩在线欧美在线| av中文字幕免费| 亚洲一线二线三线久久久| xxxwww国产| 香蕉久久夜色精品国产| 日韩高清三级| 色综合.com| 欧美精品videofree1080p| 亚洲国产日韩在线观看| 午夜精品久久久久久不卡8050| 无码精品一区二区三区在线播放| 亚洲一卡久久| 一本色道久久综合亚洲精品婷婷| 国产 日韩 欧美| 久久琪琪电影院| 久久精品蜜桃| 欧美精品在线视频| 国产精品成人免费一区二区视频| 92国产精品观看| 天堂一区在线观看| 国产一区日韩一区| 玛丽玛丽电影原版免费观看1977 | 欧美精品乱人伦久久久久久| www日韩在线| 99久久精品一区二区| 一本久道综合色婷婷五月| 外国成人免费视频| 好看的日韩精品| 99久久伊人| 亚州国产精品久久久| 国产人成在线视频| 日韩美女主播在线视频一区二区三区| 成人免费区一区二区三区| 国产精品午夜久久| 男男一级淫片免费播放| 人人精品人人爱| 欧美啪啪免费视频| 99久久九九| 久久精品综合一区| crdy在线观看欧美| 日产精品99久久久久久| a免费在线观看| 亚洲欧美色图片| 亚洲成人77777| 欧美影院一区二区| 国产香蕉视频在线| 综合自拍亚洲综合图不卡区| 成年人网站免费看| 国产成人精品网址| 亚洲最大成人在线观看| 国产情侣一区| 妞干网在线播放| 天天做天天爱天天综合网2021| 久久久一本精品99久久精品| 日韩免费一级| 国产精品天天狠天天看| 黑人巨大精品| 欧美极品少妇全裸体| 国产原创精品视频| 亚洲午夜色婷婷在线| 天天躁日日躁狠狠躁喷水| 欧美一区二区三区四区在线观看| 999视频在线| 疯狂蹂躏欧美一区二区精品| 免费麻豆国产一区二区三区四区| 中文字幕不卡在线| 国产精品密蕾丝袜| 久久亚洲欧美国产精品乐播| 丰满岳乱妇一区二区| 国产福利一区二区三区视频在线| 伊人国产在线视频| 日韩国产欧美三级| 热久久精品国产| 午夜一级久久| 春日野结衣av| 国产日韩精品视频一区二区三区| 欧美视频在线第一页| 欧美 日韩 国产一区二区在线视频| 日韩欧美亚洲日产国| 国产探花一区| 欧美高清性xxxxhd| 久久av免费| 色狠狠久久av五月综合| 国产精选一区| 神马欧美一区二区| 欧美一二区在线观看| 日韩成人av电影在线| 成人情趣视频| 亚洲人成网站在线播放2019| 日韩在线精品| 日本久久高清视频| 欧美a级在线| 成品人视频ww入口| 国产精品日本| 日韩视频第二页| 免费在线一区观看| 亚洲综合婷婷久久| 激情综合色综合久久综合| 中文字幕色网站| 国产91精品精华液一区二区三区 | 99久久亚洲精品日本无码| 555www色欧美视频| 性欧美8khd高清极品| 精品国产伦一区二区三区免费| 亚洲美女福利视频| 亚洲国产美女精品久久久久∴| 天天干天天色天天| 亚洲深夜福利视频| 五月婷婷在线视频| 欧美国产日韩一区二区三区| 国产理论在线| 国产精品免费在线免费| 久久九九精品视频| 国精产品99永久一区一区| 伊人成综合网yiren22| 日韩欧美视频一区二区| 亚洲综合色站| 日韩日韩日韩日韩日韩| 日韩和欧美一区二区| 亚洲在线观看网站| 97久久超碰国产精品电影| 亚洲第一综合网| 亚洲精品视频在线观看网站| 日本一区二区欧美| 欧美中文字幕不卡| www.蜜臀av| 亚洲小视频在线观看| 国产成人午夜| 日韩av成人在线| 成人在线日韩| 免费影院在线观看一区| 久久久久久久久久久久久久| 99在线精品免费视频| 蜜桃视频在线一区| 影音先锋黄色资源| 综合av第一页| 国产午夜精品久久久久| 欧美一区二区福利视频| 国产二区视频在线观看| 欧美乱大交xxxxx另类电影| 国产综合av| 成人免费在线看片| 日韩在线综合| 国产午夜伦鲁鲁| 韩国女主播成人在线| 性欧美13一14内谢| 亚洲国产日韩a在线播放| 中日韩在线观看视频| 亚洲第一中文字幕| av在线网址观看| 国产精品成人aaaaa网站| 精品淫伦v久久水蜜桃| 国产对白在线播放| 日韩电影在线免费| 久久久久国产精品区片区无码| 亚洲婷婷综合久久一本伊一区| 91视频久久久| 日韩精品黄色网| 黄网av在线| 亚洲直播在线一区| 久久国产成人午夜av影院宅| 日韩激情免费视频| 成人黄色网址在线观看| 成人免费视频网站入口::| 欧美日韩在线免费视频| 欧美在线一卡| 欧美在线视频网站| 国产精品天天看天天狠| 91免费国产精品| 国产一区二区三区免费| 亚洲伦理一区二区三区| 欧美日韩一区二区三区免费看| 青青免费在线视频| 国产91成人video| 日韩理论电影中文字幕| 福利视频一区二区三区四区| 国产成人激情av| 久久精品无码人妻| 欧美不卡123| 青青在线视频| 国产精品嫩草在线观看| 欧美网站在线| 国产大学生av| 亚洲一二三四久久| 手机看片福利永久| 欧美亚洲国产另类| 免费精品国产的网站免费观看| 欧美 日韩 国产在线观看| 99久久综合色| 中文字幕在线欧美| 国产亚洲视频在线| 91精品影视| 亚洲国产日韩综合一区| 久久99精品一区二区三区| 国产又粗又长免费视频| 欧美精品乱码久久久久久按摩 | 天天综合天天综合| 日本国产一区二区三区| 国产精品一区高清| 亚洲va在线va天堂va偷拍| 亚洲免费av高清| 亚洲精华国产精华精华液网站| 久久久久国色av免费观看性色| 草草视频在线一区二区| 欧美a在线视频| 国产区在线观看成人精品 | 日韩欧美一区二区三区免费观看| 欧美一区二区三区精美影视| 日韩va亚洲va欧美va久久| 91麻豆精品久久毛片一级| 91麻豆精品91久久久久久清纯 | 国产精品吊钟奶在线| 欧美国产一级| 69亚洲乱人伦| 在线观看免费亚洲| 黄视频网站在线| 国产精品免费在线播放| 天堂成人免费av电影一区| 男人的午夜天堂| 亚洲国产精品电影| 99只有精品| 精品人妻少妇一区二区| 国产日韩欧美不卡在线| www.国产三级| 欧美一级电影久久| 婷婷丁香综合| 大又大又粗又硬又爽少妇毛片| 欧美性感一区二区三区| 大香伊人久久| 亚洲v国产v在线观看| 国产99精品国产| 日本视频www色| 欧美激情乱人伦| 欧美成人milf| 亚洲男人在线天堂| 欧美精品在欧美一区二区少妇| av影院在线免费观看| 亚洲国产精品久久久久久女王| 丁香六月久久综合狠狠色| 中文人妻熟女乱又乱精品| 欧美高清激情视频| 青青草91久久久久久久久| 成年女人免费视频|