ZuulFilter转发路由

本文探讨了在项目重构中,如何使用Zuul Filter解决自定义路由与服务接口不对应的问题。作者分享了如何通过创建一个CommonServicePathFilter来动态转发请求,确保URL路径与服务接口的一致性,避免因接口变更导致的404问题。

背景:项目重构,生产线上url需要按照通过网关转发给不同的service,之前用过自定义路由,在yml文件配置route的方式去做转发时,遇到一个问题:

zuul:
  routes: 
	 <自定义一个serviceid>:
 		path = /account/**  
		serviceId=account

上面的方式不能保证请求的URL在/account/后面的url路径与account服务里面的路径一致,从而这样会有一个问题。原系统中调用方中的C接口的URL不变,而新的服务提供方没有适配的URL,只有C1接口。此处提供一种解决方案:Filter转发。
说明图

1. 网关代码

@Component
public class CommonServicePathFilter extends ZuulFilter {

    private final static String GETWAY_FOWARD_PREFIX = "getway_forward_";

    private final static String GETWAY_COMPAY_CONFIG_KEY = "getway_company";

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public String filterType() {
       //这里重要,必须是route。过滤类型有四种:pre/route/post/error
        return "route";
    }

    @Override
    public int filterOrder() {
        return 1;
    }


    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        String url = ctx.getRequest().getRequestURI();
        Map<String,String> forwardMap =  getForwardMap(url);
        if(forwardMap != null){
            String fowardUrl = forwardMap.get(url);
            String serviceId = getServiceId(fowardUrl);
            String requestUrl = getRequestUrl(fowardUrl,serviceId);
            //1.设置目标service的Controller的路径
            ctx.put(FilterConstants.REQUEST_URI_KEY,requestUrl);
            //2.设置目标service的serviceId
            ctx.put(FilterConstants.SERVICE_ID_KEY,serviceId);
        }
        return null;
    }
    
    private String getServiceId(String url){
        if(url.startsWith("/")){
            String temp = url.substring(1);
            return temp.split("/")[0];
        }else{
            return null;
        }
    }

    private String getRequestUrl(String url,String serviceId){
        return url.substring(serviceId.length() +1);
    }

    @Override
    public boolean shouldFilter() {
       return true;
    }

    private Map<String,String> getForwardMap(String originalUrl){
    	//TODO:这里是返回一个map,传入一个originUrl,返回一个要转发的url
    }

}

2. 注意点

ctx.put(FilterConstants.REQUEST_URI_KEY,requestUrl);
但是我使用的时候,一直报404,后来跟踪服务之后,发现其实ctx里面还有一个serviceId的属性,它是跟当前请求的这个原始url映射的serviceId保持一致的。如果你要转发的serviceId并不是这个的话,那就会报404,所以必须要在这里重新定义serviceId,ctx.put(FilterConstants.SERVICE_ID_KEY,serviceId);

<think>我们正在讨论ZuulFilter的使用方法和实现示例。Zuul是Netflix开源的API网关服务,在Spring Cloud Netflix中用于路由和过滤请求。ZuulFilter是Zuul的核心组件,用于自定义过滤逻辑,如鉴权、日志记录、流量控制等。 ZuulFilter有四种类型: 1. pre:在请求被路由之前执行,常用于身份验证、日志记录等。 2. route:在请求路由时执行,用于构建发送给微服务的请求。 3. post:在请求被路由微服务之后执行,常用于添加响应头、收集统计信息等。 4. error:在其他阶段发生错误时执行。 实现一个ZuulFilter需要继承ZuulFilter类并实现以下方法: - `filterType()`: 返回过滤器的类型(pre, route, post, error)。 - `filterOrder()`: 返回过滤器的执行顺序,数值越小优先级越高。 - `shouldFilter()`: 判断该过滤器是否需要执行,返回true则执行,false不执行。 - `run()`: 过滤器的具体逻辑。 下面是一个实现示例:一个简单的Pre类型的过滤器,用于记录请求开始时间,并在请求头中添加一个自定义头。 ```java import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; public class PreLogFilter extends ZuulFilter { private static final Logger logger = LoggerFactory.getLogger(PreLogFilter.class); @Override public String filterType() { return "pre"; // 指定为pre类型 } @Override public int filterOrder() { return 1; // 执行顺序,数字越小优先级越高 } @Override public boolean shouldFilter() { return true; // 该过滤器是否执行,true执行,false不执行 } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); // 记录请求信息 logger.info("Request Method: {}, Request URL: {}", request.getMethod(), request.getRequestURL().toString()); // 添加一个自定义请求头 ctx.addZuulRequestHeader("X-Request-StartTime", String.valueOf(System.currentTimeMillis())); return null; } } ``` 为了让Spring容器管理这个过滤器,我们需要将其注册为Bean。可以在配置类中声明: ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FilterConfig { @Bean public PreLogFilter preLogFilter() { return new PreLogFilter(); } } ``` 这样,当请求经过Zuul时,就会执行这个过滤器。 **注意事项**: 1. 在`run()`方法中,我们可以通过`RequestContext`获取请求和响应对象,并对其进行操作。 2. 在`shouldFilter()`方法中,我们可以根据请求上下文中的信息决定是否执行该过滤器(例如,根据请求路径判断)。 3. 过滤器的执行顺序由`filterOrder()`返回的值决定,相同类型的过滤器按照该值从小到大执行。 **另一个示例:Post过滤器** 下面是一个Post过滤器的示例,用于记录请求处理时间: ```java import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; public class PostLogFilter extends ZuulFilter { private static final Logger logger = LoggerFactory.getLogger(PostLogFilter.class); @Override public String filterType() { return "post"; } @Override public int filterOrder() { return 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); // 获取请求开始时间(由Pre过滤器设置) String startTimeHeader = request.getHeader("X-Request-StartTime"); if (startTimeHeader != null) { long startTime = Long.parseLong(startTimeHeader); long duration = System.currentTimeMillis() - startTime; logger.info("Request URL: {} handled in {} ms", request.getRequestURL().toString(), duration); } return null; } } ``` 同样,需要在配置类中注册这个Bean。 **测试**: 启动Zuul网关和相关的微服务后,发送请求,观察控制台日志,可以看到Pre和Post过滤器的日志输出。 **总结**: ZuulFilter的使用步骤: 1. 创建一个类继承ZuulFilter。 2. 实现四个抽象方法:filterType, filterOrder, shouldFilter, run。 3. 将过滤器注册为Spring Bean。 这样,我们就可以在Zuul网关中实现自定义的过滤逻辑了。 §§相关问题§§ 1. ZuulFilterfilterType有哪几种?分别代表什么含义? 2. 如何控制多个相同类型ZuulFilter的执行顺序? 3. 在ZuulFilter中如何获取请求参数和修改响应? 4. ZuulFilter中如何处理异常?</think>### ZuulFilter 使用方法与实现示例 Zuul 是 Spring Cloud 中的 API 网关组件,`ZuulFilter` 是其核心过滤机制,用于实现请求拦截、路由转发、安全验证等操作。以下是基于 Spring Cloud 官方文档的详细指南: --- ### 一、ZuulFilter 核心机制 1. **四种过滤器类型** - `pre`:请求路由前执行(如鉴权、日志记录) - `route`:请求路由时执行(如转发到具体服务) - `post`:请求路由后执行(如响应加工) - `error`:其他阶段出错时执行(如异常处理)[^1] 2. **执行顺序控制** 通过 `filterOrder()` 返回值控制优先级,值越小优先级越高(例如 `@Order(1)` 表示最高优先级)[^5]。 --- ### 二、实现步骤与代码示例 #### 1. 基础依赖(Maven) ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> ``` #### 2. 自定义过滤器示例 **场景:实现鉴权与日志记录(pre 类型)** ```java import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import javax.servlet.http.HttpServletRequest; public class AuthLogFilter extends ZuulFilter { // 指定过滤器类型 @Override public String filterType() { return "pre"; // 路由前执行 } // 优先级(值越小越先执行) @Override public int filterOrder() { return 1; // 最高优先级 } // 是否启用该过滤器 @Override public boolean shouldFilter() { return true; // 始终启用 } // 核心逻辑 @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); // 1. 记录请求日志 System.out.println("Request URL: " + request.getRequestURL()); // 2. 鉴权验证(示例:检查Token) String token = request.getHeader("Authorization"); if (token == null) { // 拦截请求并返回401 ctx.setSendZuulResponse(false); // 不转发请求 ctx.setResponseStatusCode(401); ctx.setResponseBody("Unauthorized: Missing token"); } return null; } } ``` #### 3. 注册过滤器 在 Spring Boot 主类或配置类中注册: ```java @EnableZuulProxy // 启用Zuul网关 @SpringBootApplication public class GatewayApplication { @Bean public AuthLogFilter authLogFilter() { return new AuthLogFilter(); } } ``` --- ### 三、关键应用场景 1. **安全认证(pre 类型)** - 验证 JWT/API Key - 拦截非法 IP[^1] 2. **动态路由(route 类型)** ```java @Override public String filterType() { return "route"; } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); // 根据请求路径转发到不同服务 if (ctx.getRequest().getRequestURI().contains("/api/user")) { ctx.setRouteHost(new URL("http://user-service:8080")); } return null; } ``` 3. **响应加工(post 类型)** - 添加统一响应头 - 数据脱敏处理 4. **异常处理(error 类型)** - 记录错误日志 - 返回标准化错误格式 --- ### 四、注意事项 1. **执行顺序冲突** 多个相同类型过滤器需通过 `filterOrder()` 明确优先级,避免逻辑混乱[^5]。 2. **性能影响** 复杂过滤逻辑(如数据库查询)可能成为性能瓶颈,建议异步处理。 3. **超时控制** 在 `route` 过滤器中设置超时时间: ```java ctx.set("ribbon.ReadTimeout", 5000); // 5秒超时 ``` 4. **调试技巧** 启用 Zuul 调试日志: ```properties logging.level.com.netflix.zuul=DEBUG ``` --- ### 五、测试验证 发送 HTTP 请求并观察: ```bash curl -H "Authorization: invalid_token" http://zuul-gateway/api/data ``` 预期输出: ```json { "status": 401, "message": "Unauthorized: Missing token" } ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值