You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tomcat过滤器在浏览器/curl的OPTIONS请求中未触发,Postman正常

Alright, let's break down why your ApiOriginFilter isn't triggering for OPTIONS requests from browsers and curl, but works fine with Postman, plus how to fix it.

Core Root Cause

The issue boils down to Tomcat's default handling of OPTIONS requests and how it differs between browser/curl and Postman:

  1. Tomcat's automatic OPTIONS response bypasses your filter
    When Tomcat gets an OPTIONS request, it checks if the target Servlet (your Jersey API Servlet) explicitly supports the OPTIONS method. If it doesn't (which is the default for Jersey unless configured otherwise), Tomcat generates a default OPTIONS response right at the container level—before the request even reaches your application's filter chain. That's why you don't see your Test log in catalina.out for browser/curl requests: the filter never gets touched.

  2. Postman's request behavior avoids Tomcat's auto-handling
    Even when you copy browser headers to Postman, Postman doesn't trigger the same container-level check that browsers/curl do. Either Postman's request format makes Tomcat route it through your application stack, or your Jersey setup implicitly handles OPTIONS for Postman's requests. Either way, the request hits your filter as expected.

Fixes to Try

Here are three reliable ways to resolve this:

1. Configure Jersey to explicitly handle OPTIONS requests

Force your Jersey Servlet to take over OPTIONS processing so Tomcat doesn't intercept it. For a Swagger-generated Jersey project:

  • Option A (Simpler): Use Jersey's built-in CorsFilter
    Register Jersey's CorsFilter in your ResourceConfig class instead of your custom filter:

    package io.swagger.api;
    
    import org.glassfish.jersey.server.ResourceConfig;
    import org.glassfish.jersey.server.filter.CorsFilter;
    
    @javax.annotation.Generated(...)
    public class ApplicationConfig extends ResourceConfig {
        public ApplicationConfig() {
            register(CorsFilter.class);
            // Add other registrations (like your API resources)
            packages("io.swagger.api");
        }
    }
    

    You can customize the CORS headers via init params if needed.

  • Option B: Add OPTIONS handlers to your resources
    If you prefer to keep your custom filter, add an OPTIONS method to each API resource to tell Tomcat Jersey handles it:

    @Path("/users")
    public class UsersApi {
        // ... your existing POST/GET methods ...
    
        @OPTIONS
        public Response handleOptions() {
            return Response.ok()
                .header("Access-Control-Allow-Origin", "*")
                .header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
                .header("Access-Control-Allow-Headers", "Content-Type")
                .build();
        }
    }
    

    Note: This requires updating every resource class, which is tedious for large APIs.

2. Add a global CORS filter at the Tomcat level

Bypass your application's filter chain entirely by configuring Tomcat's built-in CorsFilter globally. Edit Tomcat's conf/web.xml and add:

<filter>
    <filter-name>CorsFilter</filter-name>
    <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
    <init-param>
        <param-name>cors.allowed.origins</param-name>
        <param-value>*</param-value>
    </init-param>
    <init-param>
        <param-name>cors.allowed.methods</param-name>
        <param-value>GET,POST,DELETE,PUT,OPTIONS</param-value>
    </init-param>
    <init-param>
        <param-name>cors.allowed.headers</param-name>
        <param-value>Content-Type</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CorsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

This ensures all OPTIONS requests get CORS headers without modifying your application code.

3. Tweak your custom filter to handle OPTIONS upfront

Adjust your filter and its web.xml mapping to force Tomcat to route OPTIONS requests through it:
First, update your filter's doFilter method to handle OPTIONS immediately (before calling chain.doFilter):

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletResponse res = (HttpServletResponse) response;
    HttpServletRequest req = (HttpServletRequest) request;

    // Add CORS headers first
    res.addHeader("Access-Control-Allow-Origin", "*");
    res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
    res.addHeader("Access-Control-Allow-Headers", "Content-Type");

    // Handle OPTIONS requests directly to avoid Tomcat's auto-response
    if ("OPTIONS".equalsIgnoreCase(req.getMethod())) {
        res.setStatus(HttpServletResponse.SC_OK);
        System.out.println("Test - OPTIONS request handled");
        return;
    }

    chain.doFilter(request, response);
    System.out.println("Test - non-OPTIONS request handled");
}

Then, update your web.xml filter mapping to explicitly include OPTIONS dispatchers:

<filter-mapping>
    <filter-name>ApiOriginFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>OPTIONS</dispatcher>
</filter-mapping>

This tells Tomcat to send OPTIONS requests through your filter instead of handling them itself.

How to Verify

After applying any fix, test with curl:

curl -i -X OPTIONS http://10.0.29.1:8080/swagger-jaxrs-server.1.0.0/v1/users

You should see Access-Control-Allow-Origin: * in the response headers, and your Test log should appear in catalina.out.

内容的提问来源于stack exchange,提问作者Tasos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 08:28:14