当前位置:   article > 正文

【SpringCloud 2021.0.0】14、路由网关Gateway之Predicate与Filter (spring-boot 2.6.3)_gateway predicates 多个路径

gateway predicates 多个路径

1、Predicate

Predicate来自于java8的接口(断定式接口)。Predicate接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)。可以用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。add–与、or–或、negate–非

在这里插入图片描述

1)After

After Route Predicate Factory使用的是时间作为匹配规则,只要当前时间大于设定时间,路由才会匹配请求

spring:  
    cloud:    
        gateway:      
            routes:      
            - id: after_route        
              uri: http://www.google.com        
              predicates:        
              - After=2018-12-25T14:33:47.789+08:00
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这个路由规则会在东8区的2018-12-25 14:33:47后,将请求都转跳到google。

2)Before

Before Route Predicate Factory也是使用时间作为匹配规则,只要当前时间小于设定时间,路由才会匹配请求

spring:  
    cloud:    
        gateway:      
            routes:      
            - id: before_route        
              uri: http://www.google.com        
              predicates:        
              - Before=2018-12-25T14:33:47.789+08:00
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这个路由规则会在东8区的2018-12-25 14:33:47前,将请求都转跳到google

3)Between

Between Route Predicate Factory也是使用两个时间作为匹配规则,只要当前时间大于第一个设定时间,并小于第二个设定时间,路由才会匹配请求

spring:  
    cloud:   
        gateway:      
            routes:      
            - id: between_route        
              uri: http://www.google.com        
              predicates:        
              - Between=2018-12-25T14:33:47.789+08:00, 2018-12-26T14:33:47.789+08:00
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这个路由规则会在东8区的2018-12-25 14:33:47到2018-12-26 14:33:47之间,将请求都转跳到google

4)Cookie

Cookie Route Predicate Factory使用的是cookie名字和正则表达式的value作为两个输入参数,请求的cookie需要匹配cookie名和符合其中value的正则

spring: 
    cloud:    
        gateway:  
            routes:      
            - id: cookie_route       
              uri: http://www.google.com        
              predicates:        
              - Cookie=cookiename, cookievalue
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

路由匹配请求存在cookie名为cookiename,cookie内容匹配cookievalue的,将请求转发到google

5)Header

Header Route Predicate Factory,与Cookie Route Predicate Factory类似,也是两个参数,一个header的name,一个是正则匹配的value

spring:  
    cloud:    
        gateway:     
            routes:      
            - id: header_route        
              uri: http://www.google.com        
              predicates:        
              - Header=X-Request-Id, \d+
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

路由匹配存在名为X-Request-Id,内容为数字的header的请求,将请求转发到google

6)Host

Host Route Predicate Factory使用的是host的列表作为参数,host使用Ant style匹配

spring:  
    cloud:    
        gateway:      
            routes:      
            - id: host_route        
              uri: http://www.google.com        
              predicates:        
              - Host=**.somehost.org,**.anotherhost.org
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

路由会匹配Host诸如:www.somehost.orgbeta.somehost.orgwww.anotherhost.org等请求

7)Method

Method Route Predicate Factory是通过HTTP的method来匹配路由

spring:  
    cloud:    
        gateway:      
            routes:      
            - id: method_route        
              uri: http://www.google.com        
              predicates:        
              - Method=GET
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

路由会匹配到所有GET方法的请求

8)Path

Path Route Predicate Factory使用的是path列表作为参数,使用Spring的PathMatcher匹配path,可以设置可选变量

spring:  
    cloud:    
        gateway:      
            routes:      
            - id: host_route        
              uri: http://www.google.com       
              predicates:        
              - Path=/foo/{segment},/bar/{segment}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上面路由可以匹配诸如:/foo/1/foo/bar/bar/baz

9)Query

Query Route Predicate Factory可以通过一个或两个参数来匹配路由,一个是查询的name,一个是查询的正则value

spring:  
	cloud:    
		gateway:      
			routes:      
			- id: query_route        
			  uri: http://www.google.com        
			  predicates:        
			  - Query=baz
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

路由会匹配所有包含baz查询参数的请求

spring:  
	cloud:    
		gateway:      
			routes:      
			- id: query_route       
              uri: http://www.google.com        
              predicates:       
              - Query=foo, ba.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

路由会匹配所有包含foo,并且foo的内容为诸如:barbaz等符合ba.正则规则的请求

10)RemoteAddr

RemoteAddr Route Predicate Factory通过无类别域间路由(IPv4 or IPv6)列表匹配路由

spring:  
	cloud:    
		gateway:    
        	routes:      
        	- id: remoteaddr_route        
        	  uri: http://www.google.com        
        	  predicates:        
        	  - RemoteAddr=192.168.1.1/24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上面路由就会匹配RemoteAddr诸如192.168.1.10等请求

2、Filter

Route filters可以通过一些方式修改HTTP请求的输入和输出,针对某些特殊的场景,Spring Cloud Gateway已经内置了很多不同功能的GatewayFilter Factories。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

1)AddRequestHeader

AddRequestHeader GatewayFilter Factory通过配置name和value可以增加请求的header

spring:  
	cloud:    
		gateway:   
        	routes: 
            - id: add_request_header_route  
              uri: http://www.google.com  
              filters:
              - AddRequestHeader=X-Request-Foo, Bar
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

对匹配的请求,会额外添加X-Request-Foo:Bar的header

2)AddRequestParameter

AddRequestParameter GatewayFilter Factory通过配置name和value可以增加请求的参数

spring:  
	cloud:  
    	gateway:   
        	routes:      
        	- id: add_request_parameter_route     
              uri: http://www.google.com     
              filters:       
              - AddRequestParameter=foo, bar
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

对匹配的请求,会额外添加foo=bar的请求参数

3)AddResponseHeader

AddResponseHeader GatewayFilter Factory通过配置name和value可以增加响应的header

spring:  
	cloud:   
    	gateway:  
        	routes:      
        	- id: add_request_header_route    
              uri: http://www.google.com     
              filters:        
              - AddResponseHeader=X-Response-Foo, Bar
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

对匹配的请求,响应返回时会额外添加X-Response-Foo:Bar的header返回

4)Hystrix

Hystrix是Netflix实现的断路器模式工具包,The Hystrix GatewayFilter就是将断路器使用在gateway的路由上,目的是保护你的服务避免级联故障,以及在下游失败时可以降级返回

项目里面引入spring-cloud-starter-netflix-hystrix依赖,并提供HystrixCommand的名字,即可生效Hystrix GatewayFilter

spring:  
	cloud:    
		gateway:      
			routes:     
        	- id: hystrix_route      
              uri: http://www.google.com     
              filters:        
              - Hystrix=myCommandName
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

那么剩下的过滤器,就会包装在名为myCommandName的HystrixCommand中运行。

Hystrix过滤器也是通过配置参数fallbackUri,来支持路由熔断后的降级处理,降级后,请求会跳过fallbackUri配置的路径,目前只支持forward:的URI协议

spring:
  cloud:
    gateway:
      routes:
      - id: hystrix_route
        uri: lb://backing-service:8088
        predicates:
        - Path=/consumingserviceendpoint
        filters:
        - name: Hystrix
          args:
            name: fallbackcmd
            fallbackUri: forward:/incaseoffailureusethis
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

当Hystrix降级后就会将请求转发到/incaseoffailureusethis

整个流程其实是用fallbackUri将请求跳转到gateway内部的controller或者handler,然而也可以通过以下的方式将请求转发到外部的服务

spring:
  cloud:
    gateway:
      routes:
      - id: ingredients
        uri: lb://ingredients
        predicates:
        - Path=//ingredients/**
        filters:
        - name: Hystrix
          args:
            name: fetchIngredients
            fallbackUri: forward:/fallback
      - id: ingredients-fallback
        uri: http://localhost:9994
        predicates:
        - Path=/fallback
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

以上的例子,gateway降级后就会将请求转发到http: //localhost:9994。

Hystrix Gateway filter在转发降级请求时,会将造成降级的异常设置在ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR属性中,在处理降级时也可以用到。

比如下一节讲到的FallbackHeaders GatewayFilter Factory,就会通过上面的方式拿到异常信息,设置到降级转发请求的header上,来告知降级下游异常信息。

通过下面配置可以设置Hystrix的全局超时信息

hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 5000
  • 1

5)FallbackHeaders

FallbackHeaders GatewayFilter Factory可以将Hystrix执行的异常信息添加到外部请求的fallbackUriheader上

spring:
  cloud:
    gateway:
      routes:
      - id: ingredients
        uri: lb://ingredients
        predicates:
        - Path=//ingredients/**
        filters:
        - name: Hystrix
          args:
            name: fetchIngredients
            fallbackUri: forward:/fallback
      - id: ingredients-fallback
        uri: http://localhost:9994
        predicates:
        - Path=/fallback
        filters:
        - name: FallbackHeaders
          args:
            executionExceptionTypeHeaderName: Test-Header
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

在这个例子中,当请求lb://ingredients降级后,FallbackHeadersfilter会将HystrixCommand的异常信息,通过Test-Header带给http://localhost:9994服务。

你也可以使用默认的header,也可以像上面一下配置修改header的名字:

  • executionExceptionTypeHeaderName (“Execution-Exception-Type”)
  • executionExceptionMessageHeaderName (“Execution-Exception-Message”)
  • rootCauseExceptionTypeHeaderName (“Root-Cause-Exception-Type”)
  • rootCauseExceptionMessageHeaderName (“Root-Cause-Exception-Message”)

6)PrefixPath

The PrefixPath GatewayFilter Factory通过设置prefix参数来添加路径前缀

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: http://www.google.com
        filters:
        - PrefixPath=/mypath
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如果一个请求是/hello,通过上面路由,就会将请求修改为/mypath/hello

7)PreserveHostHeader

PreserveHostHeader GatewayFilter Factory会保留原始请求的host头信息,并原封不动的转发出去,而不是被gateway的http客户端重置

spring:
  cloud:
    gateway:
      routes:
      - id: preserve_host_route
        uri: http://www.google.com
        filters:
        - PreserveHostHeader
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

8)RequestRateLimiter

RequestRateLimiter GatewayFilter Factory使用RateLimiter来决定当前请求是否允许通过,如果不允许,则默认返回状态码HTTP 429 - Too Many Requests

RequestRateLimiter GatewayFilter可以使用一个可选参数keyResolver来做速率限制。

keyResolver是KeyResolver接口的一个实现bean,在配置里面,通过SpEL表达式#{@myKeyResolver}来管理bean的名字myKeyResolver

  • KeyResolver.java
public interface KeyResolver {
	Mono<String> resolve(ServerWebExchange exchange);
}
  • 1
  • 2
  • 3

KeyResolver接口允许你使用不同的策略来得出限制请求的key,未来,官方也会推出一些KeyResolver的不同实现。

KeyResolver默认实现是PrincipalNameKeyResolver,通过ServerWebExchange中获取Principal,并以Principal.getName()作为限流的key。

如果KeyResolver拿不到key,请求默认都会被限制,你也可以自己配置spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key:是否允许空key
spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code :空key时返回的状态码。

RequestRateLimiter不支持捷径配置,如下面的配置是非法的

# INVALID SHORTCUT CONFIGURATION
spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver} 
  • 1
  • 2

9)RedirectTo

RedirectTo GatewayFilter Factory使用statusurl两个参数,其中status必须是300系列的HTTP状态码,url则是跳转的地址,会放在响应的Location的header中(http协议中转跳的header)

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: http://www.google.cn
        filters:
        - RedirectTo=302, http://www.edjdhbb.com 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上面路由会执行302重定向到 http : //www. edjdhbb.com

10)RemoveNonProxyHeaders

RemoveNonProxyHeaders GatewayFilter Factory转发请求是会根据IETF的定义,默认会移除下列的http头信息:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • TE
  • Trailer
  • Transfer-Encoding
  • Upgrade

你也可以通过配置spring.cloud.gateway.filter.remove-non-proxy-headers.headers来更改需要移除的header列表

11)RemoveRequestHeader

RemoveRequestHeader GatewayFilter Factory配置header的name,即可以移除请求的header

spring:
  cloud:
    gateway:
      routes:
      - id: removerequestheader_route
        uri: http://www.google.com
        filters:
        - RemoveRequestHeader=X-Request-Foo 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上面路由在发送请求给下游时,会将请求中的X-Request-Foo头信息去掉

12)RemoveResponseHeader

RemoveResponseHeader GatewayFilter Factory通过配置header的name,会在响应返回时移除header

spring:
  cloud:
    gateway:
      routes:
      - id: removeresponseheader_route
        uri: http://www.google.com
        filters:
        - RemoveResponseHeader=X-Response-Foo 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上面路由会在响应返回给gateway的客户端时,将X-Response-Foo响应头信息去掉

13)RewritePath

RewritePath GatewayFilter Factory使用路径regexp和替换路径replacement两个参数做路径重写,两个都可以灵活地使用java的正则表达式

spring:
  cloud:
    gateway:
      routes:
      - id: rewritepath_route
        uri: http://www.google.com
        predicates:
        - Path=/foo/**
        filters:
        - RewritePath=/foo/(?<segment>.*), /$\{segment} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

对于上面的例子,如果请求的路径是/foo/bar,则gateway会将请求路径改为/bar发送给下游。

注:在YAML 的格式中使用$\来代替$

14)RewriteResponseHeader

RewriteResponseHeader GatewayFilter Factory的作用是修改响应返回的header内容,需要配置响应返回的header的name,匹配规则regexp和替换词replacement,也是支持java的正则表达式

spring:
  cloud:
    gateway:
      routes:
      - id: rewriteresponseheader_route
        uri: http://www.google.com
        filters:
        - RewriteResponseHeader=X-Response-Foo, , password=[^&]+, password=*** 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

举个例子,对于上面的filter,如果响应的headerX-Response-Foo的内容是/42?user=ford&password=omg!what&flag=true,这个内容会修改为/42?user=ford&password=***&flag=true

15)SaveSession

SaveSession GatewayFilter Factory会在请求下游时强制执行WebSession::save方法,用在那种像Spring Session延迟数据存储的,并在请求转发前确保session状态保存情况

spring:
  cloud:
    gateway:
      routes:
      - id: save_session
        uri: http://www.google.com
        predicates:
        - Path=/foo/**
        filters:
        - SaveSession
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

如果你将Spring SecutirySpring Session集成使用,并想确保安全信息都传到下游机器,你就需要配置这个filter

16)SecureHeaders

SecureHeaders GatewayFilter Factory会添加在返回响应中一系列安全作用的header

默认会添加这些头信息和默认内容:

  • X-Xss-Protection:1; mode=block
  • Strict-Transport-Security:max-age=631138519
  • X-Frame-Options:DENY
  • X-Content-Type-Options:nosniff
  • Referrer-Policy:no-referrer
  • Content-Security-Policy:default-src ‘self’ https:; font-src ‘self’ https: data:; img-src ‘self’ https: data:; object-src ‘none’; script-src https:; style-src ‘self’ https: ‘unsafe-inline’
  • X-Download-Options:noopen
  • X-Permitted-Cross-Domain-Policies:none

如果你想修改这些头信息的默认内容,可以在配置文件中添加下面的配置:

前缀:spring.cloud.gateway.filter.secure-headers

上面的header对应的后缀:

  • xss-protection-header
  • strict-transport-security
  • frame-options
  • content-type-options
  • referrer-policy
  • content-security-policy
  • download-options
  • permitted-cross-domain-policies

前后缀接起来即可,如:spring.cloud.gateway.filter.secure-headers.xss-protection-header

17)SetPath

SetPath GatewayFilter Factory采用路径template参数,通过请求路径的片段的模板化,来达到操作修改路径的目的,运行多个路径片段模板化

spring:
  cloud:
    gateway:
      routes:
      - id: setpath_route
        uri: http://www.google.com
        predicates:
        - Path=/foo/{segment}
        filters:
        - SetPath=/{segment}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

对于上面的例子,如果路径是/foo/bar,则对于下游的请求路径会修改为/bar

18)SetResponseHeader

SetResponseHeader GatewayFilter Factory通过设置namevalue来替换响应返回的header

spring:  
   cloud:  
   	gateway:      	
   		routes:      
   		- id: setresponseheader_route      
          uri: http://www.google.com      
          filters:      
          - SetResponseHeader=X-Response-Foo, Bar
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

对于上面的例子,如果下游的返回带有头信息为X-Response-Foo:1234,则gateway会替换为X-Response-Foo:Bar,再返回给客户端

19)SetStatus

SetStatus GatewayFilter Factory通过配置有效的Spring HttpStatus枚举参数,可以是类似于404的这些数字,也可以是枚举的name字符串,来修改响应的返回码

spring:
  cloud:
    gateway:
      routes:
      - id: setresponseheader_route
        uri: http://www.google.com
        filters:
        - SetResponseHeader=X-Response-Foo, Bar 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上面例子中,两种路由都会将响应的状态码设置为401

20)StripPrefix

StripPrefix GatewayFilter Factory通过配置parts来表示截断路径前缀的数量

spring:
  cloud:
    gateway:
      routes:
      - id: nameRoot
        uri: http://nameservice
        predicates:
        - Path=/name/**
        filters:
        - StripPrefix=2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

如上面例子中,如果请求的路径为/name/bar/foo,则路径会修改为/foo,即将路径的两个前缀去掉了

21)Retry

Retry GatewayFilter Factory可以配置针对不同的响应做请求重试,可以配置如下参数:

  • retries: 重试次数
  • statuses: 需要重试的状态码,需要根据枚举 org.springframework.http.HttpStatus来配置
  • methods: 需要重试的请求方法,需要根据枚举org.springframework.http.HttpMethod来配置
  • series: HTTP状态码系列,详情见枚举org.springframework.http.HttpStatus.Series
spring:
  cloud:
    gateway:
      routes:
      - id: retry_test
        uri: http://localhost:8080/flakey
        predicates:
        - Host=*.retry.com
        filters:
        - name: Retry
          args:
            retries: 3
            statuses: BAD_GATEWAY
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

上面例子,当下游服务返回502状态码时,gateway会重试3次

22)RequestSize

RequestSize GatewayFilter Factory会限制客户端请求包的大小,通过参数RequestSize来配置最大上传大小,单位字节

spring:
  cloud:
    gateway:
      routes:
      - id: request_size_route
      uri: http://localhost:8080/upload
      predicates:
      - Path=/upload
      filters:
      - name: RequestSize
        args:
          maxSize: 5000000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

如果请求大小超过5000kb限制,则会返回状态码413 Payload Too Large

如果不设置这个filter,默认限制5M的请求大小。

23)ModifyRequestBody

官方说这个filter目前只是beta版本,API以后可能会修改

Modify Request Body GatewayFilter Factory可以修改请求体内容,这个只能通过java来配置

@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();
}
 
static class Hello {
    String message;
 
    public Hello() { }
 
    public Hello(String message) {
        this.message = message;
    }
 
    public String getMessage() {
        return message;
    }
 
    public void setMessage(String message) {
        this.message = message;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

24)ModifyResponseBody

官方说这个filter目前只是beta版本,API以后可能会修改

Modify Response Body GatewayFilter Factory用于修改响应返回的内容,同样只能通过java配置

@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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/302171
推荐阅读
相关标签
  

闽ICP备14008679号