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:
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 yourTestlog incatalina.outfor browser/curl requests: the filter never gets touched.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'sCorsFilterin yourResourceConfigclass 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

