当前位置: 代码网 > it编程>编程语言>Java > SpringCloud-Gateway配置及持久化、过滤器、异常处理

SpringCloud-Gateway配置及持久化、过滤器、异常处理

2024年08月03日 Java 我要评论
gateway配置及持久化、过滤器、异常处理


gateway不能和web一起使用 需要排除掉

        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-gateway</artifactid>
        </dependency>

yml配置

spring:
  main:
    allow-circular-references: true #解决循环依赖,暂时跳过
  application:
    name: gateway-server
  cloud:
    # https://cloud.tencent.com/developer/article/1650115?from=15425
    gateway:
      routes:
        # 消息服务
        - id: message-server
          # 匹配后路径 配合nacos服务名称
          uri: lb://message-server
          predicates:
            # 断言路劲,匹配成功后就走uri,多个用逗号分隔
            - path=/api/msg/**
            #- after=2020-03-08t10:59:34.102+08:00[asia/shanghai] 在什么时间段之前才匹配
            #- cookie=username,zhangshuai #并且cookie是username=zhangshuai才能访问
            #- header=x-request-id, \d+ #请求头中要有x-request-id属性并且值为整数的正则表达式
            #- host=**.tecloman.cn 主机名相同才能转发
            #- method=get  请求方法匹配
            #- query=username, \d+ #要有参数名称并且是正整数才能路由
        # 计算服务
        - id: computer-server
          uri: lb://computer-server
          predicates:
            - path=/energystoragestation/**
        # web服务
        - id: hss-server
          #有多个hss-server服务,测试连不上生产的数据库,要超时报错
          #uri: lb://hss-server
          uri: http://localhost:1000
          predicates:
            - path=/api/swagger/**,/api/hss/**,/api/ruralgrid/**
            #- path=[/api/hss/**,/api/sys/**,/api/admin/**,/api/app/**,/api/openapi/**,/api/distributed/**,/api/ezviz/**,/api/ruralgrid/**,/api/swagger/**]
      # 全局的跨域处理
      globalcors:
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        corsconfigurations:
          '[/**]': # 哪些访问地址做跨域处理
            allowedorigins: # 允许哪些网站的跨域请求
              - "http://localhost:8090"
            allowedmethods: # 允许的跨域ajax的请求方式
              - "get"
              - "post"
              - "delete"
              - "put"
              - "options"
            allowedheaders: "*" # 允许在请求中携带的头信息
            allowcredentials: true # 是否允许携带cookie
            maxage: 360000  # 这次跨域检测的有效期

使用 uri: http://localhost:1000 指定网址使用,打包到linux服务器,直接使用docker部署会出问题,docker容器中不能使用localhost,服务运行后应查询到ip地址后再修改gateway的配置再部署

docker inspect --format '{{ .networksettings.ipaddress }}' <container-id>
或
docker inspect <container id> 

代码配置

package gateway.server.config;

import org.springframework.beans.factory.annotation.value;
import org.springframework.cloud.gateway.route.routelocator;
import org.springframework.cloud.gateway.route.builder.routelocatorbuilder;
import org.springframework.context.annotation.bean;
import org.springframework.http.server.reactive.serverhttprequest;
import org.springframework.stereotype.component;

import javax.servlet.http.httpservletrequest;

/**
 *网关接口路由配置
 *@author chens
 *@date  2022-12-05更新
 */

public class gatewayconfig {
    @bean
    public routelocator customroutelocator(routelocatorbuilder builder) {
        // 通信服务的接口,因为包含在web服务里面,有点特殊
        return builder.routes()
                .route("hss-server", r -> r.path(
                        // path最好不要写死,
                        "/api/captcha.jpg/**",
                        "/api/ruralgrid/**",
                        "/api/test/**")
                        // 使用order来处理接口包含关系,web服务包含全部接口,
                        // 其它服务无法处理的情况,web服务最后执行
                        .and().order(0)
                        // 有多个重名的服务,本地连不上生产数据库,最好采用http方式
                        //.uri(url)
                        .uri("lb://cs-test-hss-server")
                )
                // 消息服务
                .route("message-server", r -> r.path(
                        "/api/msg/**")
                        .uri("lb://message-server"))
                // 运算服务
                .route("computer-server", r -> r.path(
                        "/energystoragestation/**")
                        .uri("lb://computer-server"))
                // 通信服务
                .route("communications-server", r -> r.path(
                        "/api/hss/classify/**",
                        "/api/hss/type/**",
                        "/api/hss/strategy/**",
                        "/api/hss/protocol/**",
                        "/api/sys/script/**")
                        // 在web服务前面先执行,
                        .and().order(1)
                        .uri("lb://communications-server"))
                .build();
    }
    //跨域配置
    @bean
    public corswebfilter corswebfilter(){
        urlbasedcorsconfigurationsource source = new urlbasedcorsconfigurationsource();
        corsconfiguration configuration = new corsconfiguration();
        // 配置跨域的信息
        configuration.addallowedheader("*");
        configuration.addallowedmethod("*");
        // springboot升级到2.4.0 之后需要使用该配置
        configuration.addallowedoriginpattern("*");
        configuration.setallowcredentials(true);
        source.registercorsconfiguration("/**",configuration);
        return new corswebfilter(source);
    }
}

uri: lb://computer-server 采用服务名转发 需引入

        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-loadbalancer</artifactid>
        </dependency>

持久化

访问端点需要引入

        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-actuator</artifactid>
        </dependency>
management:
  endpoints:
    web:
      base-path: /root #根路劲 默认actuator
      exposure:
        include: "*" #暴露所有接口
    # server:
    #这和服务端口一样 那就没法走路由,过滤器不会生效
    # port: 8888

查看路由节点
localhost:8090/root/gateway/routes
在这里插入图片描述gateway提供的类gatewaycontrollerendpoint 包含了crud的接口
gateway/routes 就是其中一个接口
现在我们自己写crud,因为gateway操作的全是内存上的数据,现在需要把数据存入数据库,项目启动从数据库读取配置
建表sql

create table `gateway_route` (
	`id` int not null auto_increment,
	`name` varchar ( 255 ) character 
	set utf8 collate utf8_general_ci not null comment '路由名称',
	`route_id` varchar ( 255 ) character 
	set utf8 collate utf8_general_ci not null comment '路由key',
	`uri` varchar ( 255 ) character 
	set utf8 collate utf8_general_ci not null comment '转发url',
	`predicates` json not null comment '断言数据',
	`filters` json not null comment '过滤数据',
	`order_num` int default null comment '顺序',
	`state` tinyint ( 1 ) not null default '0' comment '是否启用 0未启用 1启用',
	`remark` varchar ( 255 ) character 
	set utf8 collate utf8_general_ci default null comment '备注',
	`create_time` datetime not null comment '创建时间',
	`create_user_id` bigint not null comment '创建人id',
	`dtime` bit ( 1 ) not null default b '0' comment '逻辑删除标记',
primary key ( `id` ) using btree 
) engine = innodb auto_increment = 17 default charset = utf8mb3 row_format = dynamic comment = '<dodo-server-app-manager>上架应用路由信息表';

持久框架 mp autoresultmap json字段自动映射

package gateway.server.hss.entity;

import com.alibaba.fastjson.jsonarray;
import com.baomidou.mybatisplus.annotation.tablefield;
import com.baomidou.mybatisplus.annotation.tableid;
import com.baomidou.mybatisplus.annotation.tablename;
import com.baomidou.mybatisplus.extension.activerecord.model;
import com.baomidou.mybatisplus.extension.handlers.jacksontypehandler;
import lombok.data;

import java.io.serializable;
import java.util.date;

/**
 * 网关路由配置
 * @author chens
 */
@data
@tablename(value = "gateway_route", autoresultmap = true)
public class gatewayrouterentity extends model<gatewayrouterentity> implements serializable {
    private static final long serialversionuid = 1l;
    @tableid
    private integer id;
    /**
     * 路由名称
     */
    private string name;
    /**
     * 路由key
     */
    private string routeid;
    /**
     * 转发url
     */
    private string uri;
    /**
     * 断言数据
     */
    @tablefield(typehandler = jacksontypehandler.class)
    private jsonarray predicates;
    /**
     * 过滤数据
     */
    @tablefield(typehandler = jacksontypehandler.class)
    private jsonarray filters;
    /**
     * 备注
     */
    private string remark;
    /**
     * 执行顺序
     */
    private int ordernum = 0;
    /**
     * 状态 0未启用 1启用
     */
    private int state = 0;
    /**
     * 创建人id
     */
    private long createuserid;
    @tablefield(exist = false)
    private string createusername;
    private date createtime;
    private int dtime = 0;
}

gateway只能使用serverwebexchange 获取请求信息 ,不能使用httpservletrequest
serverwebexchange exchange

package gateway.server.hss.controller;

import gateway.server.util.pageutils;
import gateway.server.util.r;
import gateway.server.hss.entity.gatewayrouterentity;
import gateway.server.hss.service.gatewayrouteservice;
import gateway.server.hss.service.impl.dynamicrouteservice;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.web.bind.annotation.deletemapping;
import org.springframework.web.bind.annotation.getmapping;
import org.springframework.web.bind.annotation.pathvariable;
import org.springframework.web.bind.annotation.postmapping;
import org.springframework.web.bind.annotation.requestbody;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.restcontroller;
import org.springframework.web.server.serverwebexchange;

import javax.servlet.http.httpservletrequest;
import java.util.map;

/**
 * 网关路由配置
 *
 * @author chens
 * @create 2022-12-7
 * @desc
 **/
//@component
//@restcontrollerendpoint(id = "chen")
@restcontroller
@requestmapping("/gateway/route")
public class gatawayroutecontroller {
    @autowired
    private gatewayrouteservice gatewayrouteservice;
    private final dynamicrouteservice dynamicrouteservice;
    public gatawayroutecontroller(dynamicrouteservice dynamicrouteservice) {
        this.dynamicrouteservice = dynamicrouteservice;
    }

    @getmapping("/list")
    public r list(serverwebexchange exchange, map<string, object> params) {
        pageutils page = gatewayrouteservice.querypage(params);
        return r.ok().put("page", page);
    }

    @postmapping("/save")
    public r save(@requestbody gatewayrouterentity entity, serverwebexchange exchange) {
        return this.dynamicrouteservice.save(entity);
    }

    @postmapping("/update")
    public r update(serverwebexchange exchange, @requestbody gatewayrouterentity entity) {
        return this.dynamicrouteservice.update(entity);
    }

    /**
     * 修改路由状态
     *
     * @param routeid 路由id
     * @return
     */
    @postmapping("/upstate/{routeid}")
    public r upstate(serverwebexchange exchange,@pathvariable("routeid") string routeid) {
        return this.dynamicrouteservice.upstate(routeid);
    }
    /**
     * 删除路由
     *
     * @param routeid 路由id
     * @return
     */
    @postmapping("/delete/{routeid}")
    public r delete(serverwebexchange exchange, @pathvariable("routeid") string routeid) {
        return this.dynamicrouteservice.delete(routeid);
    }

    /**
     * 刷新路由
     *
     * @return
     */
    @getmapping("/flush")
    public r flush(serverwebexchange exchange) {
        return this.dynamicrouteservice.flushroute();
    }

}

主要crud类

package gateway.server.hss.service.impl;

import com.alibaba.fastjson.json;
import com.alibaba.fastjson.jsonarray;
import com.alibaba.fastjson.jsonobject;
import com.baomidou.mybatisplus.core.conditions.query.querywrapper;
import gateway.server.util.r;
import gateway.server.hss.entity.gatewayrouterentity;
import lombok.extern.log4j.log4j2;
import org.springframework.boot.applicationarguments;
import org.springframework.boot.applicationrunner;
import org.springframework.cloud.gateway.event.refreshroutesevent;
import org.springframework.cloud.gateway.filter.filterdefinition;
import org.springframework.cloud.gateway.handler.predicate.predicatedefinition;
import org.springframework.cloud.gateway.route.routedefinition;
import org.springframework.cloud.gateway.route.routedefinitionwriter;
import org.springframework.context.applicationeventpublisher;
import org.springframework.context.applicationeventpublisheraware;
import org.springframework.stereotype.service;
import org.springframework.web.util.uricomponentsbuilder;
import reactor.core.publisher.mono;

import java.net.uri;
import java.util.date;
import java.util.list;
import java.util.map;
import java.util.stream.collectors;

/**
 * @desc 动态路由配置
 **/
@service
@log4j2
public class dynamicrouteservice implements applicationeventpublisheraware, applicationrunner {

    private final routedefinitionwriter routedefinitionwriter;
    private gatewayrouteserviceimpl gatewayrouteserviceimpl;

    private applicationeventpublisher publisher;

    public dynamicrouteservice(routedefinitionwriter routedefinitionwriter, gatewayrouteserviceimpl gatewayrouteserviceimpl) {
        this.routedefinitionwriter = routedefinitionwriter;
        this.gatewayrouteserviceimpl = gatewayrouteserviceimpl;
    }


    /**
     * 增加路由
     *
     * @param gatewayrouterentity
     * @return
     */
    public r save(gatewayrouterentity gatewayrouterentity) {
        gatewayrouterentity one = getone(gatewayrouterentity.getrouteid());
        if (one != null) return r.error("路由id已存在");
        routedefinition definition = convertgateway(gatewayrouterentity);
        // 新增到内存中, 新增先暂不写入内存,更改状态再写入
        //routedefinitionwriter.save(mono.just(definition)).subscribe();
        // 保存到数据库中
        savedata(gatewayrouterentity.getname(), gatewayrouterentity.getremark(), definition);
        flushrouteconfig();
        return r.ok();
    }

    /**
     * 修改路由状态
     */
    public r upstate(string routeid) {
        gatewayrouterentity entity = getone(routeid);
        // 状态取反,判断状态 决定删除内存中的路由还是新增到内存中
        if (entity == null) return r.error("路由不存在");
        routedefinition definition = convertgateway(entity);
        if (entity.getstate() == 0) {
            entity.setstate(1);
            // 启用 新增到内存中
            routedefinitionwriter.save(mono.just(definition)).subscribe();
        } else if (entity.getstate() == 1) {
            entity.setstate(0);
            // 关闭 从内存中删除
            routedefinitionwriter.delete(mono.just(definition.getid())).subscribe();
        } else return r.error("路由状态异常");
        flushrouteconfig();
        // 状态取反 更新到数据库
        gatewayrouteserviceimpl.updatebyid(entity);
        return r.ok();
    }

    /**
     * 更新路由
     *
     * @param routeform
     * @return
     */
    public r update(gatewayrouterentity routeform) {
        gatewayrouterentity one = getone(routeform.getrouteid());
        if (one != null && !routeform.getid().equals(one.getid()) ) {
            return r.error("路由id已存在");
        }
        routedefinition definition = convertgateway(routeform);
        try {
            routedefinitionwriter.delete(mono.just(definition.getid())).subscribe();
        } catch (exception e) {
            return r.error("未知路由信息");
        }
        try {
            routedefinitionwriter.save(mono.just(definition)).subscribe();
            savedata(routeform.getname(), routeform.getremark(), definition);
            flushrouteconfig();
            return r.ok();
        } catch (exception e) {
            return r.error("路由信息修改失败!");
        }
    }

    /**
     * 删除路由
     *
     * @param routeid 路由id
     * @return
     */
    public r delete(string routeid) {
        this.routedefinitionwriter.delete(mono.just(routeid)).subscribe();
        gatewayrouteserviceimpl.remove(new querywrapper<gatewayrouterentity>().lambda().eq(gatewayrouterentity::getrouteid, routeid));
        flushrouteconfig();
        return r.ok();
    }

    /**
     * 刷新路由
     *
     * @return
     */
    private void flushrouteconfig() {
        this.publisher.publishevent(new refreshroutesevent(this));
    }


    public r flushroute() {
        flushrouteconfig();
        return r.ok();
    }

    @override
    public void setapplicationeventpublisher(applicationeventpublisher publisher) {
        this.publisher = publisher;
    }


    @override
    public void run(applicationarguments args) {
        log.info("----------从数据库加载额外路由信息---------");
        this.queryroute();
    }

    // 从数据库查询配置
    private void queryroute() {
        list<routedefinition> gatewaylist = gatewayrouteserviceimpl.list();
        log.info("----------数据库路由数量:{}---------", gatewaylist.size());
        // 数据库中的配置写入内存中
        gatewaylist.foreach(x -> routedefinitionwriter.save(mono.just(x)).subscribe());
        flushrouteconfig();
    }


    /**
     * 实体转换成gateway实体
     *
     * @param entity
     * @return
     */
    private routedefinition convertgateway(gatewayrouterentity entity) {
        routedefinition definition = new routedefinition();
        definition.setid(entity.getrouteid());
        definition.setorder(entity.getordernum());
        //设置断言
        list<predicatedefinition> predicatedefinitions = entity.getpredicates().stream()
                .distinct().map(x -> {
                    predicatedefinition predicate = new predicatedefinition();
                    map object = (map) x;
                    predicate.setargs((map) object.get("args"));
                    predicate.setname(object.get("name").tostring());
                    return predicate;
                }).collect(collectors.tolist());
        definition.setpredicates(predicatedefinitions);

        // 设置过滤
        list<filterdefinition> filterlist = entity.getfilters().stream()
                .distinct().map(x -> {
                    filterdefinition filter = new filterdefinition();
                    map object = (map) x;
                    filter.setargs((map) object.get("args"));
                    filter.setname(object.get("name").tostring());
                    return filter;
                }).collect(collectors.tolist());
        definition.setfilters(filterlist);
        // 设置uri,判断是否进行负载均衡
        uri uri;
        if (entity.geturi().startswith("http")) {
            uri = uricomponentsbuilder.fromhttpurl(entity.geturi()).build().touri();
        } else {
            uri = uri.create(entity.geturi());
        }
        definition.seturi(uri);
        return definition;
    }


    /**
     * 数据落库
     */
    public void savedata(string name, string remark, routedefinition definition) {
        string routeid = definition.getid();
        list<predicatedefinition> predicates = definition.getpredicates();
        list<filterdefinition> filters = definition.getfilters();
        int order = definition.getorder();
        uri uri = definition.geturi();
        gatewayrouterentity entity = new gatewayrouterentity();
        entity.setname(name);
        entity.setrouteid(routeid);
        entity.seturi(uri.tostring());
        entity.setpredicates(jsonarray.parsearray(json.tojsonstring(predicates)));
        entity.setfilters(jsonarray.parsearray(json.tojsonstring(filters)));
        entity.setremark(remark);
        entity.setordernum(order);
        entity.setcreateuserid(1l);
        entity.setcreatetime(new date());
        entity.setdtime(0);
        // 数据库不存在则保存,存在修改路由id保存
        gatewayrouterentity one = getone(routeid);
        if (one == null) {
            gatewayrouteserviceimpl.save(entity);
        } else {
            entity.setid(one.getid());
            gatewayrouteserviceimpl.updatebyid(entity);
        }
    }

    public gatewayrouterentity getone(string routeid) {
        gatewayrouterentity entity = gatewayrouteserviceimpl.getone(new querywrapper<gatewayrouterentity>().lambda()
                .eq(gatewayrouterentity::getrouteid, routeid).last("limit 1"), false);
        return entity;
    }

}

查询类

package gateway.server.hss.service.impl;

import com.alibaba.fastjson.jsonobject;
import com.baomidou.mybatisplus.core.conditions.query.querywrapper;
import com.baomidou.mybatisplus.core.metadata.ipage;
import com.baomidou.mybatisplus.extension.service.impl.serviceimpl;
import gateway.server.util.pageutils;
import gateway.server.util.query;
import gateway.server.hss.entity.gatewayrouterentity;
import gateway.server.hss.dao.routemapper;
import gateway.server.hss.service.gatewayrouteservice;
import org.springframework.cloud.gateway.filter.filterdefinition;
import org.springframework.cloud.gateway.handler.predicate.predicatedefinition;
import org.springframework.cloud.gateway.route.routedefinition;
import org.springframework.stereotype.service;

import java.net.uri;
import java.net.urisyntaxexception;
import java.util.hashmap;
import java.util.list;
import java.util.map;
import java.util.objects;
import java.util.stream.collectors;

/**
 * @create 2022-12-7 14:13
 * @desc 路由信息持久化
 **/
@service
public class gatewayrouteserviceimpl extends serviceimpl<routemapper, gatewayrouterentity> implements gatewayrouteservice {
    @override
    public pageutils querypage(map<string, object> params) {
        ipage page = this.page(new query<gatewayrouterentity>().getpage(params),
                new querywrapper<gatewayrouterentity>().eq("dtime", 0));
        return new pageutils(page);
    }


    public list<routedefinition> list() {
        // 只查询启用状态的配置
        list<gatewayrouterentity> list = list(new querywrapper<gatewayrouterentity>().lambda()
                .eq(gatewayrouterentity::getstate, 1).eq(gatewayrouterentity::getdtime, 0));
        return list.stream().map(x -> {
            routedefinition routedefinition = new routedefinition();
            routedefinition.setid(x.getrouteid());
            // 这里需要注意判空
            routedefinition.setpredicates(jsonobject.parsearray(x.getpredicates().tojsonstring(), predicatedefinition.class));
            routedefinition.setfilters(jsonobject.parsearray(x.getfilters().tojsonstring(), filterdefinition.class));
            try {
                routedefinition.seturi(new uri(x.geturi()));
                routedefinition.setorder(x.getordernum());
                routedefinition.setmetadata(new hashmap<>(2));
                return routedefinition;
            } catch (urisyntaxexception e) {
                return null;
            }
        }).filter(objects::nonnull).collect(collectors.tolist());
    }


}

新增和删除路由逻辑类

package gateway.server.hss.service.impl;

import lombok.extern.log4j.log4j2;
import org.springframework.cloud.gateway.route.routedefinition;
import org.springframework.cloud.gateway.route.routedefinitionrepository;
import org.springframework.cloud.gateway.support.notfoundexception;
import org.springframework.stereotype.service;
import reactor.core.publisher.flux;
import reactor.core.publisher.mono;

import java.util.collection;
import java.util.collections;
import java.util.linkedhashmap;
import java.util.map;

/**
 * @author administrator
 * @create 2022-12-07
 * @desc 自定义内存路由管理仓,开启日志打印
 **/
@service
@log4j2
public class diyroutedefinitionrepository implements routedefinitionrepository {

    public  final map<string, routedefinition> routes = collections.synchronizedmap(new linkedhashmap<>());

    @override
    public flux<routedefinition> getroutedefinitions() {
        collection<routedefinition> values = routes.values();
        return flux.fromiterable(values);
    }

    @override
    public mono<void> save(mono<routedefinition> route) {
        return route.flatmap( r -> {
            log.info("新增路由信息:{}",r);
            routes.put(r.getid(), r);
            return mono.empty();
        });
    }

    @override
    public mono<void> delete(mono<string> routeid) {
        return routeid.flatmap(id -> {
            log.info("删除路由信息,路由id为:{}",id);
            if (routes.containskey(id)) {
                routes.remove(id);
                return mono.empty();
            }
            return mono.defer(() -> mono.error(new notfoundexception("routedefinition not found: "+routeid)));
        });
    }


}

数据结构


{
      "name":"通信路由",
      "routeid": "communications-server",
      "uri": "lb://communications-server",
      "order": 1,
      "predicates": [{
              "name": "path",
              "args": {
                    "_genkey_0": "/api/hss/classify/**",
                    "_genkey_1": "/api/hss/type/**",
                    "_genkey_2": "/api/hss/strategy/**",
                    "_genkey_3": "/api/hss/protocol/**",
                    "_genkey_4": "/api/sys/script/**"
                         }
                          }],
      "remark":"测试自定义路由信息",
      "filters": [{
              "name": "stripprefix",
              "args": {
                 "_genkey_0": "1"
                      }
                  }]
 }

predicates(断言) 和filters(过滤)新增配置说明


predicates, name和args固定key不可更改,首字母大写 ,args参数对象,_genkey_*代表其中一个参数,固定格式

name含义示例
path指定路径匹配{“name”:“path”,“args”:{“pattern”:“/aa/“,“pattern1”:”/bb/”}}
cookie配置对cookie中值的匹配,第一个为key,第二个为value{“name”:“cookie”,“args”:{“_genkey_0”:“chocolate”,“_genkey_1”:“ch.p”}}
header匹配http请求中设置的内容{“name”:“header”,“args”:{“_genkey_0”:“x-request-id”,“_genkey_1”:“\d+”}}
host匹配http请求host,匹配所有host为**.tecloman.cn的请求{“name”:“host”,“args”:{“_genkey_0”:“**.somehost.com”}}
method匹配http请求头{“name”:“method”,“args”:{“_genkey_0”:“get”}}
query匹配http请求中的查询参数,请求中携带{“name”:“query”,“args”:{“_genkey_0”:“param1”,“_genkey_1”:“value”}}
remoteaddr匹配请求中的remoteaddr{“name”:“remoteaddr”,“args”:{“_genkey_0”:“192.168.1.1/24”}}
after设置时间之后可以访问{“name”:“after”,“args”:{“_genkey_0”:“2017-01-20t17:42:47.789-07:00[america/denver]”}}
before设置时间之前可以访问{“name”:“before”,“args”:{“_genkey_0”:“2017-01-20t17:42:47.789-07:00[america/denver]”}}
between设置时间段内可以访问{“name”:“between”,“args”:{“_genkey_0”:“2017-01-20t17:42:47.789-07:00[america/denver]”,“_genkey_1”:“2017-01-21t17:42:47.789-07:00[america/denver]”}}
weight两组以上路由可以配置权重路由{“name”:“weight”,“args”:{“_genkey_0”:“service1”,“_genkey_1”:“80”}}

filters name 属性

name含义示例
rewritepath路径重写{“name”:“rewritepath”,“args”:{“_genkey_0”:“/foo/(?.*)”,“_genkey_1”:“/${segment}”}}
addrequestheader#### 修改请求头{“name”:“addrequestheader”,“args”:{“_genkey_0”:“x-request-foo”,“_genkey_1”:“bar”}}
addrequestparameter修改请求参数{“name”:“addrequestparameter”,“args”:{“_genkey_0”:“foo”,“_genkey_1”:“bar”}}
addresponseheader修改响应参数{“name”:“addresponseheader”,“args”:{“_genkey_0”:“x-request-foo”,“_genkey_1”:“bar”}}
prefixpath路径前缀增强{“name”:“prefixpath”,“args”:{“_genkey_0”:“/mypath”}}
stripprefix路径前缀删除{“name”:“stripprefix”,“args”:{“_genkey_0”:“2”}}
preservehostheader请求携带保留原始host{“name”:“preservehostheader”,“args”:{}}
redirectto重定向{“name”:“redirectto”,“args”:{“_genkey_0”:“302”,“_genkey_1”:“http://acme.org”}}
hystrix断路器{“name”:“hystrix”,“args”:{“name”:“fallbackcmd”,“fallbackuri”:“forward:/incaseoffailureusethis”}}
requestratelimiter集成redis原生支持请求限流{“name”:“requestratelimiter”,“args”:{“redis-rate-limiter.replenishrate”:“10”,“redis-rate-limiter.burstcapacity”:“20”}}
removerequestheader删除请求头属性{“name”:“removerequestheader”,“args”:{“_genkey_0”:“x-request-foo”}}
removeresponseheader删除响应头属性{“name”:“removeresponseheader”,“args”:{“_genkey_0”:“x-request-foo”}}
rewriteresponseheader重写响应头{“name”:“rewriteresponseheader”,“args”:{“_genkey_0”:“x-response-foo”,“_genkey_1”:“password=[^&]+”,“_genkey_2”:“password=***”}}
setpath重设请求路径{“name”:“setpath”,“args”:{“_genkey_0”:“/{segment}”}}
setresponseheader设置响应头{“name”:“setresponseheader”,“args”:{“_genkey_0”:“x-response-foo”,“_genkey_1”:“bar”}}
setstatus设置http状态{“name”:“setstatus”,“args”:{“_genkey_0”:“302”}}
requestsize设置文件传输大小{“name”:“requestsize”,“args”:{“_genkey_0”:“5000000”}}
retry失败重试{“name”:“retry”,“args”:{“_genkey_0”:3,“_genkey_1”:“bad_gateway”}}

相关接口


列表getlocalhost:8090/gateway/route/list
刷新getlocalhost:8090/gateway/route/flush
新增postlocalhost:8090/gateway/route/save
修改postlocalhost:8090/gateway/route/update
删除postlocalhost:8090/gateway/route/delete/{routeid}
修改状态postlocalhost:8090/gateway/route/upstate/{routeid}
删除传入id为路由的id,修改状态传入为主键id
网关端点localhost:8090/root/gateway/
查看正在运行的路由信息localhost:8090/root/gateway/routes
监控端点localhost:8090/root/

全局过滤器

经过网关路由转发 才能走到全局过滤器

package gateway.server.filter;

import com.alibaba.fastjson.json;
import com.alibaba.fastjson.serializer.serializerfeature;
import gateway.server.util.r;
import lombok.extern.log4j.log4j2;
import org.springframework.beans.factory.annotation.value;
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.http.httpheaders;
import org.springframework.http.mediatype;
import org.springframework.http.server.reactive.serverhttprequest;
import org.springframework.http.server.reactive.serverhttpresponse;
import org.springframework.stereotype.component;
import org.springframework.web.server.serverwebexchange;
import reactor.core.publisher.flux;
import reactor.core.publisher.mono;

import java.nio.charset.standardcharsets;


/**
 * @author administrator
 * @create 2022-12-07 17:19
 * @desc 全局过滤器
 */
@component
public class authglobalfilter implements globalfilter, ordered {
    @value("${custom.version}")
    string version;
    @value("${custom.enable}")
    boolean enable;

    @override
    public mono<void> filter(serverwebexchange exchange, gatewayfilterchain chain) {
        if (!enable) return chain.filter(exchange);
        // 获取前端传入的version
        serverhttprequest request = exchange.getrequest();
        string versionnum = request.getheaders().getfirst("version");
        if (version.equals(versionnum)) {
            //当前放行,交由下个过滤器过滤
            return chain.filter(exchange);
        } else {
            string path = request.geturi().getpath();
            if ("/api/sys/login".equals(path)) {
                return chain.filter(exchange);
            }
            // 版本不一致,返版本回前端 强制刷新
            byte[] bytes = json.tojsonstring(r.error(426, "error").put("version", version), serializerfeature.writemapnullvalue)
                    .getbytes(standardcharsets.utf_8);
            serverhttpresponse response = exchange.getresponse();
            response.getheaders().add(httpheaders.content_type, mediatype.application_json_value);
            databuffer buffer = response.bufferfactory().wrap(bytes);
            //响应出去
            return response.writewith(flux.just(buffer));
        }
    }

    @override
    public int getorder() {
        return -1;
    }
}

局部过滤器

处理某个服务转发 配置文件中需要指定

    gateway:
      routes:
        - id: web_route
          uri: lb://web-server
          predicates:
            - path=/api/captcha.jpg/**,/api/ruralgrid/**
          filters:
            - addresponseheader=name,tecloman #添加响应头
            - local #局部过滤器的前缀 localgatewayfilter  只取local就行

注册局部过滤器,全局的不需要

package gateway.server.filter;

import gateway.server.filter.localgatewayfilter;
import org.springframework.cloud.gateway.filter.gatewayfilter;
import org.springframework.cloud.gateway.filter.factory.abstractgatewayfilterfactory;
import org.springframework.stereotype.component;

/**
 * @author administrator
 */
@component
public class localgatewayfilterfactory extends abstractgatewayfilterfactory<object> {
    // 注册局部过滤器
    @override
    public gatewayfilter apply(object config) {
        return new localgatewayfilter();
    }
}
package gateway.server.filter;

import com.alibaba.fastjson.json;
import com.alibaba.fastjson.serializer.serializerfeature;
import gateway.server.util.r;
import gateway.server.util.springutil;
import gateway.server.hss.dao.sysusertokendao;
import gateway.server.hss.entity.sysusertokenentity;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.cloud.gateway.filter.gatewayfilter;
import org.springframework.cloud.gateway.filter.gatewayfilterchain;
import org.springframework.core.ordered;
import org.springframework.core.io.buffer.databuffer;
import org.springframework.http.httpheaders;
import org.springframework.http.mediatype;
import org.springframework.http.server.reactive.serverhttpresponse;
import org.springframework.stereotype.component;
import org.springframework.web.server.serverwebexchange;
import reactor.core.publisher.flux;
import reactor.core.publisher.mono;

import java.nio.charset.standardcharsets;

/**
 * @author chens
 * @create 2022-12-07 17:19
 * @desc 局部过滤器
 * 主要处理gateway本服务的接口和端点,其它服务绕过
 **/
@component
public class localgatewayfilter implements gatewayfilter, ordered {
    //private static sysusertokendao sysusertokendao = springutil.getbean(sysusertokendao.class);
    @autowired
    private sysusertokendao sysusertokendao;

    // 多个过滤器,决定顺序
    @override
    public int getorder() {
        return -1;
    }
    // 超级管理员才允许操作gateway相关接口 ,需要路由转发才生效
    @override
    public mono<void> filter(serverwebexchange exchange, gatewayfilterchain chain) {
        httpheaders headers = exchange.getrequest().getheaders();
        string token = headers.getfirst("token");
        sysusertokenentity entity = sysusertokendao.selectbyid(1);
        string msg = "无权限查看此接口";
        if (entity != null) {
            if (entity.gettoken().equals(token)) {
                if (entity.getexpiretime() > system.currenttimemillis() / 1000) {
                    return chain.filter(exchange);
                } else {
                    msg = "token失效,请重新登录";
                }
            }
        }
        byte[] bytes = json.tojsonstring(r.error(401, msg), serializerfeature.writemapnullvalue)
                .getbytes(standardcharsets.utf_8);
        serverhttpresponse response = exchange.getresponse();
        response.getheaders().add(httpheaders.content_type, mediatype.application_json_value);
        databuffer buffer = response.bufferfactory().wrap(bytes);
        //响应出去
        return response.writewith(flux.just(buffer));

    }
}

全局异常处理

不能像spring boot那样类上打个@restcontrolleradvice注解使用

package gateway.server.config;

import com.alibaba.fastjson.json;
import com.alibaba.fastjson.serializer.serializerfeature;
import gateway.server.util.r;
import lombok.extern.slf4j.slf4j;
import org.springframework.core.nestedexceptionutils;
import org.springframework.core.annotation.order;
import org.springframework.core.io.buffer.databuffer;
import org.springframework.http.httpheaders;
import org.springframework.http.httpstatus;
import org.springframework.http.mediatype;
import org.springframework.http.server.reactive.serverhttprequest;
import org.springframework.http.server.reactive.serverhttpresponse;
import org.springframework.lang.nullable;
import org.springframework.stereotype.component;
import org.springframework.util.stringutils;
import org.springframework.web.server.responsestatusexception;
import org.springframework.web.server.serverwebexchange;
import org.springframework.web.server.webexceptionhandler;
import reactor.core.publisher.flux;
import reactor.core.publisher.mono;

import java.nio.charset.standardcharsets;
import java.util.collections;
import java.util.hashset;
import java.util.set;


/**
 * @author administrator
 * gateway全局异常处理
 */
@slf4j
@order(-1)
@component
public class customwebexceptionhandler implements webexceptionhandler {

    private static final set<string> disconnected_client_exceptions;

    //排除部份系统级的异常
    static {
        set<string> exceptions = new hashset<>();
        exceptions.add("abortedexception");
        exceptions.add("clientabortexception");
        exceptions.add("eofexception");
        exceptions.add("eofexception");
        disconnected_client_exceptions = collections.unmodifiableset(exceptions);
    }

    @override
    public mono<void> handle(serverwebexchange exchange, throwable ex) {
        if (exchange.getresponse().iscommitted() || isdisconnectedclienterror(ex)) {
            return mono.error(ex);
        }
        serverhttprequest request = exchange.getrequest();
        string rawquery = request.geturi().getrawquery();
        string query = stringutils.hastext(rawquery) ? "?" + rawquery : "";
        string path = request.getpath() + query;
        string message;
        httpstatus status = determinestatus(ex);
        if (status == null) {
            status = httpstatus.internal_server_error;
        }
        // 通过状态码自定义异常信息
        if (status.value() >= 400 && status.value() < 500) {
            message = "路由服务不可达或禁止访问!";
        } else {
            message = "路由服务异常!" + ex.getmessage();
        }
        message += " path:" + path;
        //工具类输出json字符串
        byte[] bytes = json.tojsonstring(r.error(status.value(), message), serializerfeature.writemapnullvalue)
                .getbytes(standardcharsets.utf_8);
        serverhttpresponse response = exchange.getresponse();
        response.getheaders().add(httpheaders.content_type, mediatype.application_json_value);
        databuffer buffer = response.bufferfactory().wrap(bytes);
        //响应出去
        return response.writewith(flux.just(buffer));
    }

    @nullable
    protected httpstatus determinestatus(throwable ex) {
        if (ex instanceof responsestatusexception) {
            return ((responsestatusexception) ex).getstatus();
        }
        return null;
    }

    private boolean isdisconnectedclienterror(throwable ex) {
        return disconnected_client_exceptions.contains(ex.getclass().getsimplename())
                || isdisconnectedclienterrormessage(nestedexceptionutils.getmostspecificcause(ex).getmessage());
    }

    private boolean isdisconnectedclienterrormessage(string message) {
        message = (message != null) ? message.tolowercase() : "";
        return (message.contains("broken pipe") || message.contains("connection reset by peer"));
    }
}

如果是在gateway本服务写的接口还需要添加跨域配置

package gateway.server.config;

import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.http.httpheaders;
import org.springframework.http.httpmethod;
import org.springframework.http.httpstatus;
import org.springframework.http.server.reactive.serverhttprequest;
import org.springframework.http.server.reactive.serverhttpresponse;
import org.springframework.web.cors.reactive.corsutils;


import org.springframework.web.server.serverwebexchange;
import org.springframework.web.server.webfilter;
import org.springframework.web.server.webfilterchain;
import reactor.core.publisher.mono;

/**
 * 本地接口的跨域配置
 */
@configuration
public class corsconfig{
    @bean
    public webfilter corsfilter() {
        return (serverwebexchange ctx, webfilterchain chain) -> {
            serverhttprequest request = ctx.getrequest();
            if (corsutils.iscorsrequest(request)) {
                httpheaders requestheaders = request.getheaders();
                serverhttpresponse response = ctx.getresponse();
                httpmethod requestmethod = requestheaders.getaccesscontrolrequestmethod();
                httpheaders headers = response.getheaders();
                headers.add(httpheaders.access_control_allow_origin, requestheaders.getorigin());
                headers.addall(httpheaders.access_control_allow_headers,
                        requestheaders.getaccesscontrolrequestheaders());
                if (requestmethod != null) {
                    headers.add(httpheaders.access_control_allow_methods, requestmethod.name());
                }
                headers.add(httpheaders.access_control_allow_credentials, "true");
                headers.add(httpheaders.access_control_expose_headers, "*");
                if (request.getmethod() == httpmethod.options) {
                    response.setstatuscode(httpstatus.ok);
                    return mono.empty();
                }
            }
            return chain.filter(ctx);
        };
    }
}

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com