请求异常如何过滤拦截

wen 开源项目 26

本文目录导读:

请求异常如何过滤拦截

  1. 前端层面(最常用)
  2. 后端层面(网关/中间件)
  3. 请求前端/后端链路拦截模型
  4. 最佳实践总结

针对“请求异常”的过滤拦截,通常需要根据你所处的技术栈(前端/后端)以及异常的类型(网络错误、业务错误、认证失败等)来设计,以下从前端后端两个维度,以及客户端(Axios/Fetch)网关/中间件层面,给出具体的拦截方案。


前端层面(最常用)

前端拦截可以避免无效请求发出,或对异常进行统一处理(如弹窗、跳转登录)。

Axios 拦截器(推荐)

Axios 是最常用的 HTTP 客户端,其拦截器可以统一处理请求和响应异常。

// 创建 Axios 实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 10000,
});
// 请求拦截器
service.interceptors.request.use(
  (config) => {
    // 1. 过滤无效请求:如缺少必要参数
    if (!config.url || config.url.includes('undefined')) {
      return Promise.reject(new Error('请求地址无效'));
    }
    // 2. 检查 Token 是否存在
    const token = getToken();
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`;
    } else {
      // 如果没有Token且需要登录,直接拦截并跳转
      console.warn('Token 不存在,请求被拦截');
      return Promise.reject(new Error('未登录'));
    }
    return config;
  },
  (error) => Promise.reject(error)
);
// 响应拦截器
service.interceptors.response.use(
  (response) => {
    // 1. 对状态码进行过滤
    if (response.status !== 200) {
      // 非200视为异常
      return Promise.reject(new Error(`后端返回状态码: ${response.status}`));
    }
    // 2. 业务状态码过滤(自定义code)
    const res = response.data;
    if (res.code !== 0) {
      // 业务异常,统一提示
      Message.error(res.message || '业务请求异常');
      // 如果是登录过期 (如 code=401)
      if (res.code === 401) {
        logoutAndRedirect();
      }
      return Promise.reject(new Error(res.message));
    }
    return res;
  },
  (error) => {
    // 网络异常、超时、500等 HTTP 错误
    if (error.code === 'ECONNABORTED') {
      Message.error('请求超时');
    } else if (error.response) {
      const status = error.response.status;
      switch (status) {
        case 401:
          Message.error('未授权,请登录');
          logoutAndRedirect();
          break;
        case 403:
          Message.error('拒绝访问');
          break;
        case 404:
          Message.error('接口不存在');
          break;
        case 500:
          Message.error('服务器内部错误');
          break;
        default:
          Message.error('请求失败');
      }
    } else {
      Message.error('网络连接异常');
    }
    return Promise.reject(error);
  }
);
export default service;

Fetch 封装拦截

原生 Fetch 没有拦截器,但可以通过封装函数实现类似效果:

async function myFetch(url, options = {}) {
  // 请求前过滤
  if (!url || url.startsWith('//')) {
    throw new Error('无效请求 URL');
  }
  const token = getToken();
  if (!token && options.needAuth) {
    throw new Error('未登录');
  }
  const config = {
    headers: { 'Content-Type': 'application/json', ...options.headers },
    ...options,
  };
  if (token) config.headers['Authorization'] = `Bearer ${token}`;
  // 发起请求
  let response;
  try {
    response = await fetch(url, config);
  } catch (e) {
    throw new Error('网络不通或请求被拦截');
  }
  // 响应过滤
  if (!response.ok) {
    if (response.status === 401) {
      logoutAndRedirect();
    }
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  const data = await response.json();
  if (data.code !== 0) {
    // 业务异常过滤
    throw new Error(data.message || '业务异常');
  }
  return data;
}

URL 过滤(前端路由守卫)

对于某些白名单外的 URL,可直接在 Vue/React Router 中拦截:

// Vue Router 导航守卫
router.beforeEach((to, from, next) => {
  // 过滤非业务域名请求(比如阻止直接调用外部API)
  if (to.path.startsWith('/api/external')) {
    // 记录日志或阻止
    console.warn('禁止访问外部API路径');
    next(false);
  } else {
    next();
  }
});

后端层面(网关/中间件)

后端是异常的最后一道防线,通常使用网关(如 Nginx、Kong、Spring Cloud Gateway)或中间件(如 Express、Flask、Spring Interceptor)进行过滤。

Nginx 网关过滤(性能最高)

Nginx 可拦截非法请求(如 SQL 注入、无效 Header、频率超限)直接返回 4xx/5xx。

# 1. 拦截不合法 URL(包含特殊字符)
if ($request_uri ~* "(\"|'|%27|%22|<|>|script|select|union)") {
    return 403;
}
# 2. IP 黑名单
deny 192.168.1.100;
# 3. 限流(异常高频请求)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
location /api/ {
    limit_req zone=api_limit burst=5 nodelay;
    proxy_pass http://backend;
}
# 4. 拦截特定 User-Agent(爬虫或恶意请求)
if ($http_user_agent ~* (Python-urllib|curl|wget) ) {
    return 403;
}

Node.js (Express/Koa) 中间件

// 请求异常过滤中间件
const requestFilter = (req, res, next) => {
  // 1. 检查请求体大小(防止超大请求)
  const contentLength = parseInt(req.headers['content-length'], 10);
  if (contentLength > 10 * 1024 * 1024) { // 大于10MB
    return res.status(413).json({ error: '请求体过大' });
  }
  // 2. 检查必须的 Header
  if (!req.headers['x-app-version']) {
    return res.status(400).json({ error: '缺少 App Version Header' });
  }
  // 3. 检查 Token 有效性
  const token = req.headers.authorization?.split(' ')[1];
  if (req.url.startsWith('/api/protected') && !token) {
    return res.status(401).json({ error: '未授权' });
  }
  // 4. 过滤 XSS 关键字
  const body = req.body?.toString() || '';
  if (/<script>|onerror=|javascript:/i.test(body)) {
    return res.status(403).json({ error: '包含非法字符' });
  }
  next();
};
// 全局异常处理
app.use((err, req, res, next) => {
  // 捕获中间件和业务异常
  console.error('Request Error:', err);
  res.status(err.status || 500).json({
    error: err.message || '服务器内部错误',
  });
});

Java Spring Boot 拦截器

@Component
public class RequestFilter implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, 
                              HttpServletResponse response, Object handler) 
                              throws Exception {
        // 1. 黑白名单
        String ip = request.getRemoteAddr();
        if (ip.equals("192.168.1.100")) {
            response.sendError(403, "IP被禁止");
            return false;
        }
        // 2. 参数校验(防注入)
        String queryString = request.getQueryString();
        if (queryString != null && queryString.matches(".*['\"<>&].*")) {
            response.sendError(400, "参数包含非法字符");
            return false;
        }
        // 3. Token 验证
        String token = request.getHeader("Authorization");
        if (request.getRequestURI().startsWith("/api/private") && 
            (token == null || !token.startsWith("Bearer "))) {
            response.sendError(401, "Token无效");
            return false;
        }
        return true;
    }
}

后端业务层异常过滤(Database/Service)

在业务代码中通过捕获特定异常进行过滤,防止脏数据进入核心逻辑:

// 例:Spring 全局异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<?> handleBadRequest(Exception e) {
        return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
    }
    @ExceptionHandler(AccessDeniedException.class)
    public ResponseEntity<?> handleAuthError() {
        return ResponseEntity.status(403).body(Map.of("error", "权限不足"));
    }
    @ExceptionHandler(DataIntegrityViolationException.class)
    public ResponseEntity<?> handleDBError() {
        return ResponseEntity.status(500).body(Map.of("error", "数据异常,已拦截"));
    }
}

请求前端/后端链路拦截模型

[客户端] 
   ↓ 请求拦截(Axios/自定义Fetch)
     ↓ URL白名单、Token检查、参数基础校验
        ↓ 若异常 → 返回Promise.reject或跳转
  [网络]
   ↓ 可能被 CDN/WAF 拦截
  [网关/反向代理](Nginx/Kong)
   ↓ 限流、IP黑名单、非法字符过滤、Header验证
     ↓ 若异常 → 直接返回 429/403/400
  [后端应用]
   ↓ 中间件/拦截器(Express/Spring Interceptor)
     ↓ Token解密、权限验证、参数深度校验
  [业务层]
   ↓ 数据校验异常捕获(ControllerAdvice)
  [数据库/外部服务]

最佳实践总结

拦截层次 常用工具/方法
客户端 Axios拦截器, Fetch封装, 前端路由守卫 无效URL、Token丢失、业务code异常、网络超时
网关 Nginx/OpenResty/Kong/WAF 恶意IP、高频请求、SQL/XSS注入、非标准HTTP方法
后端中间件 Express中间件、SpringInterceptor、Flask装饰器 Token解析失败、Header缺失、权限不足、请求体过大
业务代码 全局异常处理器、参数校验框架 参数格式错误、数据库约束违反、业务状态码异常

建议优先级

  1. 首先在客户端做基础过滤(避免浪费网络请求)
  2. 其次在网关做流量和安全性过滤(拦截绝大多数恶意请求)
  3. 最后在后端中间件做严谨的权限和参数验证(安全底线)

这样通过多层过滤,可以过滤掉 99% 的异常请求,同时让真正的异常被及时捕获并返回友好提示。

抱歉,评论功能暂时关闭!