一、背景
统计网关制定接口访问次数
二、实现代码
@Slf4j
public class GatewayFilter extends OncePerRequestFilter {
private final ApplyService applyService;
private final AsyncService asyncService;
public GatewayFilter(ApplyService applyService, AsyncService asyncService) {
this.applyService = applyService;
this.asyncService = asyncService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String appId = request.getHeader("appId");
String userId = request.getHeader("userId");
String accessUrl = request.getRequestURI();
log.info("网关接口headers,appId={},userId={},accessUrl={}", appId, userId, accessUrl);
if (StrUtil.isBlank(appId)) {
this.returnJson(response, ResultData.failed("网关接口缺少appId"));
}
Apply apply = applyService.selectByAppId(appId);
if (apply == null) {
this.returnJson(response, ResultData.failed("appId不合法"));
}
if (apply != null && apply.getEnable() == 0) {
this.returnJson(response, ResultData.failed("appId未启用"));
}
AccessLog accessLog = new AccessLog();
accessLog.setAppId(appId);
accessLog.setUserId(userId);
accessLog.setAccessUrl(accessUrl);
asyncService.saveGatewayLog(accessLog);
chain.doFilter(request, response);
}
private void returnJson(ServletResponse response, ResultData resultData) {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
try (OutputStream out = response.getOutputStream()) {
out.write(JSONUtil.toJsonStr(resultData).getBytes(StandardCharsets.UTF_8));
out.flush();
} catch (IOException e) {
log.error("response error:", e);
}
throw new BusinessException("appId异常");
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Resource
private ApplyService applyService;
@Resource
private AsyncService asyncService;
@Bean
public FilterRegistrationBean<GatewayFilter> registerGatewayFilter() {
FilterRegistrationBean<GatewayFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new GatewayFilter(applyService, asyncService));
registration.addUrlPatterns(
"/gateway/getListByIds",
"/gateway/getById"
);
registration.setName("gatewayFilter");
registration.setOrder(1);
return registration;
}
}
统计结果:
三、踩坑及解决办法
首先启动报错(我这里是用外部tomcat启动):
16-Aug-2021 11:53:41.756 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.StandardContext.startInternal 一个或多个筛选器启动失败。完整的详细信息将在相应的容器日志文件中找到
这个我看网上好多都把lib包导入,这里没用,看我开始的filter代码:
@Slf4j
public class GatewayFilter extends OncePerRequestFilter {
@Resource
private ApplyService applyService;
@Resource
private AsyncService asyncService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String appId = request.getHeader("appId");
String userId = request.getHeader("userId");
String accessUrl = request.getRequestURI();
log.info("网关接口headers,appId={},userId={},accessUrl={}", appId, userId, accessUrl);
if (StrUtil.isBlank(appId)) {
this.returnJson(response, ResultData.failed("网关接口缺少appId"));
}
Apply apply = applyService.selectByAppId(appId);
if (apply == null) {
this.returnJson(response, ResultData.failed("appId不合法"));
}
if (apply != null && apply.getEnable() == 0) {
this.returnJson(response, ResultData.failed("appId未启用"));
}
AccessLog accessLog = new AccessLog();
accessLog.setAppId(appId);
accessLog.setUserId(userId);
accessLog.setAccessUrl(accessUrl);
asyncService.saveGatewayLog(accessLog);
chain.doFilter(request, response);
}
private void returnJson(ServletResponse response, ResultData resultData) {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
try (OutputStream out = response.getOutputStream()) {
out.write(JSONUtil.toJsonStr(resultData).getBytes(StandardCharsets.UTF_8));
out.flush();
} catch (IOException e) {
log.error("response error:", e);
}
throw new BusinessException("appId异常");
}
}
然后我们看tomcat localhost log:
16-Aug-2021 11:53:41.756 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.StandardContext.filterStart 启动过滤器异常
javax.naming.NameNotFoundException: 名称[com.video.manager.component.GatewayFilter/applyService]未在此上下文中绑定。找不到[com.video.manager.component.GatewayFilter]。
很显然,是我们filter里不能注入bean,这是由于filter的优先级比较高,在web容器启动时就开始加载filter,此时我们写的bean都还没注入到容器,肯定是找不到的,所以后来改成 步骤二 里的实现。之前也遇到过这个问题,记当时记住了,时间长了就忘了,所以这次记录下。这里注入bean也有其他实现方式,有兴趣可以自己实现下。
其实实现网关接口管理也可以使用注解方式实现,不过个人觉得不适合,于是选择了filter,注解的话需要把每个需要统计的接口都要加上注解,不灵活,filter的话可以把统计的接口放到apollo上,动态更新,更灵活。