SpringCloudGateWay个人笔记

核心概念:

Route(路由):
路由是构建⽹关的基本模块,它由 ID ,⽬标 URI ,⼀系列的断⾔和过滤器组成,如果断⾔为 true就  匹配该路由。
Predicate(断⾔、谓词): 开发⼈员可以匹配 HTTP 请求中的所有内容(例如请求头或请求参数),如果请求与断⾔相匹配则进 ⾏路由。
Filter(过滤): 指的是 Spring 框架中 GatewayFilter 的实例,使⽤过滤器,可以在请求被路由前或者之后对请求 进⾏修改。
⼯作流程:
1: 客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由
2: 将其发送到 Gateway Web Handler
3:Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执⾏业务逻辑,然后返回。过滤器之间⽤虚线分开是因为过滤器可能会在发送代理请求之前(“pre” )或之后( “post” )执⾏业务逻辑。
依赖:
<dependencies>
     <!-- spring-cloud gateway,底层基于netty -->
     <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-gateway</artifactId>
     </dependency>
     <!-- 端点监控 -->
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-actuator</artifactId>
     </dependency>
     <!-- nacos注册中⼼ -->
     <dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-nacosdiscovery</artifactId>
     </dependency>
</dependencies>

配置文件(动态路由)例子:

server:
  #gateway的端⼝
  port: 8888
spring:
  application:
    name: cloud-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
        - id: service-user
          uri: lb://service-user
          predicates:
            - Path=/user/**

谓词工厂:

spring:
  cloud:
    gateway:
      routes:
        - id: wfx-jifen
         uri: lb://wfx-goods
         predicates:
           - Path=/goods/detail/100
           - After=2022-11-28T15:26:40.626+08:00[Asia/Shanghai]
           - Cookie=name,jack
           - Header=token
           - Host=**.baidu.com,**.taobao.com
           - Query=name,tom
           - RemoteAddr=192.168.56.10,192.168.56.11

过滤器:

分成了全局和局部过滤器。局部只针对某一路由,全局针对所有路由。

使用内置过滤器:

spring:
  cloud:
    gateway:
      routes:
        - id: add_request_header_route
         uri: https://example.org
         filters:
         - AddRequestHeader=Foo, Bar

一般内置过滤器无法满足需求,所以经常使用自定义过滤器。自定义全局过滤器(请求是否带token的例子):

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import com.google.gson.JsonObject;

@Component
public class AuthFilter implements GlobalFilter, Ordered {

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    //针对所有的路由进⾏过滤
    @Override
    public Mono<Void> filter(ServerWebExchange exchange,
                             GatewayFilterChain chain) {
        //过滤器的前处理
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //获取当前的请求连接
        String path = request.getURI().getPath();
        if(!(path.contains("api/ucenter/login")||path.contains("api/ucenter/register")||path.contains("admin/acl/user/**"))){
            String token = request.getHeaders().getFirst("token");
            if(StringUtils.isEmpty(token)){
                JsonObject message = new JsonObject();
                message.addProperty("success", false);
                message.addProperty("code", 28004);
                message.addProperty("data", "没有登录");
                return response(response,message);
            }else{
                String tokenRedis = redisTemplate.opsForValue().get("token");
                if(!tokenRedis.equals(token)){
                    JsonObject message = new JsonObject();
                    message.addProperty("success", false);
                    message.addProperty("code", 28004);
                    message.addProperty("data", "令牌无效");
                    return response(response,message);
                }else{
                    return chain.filter(exchange); //放⾏
                }
            }
        }
        return chain.filter(exchange);
    }
    private Mono<Void> response(ServerHttpResponse response,Object message){
        response.getHeaders().add("Content-Type",
                "application/json;charset=UTF-8");
        byte[] bits = message.toString().getBytes(StandardCharsets.UTF_8);
        DataBuffer dataBuffer =
                response.bufferFactory().wrap(bits);
        return response.writeWith(Mono.just(dataBuffer));//响应json数据
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

自定义异常处理:

服务网关调用服务时返回的错误信息对开发人员并不是很友好,所以可以自定义异常处理:

import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.server.*;
import java.util.HashMap;
import java.util.Map;

//自定义异常处理
public class JsonExceptionHandler extends DefaultErrorWebExceptionHandler {
    public JsonExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
                                ErrorProperties errorProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
    }
    /**
     * 获取异常属性
     */
    @Override
    protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
        Map<String, Object> map = new HashMap<>();
        map.put("success", false);
        map.put("code", 20005);
        map.put("message", "网关失败");
        map.put("data", null);
        return map;
    }
    /**
     * 指定响应处理方法为JSON处理的方法
     * @param errorAttributes
     */
    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }
    /**
     * 根据code获取对应的HttpStatus
     * @param errorAttributes
     */
    @Override
    protected int getHttpStatus(Map<String, Object> errorAttributes) {
        return HttpStatus.OK.value();
    }
}
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;
import java.util.Collections;
import java.util.List;

//覆盖默认的异常处理
@Configuration
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
public class ErrorHandlerConfig {
    private final ServerProperties serverProperties;
    private final ApplicationContext applicationContext;
    private final ResourceProperties resourceProperties;
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;
    public ErrorHandlerConfig(ServerProperties serverProperties,
                              ResourceProperties resourceProperties,
                              ObjectProvider<List<ViewResolver>> viewResolversProvider,
                              ServerCodecConfigurer serverCodecConfigurer,
                              ApplicationContext applicationContext) {
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.resourceProperties = resourceProperties;
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
        JsonExceptionHandler exceptionHandler = new JsonExceptionHandler(
                errorAttributes,
                this.resourceProperties,
                this.serverProperties.getError(),
                this.applicationContext);
        exceptionHandler.setViewResolvers(this.viewResolvers);
        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
        return exceptionHandler;
    }
}

跨域:

由于 gateway 使⽤的是 webflux ,⽽不是 springmvc ,所以需要先关闭 springmvc cors ,再从gateway filter ⾥边设置 cors 就⾏了。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

@Configuration
public class CorsConfig {
    //处理跨域
    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(source);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值