一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - 詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

2021-12-20 13:59程序員欣宸 Java教程

這篇文章主要介紹了Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容的相關(guān)資料,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

歡迎訪問我的GitHub

這里分類和匯總了欣宸的全部原創(chuàng)(含配套源碼):https://github.com/zq2599/blog_demos

本篇概覽

  • 作為《Spring Cloud Gateway實(shí)戰(zhàn)》系列的第九篇,咱們聊聊如何用Spring Cloud Gateway修改原始請求和響應(yīng)內(nèi)容,以及修改過程中遇到的問題
  • 首先是修改請求body,如下圖,瀏覽器是請求發(fā)起方,真實(shí)參數(shù)只有user-id,經(jīng)過網(wǎng)關(guān)時(shí)被塞入字段user-name,于是,后臺(tái)服務(wù)收到的請求就帶有user-name字段了

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

其次是修改響應(yīng),如下圖,服務(wù)提供方provider-hello的原始響應(yīng)只有response-tag字段,經(jīng)過網(wǎng)關(guān)時(shí)被塞入了gateway-response-tag字段,最終瀏覽器收到的響應(yīng)就是response-tag和gateway-response-tag兩個(gè)字段:

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

總的來說,今天要做具體事情如下:

  1. 準(zhǔn)備工作:在服務(wù)提供者的代碼中新增一個(gè)web接口,用于驗(yàn)證Gateway的操作是否有效
  2. 介紹修改請求body和響應(yīng)body的套路
  3. 按套路開發(fā)一個(gè)過濾器(filter),用于修改請求的body
  4. 按套路開發(fā)一個(gè)過濾器(filter),用于修改響應(yīng)的body
  5. 思考和嘗試:如何從Gateway返回錯(cuò)誤?

在實(shí)戰(zhàn)過程中,咱們順便搞清楚兩個(gè)問題:

  1. 代碼配置路由時(shí),如何給一個(gè)路由添加多個(gè)filter?
  2. 代碼配置路由和yml配置是否可以混搭,兩者有沖突嗎?

源碼下載

  • 本篇實(shí)戰(zhàn)中的完整源碼可在GitHub下載到,地址和鏈接信息如下表所示(https://github.com/zq2599/blog_demos):
名稱 鏈接 備注
項(xiàng)目主頁 https://github.com/zq2599/blog_demos 該項(xiàng)目在GitHub上的主頁
git倉庫地址(https) https://github.com/zq2599/blog_demos.git 該項(xiàng)目源碼的倉庫地址,https協(xié)議
git倉庫地址(ssh) [email protected]:zq2599/blog_demos.git 該項(xiàng)目源碼的倉庫地址,ssh協(xié)議

這個(gè)git項(xiàng)目中有多個(gè)文件夾,本篇的源碼在spring-cloud-tutorials文件夾下,如下圖紅框所示:

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

spring-cloud-tutorials文件夾下有多個(gè)子工程,本篇的代碼是gateway-change-body,如下圖紅框所示:

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

 

準(zhǔn)備工作

為了觀察Gateway能否按預(yù)期去修改請求和響應(yīng)的body,咱們給服務(wù)提供者provider-hello增加一個(gè)接口,代碼在Hello.java中,如下:

@PostMapping("/change")
  public Map<String, Object> change(@RequestBody Map<String, Object> map) {
      map.put("response-tag", dateStr());
      return map;
  }
  • 可見新增的web接口很簡單:將收到的請求數(shù)據(jù)作為返回值,在里面添加了一個(gè)鍵值對,然后返回給請求方,有了這個(gè)接口,咱們就能通過觀察返回值來判斷Gateway對請求和響應(yīng)的操作是否生效
  • 來試一下,先啟動(dòng)nacos(provider-hello需要的)
  • 再運(yùn)行provider-hello應(yīng)用,用Postman向其發(fā)請求試試,如下圖,符合預(yù)期:

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

  • 準(zhǔn)備工作已完成,開始開發(fā)吧

 

修改請求body的套路

  1. 修改請求body是通過自定義filter實(shí)現(xiàn)的
  2. 配置路由及其filter的時(shí)候,有yml配置文件和代碼配置兩種方式可以配置路由,官方文檔給出的demo是代碼配置的,因此今天咱們也參考官方做法,通過代碼來配置路由和過濾器
  3. 在代碼配置路由的時(shí)候,調(diào)用filters方法,該方法的入?yún)⑹莻€(gè)lambda表達(dá)式
  4. 此lambda表達(dá)式固定調(diào)用modifyRequestBody方法,咱們只要定義好modifyRequestBody方法的三個(gè)入?yún)⒓纯?/li>
  5. modifyRequestBody方法的第一個(gè)入?yún)⑹禽斎腩愋?/li>
  6. 第二個(gè)入?yún)⑹欠祷仡愋?/li>
  7. 第三個(gè)是RewriteFunction接口的實(shí)現(xiàn),這個(gè)代碼需要您自己寫,內(nèi)容是將輸入數(shù)據(jù)轉(zhuǎn)換為返回類型數(shù)據(jù)具體邏輯,咱們來看官方Demo,也就是上述套路了:
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
  return builder.routes()
      .route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")
          .filters(f -> f.prefixPath("/httpbin")
              .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
                  (exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))
      .build();
}

 

修改響應(yīng)body的套路

  • 用Spring Cloud Gateway修改響應(yīng)body的套路和前面的請求body如出一轍
  • 通過代碼來配置路由和過濾器
  • 在代碼配置路由的時(shí)候,調(diào)用filters方法,該方法的入?yún)⑹莻€(gè)lambda表達(dá)式
  • 此lambda表達(dá)式固定調(diào)用modifyResponseBody方法,咱們只要定義好modifyResponseBody方法的三個(gè)入?yún)⒓纯?/li>
  • modifyRequestBody方法的第一個(gè)入?yún)⑹禽斎腩愋?/li>
  • 第二個(gè)入?yún)⑹欠祷仡愋?/li>
  • 第三個(gè)是RewriteFunction接口的實(shí)現(xiàn),這個(gè)代碼要您自己寫,內(nèi)容是將輸入數(shù)據(jù)轉(zhuǎn)換為返回類型數(shù)據(jù)具體邏輯,咱們來看官方Demo,其實(shí)就是上述套路:
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
  return builder.routes()
      .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
          .filters(f -> f.prefixPath("/httpbin")
              .modifyResponseBody(String.class, String.class,
                  (exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri))
      .build();
}

套路總結(jié)出來了,接下來,咱們一起擼代碼?

 

按套路開發(fā)一個(gè)修改請求body的過濾器(filter)

  • 廢話不說,在父工程spring-cloud-tutorials下新建子工程gateway-change-body,pom.xml無任何特殊之處,注意依賴spring-cloud-starter-gateway即可
  • 啟動(dòng)類毫無新意:
package com.bolingcavalry.changebody;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ChangeBodyApplication {
  public static void main(String[] args) {
      SpringApplication.run(ChangeBodyApplication.class,args);
  }
}

配置文件千篇一律:

server:
#服務(wù)端口
port: 8081
spring:
application:
  name: gateway-change-body

然后是核心邏輯:修改請求body的代碼,既RewriteFunction的實(shí)現(xiàn)類,代碼很簡單,將原始的請求body解析成Map對象,取出user-id字段,生成user-name字段放回map,apply方法返回的是個(gè)Mono:

package com.bolingcavalry.changebody.function;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Map;


@Slf4j
public class RequestBodyRewrite implements RewriteFunction<String, String> {

  private ObjectMapper objectMapper;

  public RequestBodyRewrite(ObjectMapper objectMapper) {
      this.objectMapper = objectMapper;
  }

  /**
   * 根據(jù)用戶ID獲取用戶名稱的方法,可以按實(shí)際情況來內(nèi)部實(shí)現(xiàn),例如查庫或緩存,或者遠(yuǎn)程調(diào)用
   * @param userId
   * @return
   */
  private  String mockUserName(int userId) {
      return "user-" + userId;
  }

  @Override
  public Publisher<String> apply(ServerWebExchange exchange, String body) {
      try {
          Map<String, Object> map = objectMapper.readValue(body, Map.class);

          // 取得id
          int userId = (Integer)map.get("user-id");

          // 得到nanme后寫入map
          map.put("user-name", mockUserName(userId));

          // 添加一個(gè)key/value
          map.put("gateway-request-tag", userId + "-" + System.currentTimeMillis());

          return Mono.just(objectMapper.writeValueAsString(map));
      } catch (Exception ex) {
          log.error("1. json process fail", ex);
          // json操作出現(xiàn)異常時(shí)的處理
          return Mono.error(new Exception("1. json process fail", ex));
      }
  }
}

然后是按部就班的基于代碼實(shí)現(xiàn)路由配置,重點(diǎn)是lambda表達(dá)式執(zhí)行modifyRequestBody方法,并且將RequestBodyRewrite作為參數(shù)傳入:

package com.bolingcavalry.changebody.config;

import com.bolingcavalry.changebody.function.RequestBodyRewrite;
import com.bolingcavalry.changebody.function.ResponseBodyRewrite;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import reactor.core.publisher.Mono;

@Configuration
public class FilterConfig {
  @Bean
  public RouteLocator routes(RouteLocatorBuilder builder, ObjectMapper objectMapper) {
      return builder
              .routes()
              .route("path_route_change",
                      r -> r.path("/hello/change")
                              .filters(f -> f
                                      .modifyRequestBody(String.class,String.class,new RequestBodyRewrite(objectMapper))
                                      )
                      .uri("http://127.0.0.1:8082"))
              .build();
  }
}

代碼寫完了,運(yùn)行工程gateway-change-body,在postman發(fā)起請求,得到響應(yīng)如下圖,紅框中可見Gateway添加的內(nèi)容已成功:

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

現(xiàn)在修改請求body已經(jīng)成功,接下來再來修改服務(wù)提供者響應(yīng)的body

 

修改響應(yīng)body

  • 接下來開發(fā)修改響應(yīng)body的代碼
  • 新增RewriteFunction接口的實(shí)現(xiàn)類ResponseBodyRewrite.java
package com.bolingcavalry.changebody.function;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Map;

@Slf4j
public class ResponseBodyRewrite implements RewriteFunction<String, String> {

  private ObjectMapper objectMapper;

  public ResponseBodyRewrite(ObjectMapper objectMapper) {
      this.objectMapper = objectMapper;
  }

  @Override
  public Publisher<String> apply(ServerWebExchange exchange, String body) {
      try {
          Map<String, Object> map = objectMapper.readValue(body, Map.class);

          // 取得id
          int userId = (Integer)map.get("user-id");

          // 添加一個(gè)key/value
          map.put("gateway-response-tag", userId + "-" + System.currentTimeMillis());

          return Mono.just(objectMapper.writeValueAsString(map));
      } catch (Exception ex) {
          log.error("2. json process fail", ex);
          return Mono.error(new Exception("2. json process fail", ex));
      }
  }
}

路由配置代碼中,lambda表達(dá)式里面,filters方法內(nèi)部調(diào)用modifyResponseBody,第三個(gè)入?yún)⑹荝esponseBodyRewrite:

package com.bolingcavalry.changebody.config;

import com.bolingcavalry.changebody.function.RequestBodyRewrite;
import com.bolingcavalry.changebody.function.ResponseBodyRewrite;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import reactor.core.publisher.Mono;

@Configuration
public class FilterConfig {

  @Bean
  public RouteLocator routes(RouteLocatorBuilder builder, ObjectMapper objectMapper) {
      return builder
              .routes()
              .route("path_route_change",
                      r -> r.path("/hello/change")
                              .filters(f -> f
                                      .modifyRequestBody(String.class,String.class,new RequestBodyRewrite(objectMapper))
                                      .modifyResponseBody(String.class, String.class, new ResponseBodyRewrite(objectMapper))
                                      )
                      .uri("http://127.0.0.1:8082"))
              .build();
  }
}

還記得咱們的第一個(gè)問題嗎?通過上面的代碼,您應(yīng)該已經(jīng)看到了答案:用代碼配置路由時(shí),多個(gè)過濾器的配置方法就是在filters方法中反復(fù)調(diào)用內(nèi)置的過濾器相關(guān)API,下圖紅框中的都可以:

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

運(yùn)行服務(wù),用Postman驗(yàn)證效果,如下圖紅框,Gateway在響應(yīng)body中成功添加了一個(gè)key&value:

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

 

代碼配置路由和yml配置是否可以混搭?

前面有兩個(gè)問題,接下來回答第二個(gè),咱們在application.yml中增加一個(gè)路由配置:

server:
#服務(wù)端口
port: 8081
spring:
application:
  name: gateway-change-body
cloud:
  gateway:
    routes:
      - id: path_route_str
        uri: http://127.0.0.1:8082
        predicates:
          - Path=/hello/str

把gateway-change-body服務(wù)啟動(dòng)起來,此時(shí)已經(jīng)有了兩個(gè)路由配置,一個(gè)在代碼中,一個(gè)在yml中,先試試yml中的這個(gè),如下圖沒問題:

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

再試試代碼配置的路由,如下圖,結(jié)論是代碼配置路由和yml配置可以混搭

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

 

如何處理異常

  • 還有個(gè)問題必須要面對:修改請求或者響應(yīng)body的過程中,如果發(fā)現(xiàn)問題需要提前返回錯(cuò)誤(例如必要的字段不存在),代碼該怎么寫?
  • 咱們修改請求body的代碼集中在RequestBodyRewrite.java,增加下圖紅框內(nèi)容:

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

  • 再來試試,這次請求參數(shù)中不包含user-id,收到Gateway返回的錯(cuò)誤信息如下圖:

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

看看控制臺(tái),能看到代碼中拋出的異常信息:

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

  • 此時(shí),聰明的您應(yīng)該發(fā)現(xiàn)問題所在了:咱們想告訴客戶端具體的錯(cuò)誤,但實(shí)際上客戶端收到的是被Gateway框架處理后的內(nèi)容
  • 篇幅所限,上述問題從分析到解決的過程,就留給下一篇文章吧
  • 本篇的最后,請容許欣宸嘮叨兩句,聊聊為何要網(wǎng)關(guān)來修改請求和響應(yīng)body的內(nèi)容,如果您沒興趣還請忽略

 

網(wǎng)關(guān)(Gateway)為什么要做這些?

看過開篇的兩個(gè)圖,聰明的您一定發(fā)現(xiàn)了問題:為什么要破壞原始數(shù)據(jù),一旦系統(tǒng)出了問題如何定位是服務(wù)提供方還是網(wǎng)關(guān)?

  • 按照欣宸之前的經(jīng)驗(yàn),盡管網(wǎng)關(guān)會(huì)破壞原始數(shù)據(jù),但只做一些簡單固定的處理,一般以添加數(shù)據(jù)為主,網(wǎng)關(guān)不了解業(yè)務(wù),最常見的就是鑒權(quán)、添加身份或標(biāo)簽等操作
  • 前面的圖中確實(shí)感受不到網(wǎng)關(guān)的作用,但如果網(wǎng)關(guān)后面有多個(gè)服務(wù)提供者,如下圖,這時(shí)候諸如鑒權(quán)、獲取賬號信息等操作由網(wǎng)關(guān)統(tǒng)一完成,比每個(gè)后臺(tái)分別實(shí)現(xiàn)一次更有效率,后臺(tái)可以更加專注于自身業(yè)務(wù):

詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

  • 經(jīng)驗(yàn)豐富的您可能會(huì)對我的狡辯不屑一顧:網(wǎng)關(guān)統(tǒng)一鑒權(quán)、獲取身份,一般會(huì)把身份信息放入請求的header中,也不會(huì)修改請求和響應(yīng)的內(nèi)容啊,欣宸前面的一堆解釋還是沒說清楚為啥要在網(wǎng)關(guān)位置修改請求和響應(yīng)的內(nèi)容!
  • 好吧,面對聰明的您,我攤牌了:本篇只是從技術(shù)上演示Spring Cloud Gateway如何修改請求和響應(yīng)內(nèi)容,請不要將此技術(shù)與實(shí)際后臺(tái)業(yè)務(wù)耦合;

 

你不孤單,欣宸原創(chuàng)一路相伴

到此這篇關(guān)于Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容的文章就介紹到這了,更多相關(guān)Spring Cloud Gateway請求響應(yīng)body內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://blog.csdn.net/boling_cavalry/article/details/120096926

延伸 · 閱讀

精彩推薦
  • Java教程Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關(guān)于小米推送Java代碼,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧...

    富貴穩(wěn)中求8032021-07-12
  • Java教程20個(gè)非常實(shí)用的Java程序代碼片段

    20個(gè)非常實(shí)用的Java程序代碼片段

    這篇文章主要為大家分享了20個(gè)非常實(shí)用的Java程序片段,對java開發(fā)項(xiàng)目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程xml與Java對象的轉(zhuǎn)換詳解

    xml與Java對象的轉(zhuǎn)換詳解

    這篇文章主要介紹了xml與Java對象的轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下...

    Java教程網(wǎng)2942020-09-17
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經(jīng)有好久沒有升過級了。升級完畢重啟之后,突然發(fā)現(xiàn)好多錯(cuò)誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java8中Stream使用的一個(gè)注意事項(xiàng)

    Java8中Stream使用的一個(gè)注意事項(xiàng)

    最近在工作中發(fā)現(xiàn)了對于集合操作轉(zhuǎn)換的神器,java8新特性 stream,但在使用中遇到了一個(gè)非常重要的注意點(diǎn),所以這篇文章主要給大家介紹了關(guān)于Java8中S...

    阿杜7482021-02-04
  • Java教程Java實(shí)現(xiàn)搶紅包功能

    Java實(shí)現(xiàn)搶紅包功能

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)搶紅包功能,采用多線程模擬多人同時(shí)搶紅包,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙...

    littleschemer13532021-05-16
主站蜘蛛池模板: 免费看国产一级特黄aa大片 | 猛男强攻变sao货 | 日韩免费 | 国产嫩草视频 | 隔壁的漂亮邻居hd中文 | 日韩伦理在线免费观看 | gogo人体模特啪啪季玥图片 | 欧美军人男同69gay | 黑人粗又长 | 99re8在线精品视频免费播放 | 亚洲精品tv久久久久久久久久 | 日韩欧美一区二区三区免费观看 | 日韩一二三| 久久一er精这里有精品 | 乌克兰黄色录像 | 国产亚洲欧美日韩俺去了 | yellow高清免费 | mm131亚洲精品久久 | 久久久无码精品亚洲A片猫咪 | 欧美专区在线播放 | 肥胖老寡妇做性 | 好大好猛好爽好深视频免费 | 毛片网站免费观看 | 水多多www视频在线观看高清 | 性印度freehd| 国产精品露脸国语对白河北 | hezyo加勒比一区二区三区 | 久久re亚洲在线视频 | 青青热久免费精品视频精品 | 毛毛片在线 | 欧洲兽皇 | 洗濯屋H纯肉动漫在线观看 武侠艳妇屈辱的张开双腿 午夜在线观看免费观看 视频 | 久久久精品国产免费A片胖妇女 | 日韩亚洲欧美理论片 | avtt天堂网手机版亚洲 | 动漫美女被褥吸奶漫画漫画 | 欧美图片小说 | 精品国产精品国产偷麻豆 | 亚洲免费视 | 欧美日韩国产成人综合在线影院 | 美女天天色 |