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

Tomcat环境下JavaEE Servlets的CORS跨域POST请求问题排查

解决Servlet POST请求的CORS预检错误

问题场景

已安装Access Control插件但无效果,使用Servlets结合Gson开发接口。通过Hoppscotch调用所有HTTP方法均正常,Axios发起的GET请求也能成功,但Vue项目中用Axios发送POST请求时触发CORS错误:

Access to XMLHttpRequest at 'http://localhost:8080/demo5_war_exploded/client'
from origin 'http://localhost:8081' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

同时伴随请求失败错误:

xhr.js:233
POST http://localhost:8080/demo5_war_exploded/client net::ERR_FAILED

已在doGet和doPost方法中设置CORS响应头,但POST请求仍无法解决问题,相关代码如下:

异常的doPost代码

protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws IOException {

    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Methods", "POST");
    response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
    response.setHeader("Access-Control-Max-Age", "86400");
    response.setHeader("Content-Type", "application/json");

    JsonReader jsonReader = Json.createReader(request.getReader());
    JsonObject jsonObject = jsonReader.readObject();
    jsonReader.close();

    // extract the fields from the JSON object
    String name = jsonObject.getString("name");
    String email = jsonObject.getString("email");

    // create the Client object
    Client client = new Client(name, email);

    // insert the client into the database
    try {
        clientDao.insert(client);
        response.setStatus(HttpServletResponse.SC_CREATED);
    } catch (SQLException e) {
        e.printStackTrace();
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    }

}

正常工作的doGet代码

protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException {

    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Methods", "GET");
    response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
    response.setHeader("Access-Control-Max-Age", "86400");
    response.setHeader("Content-Type", "application/json");

    String idString = request.getParameter("id");
    if (idString != null) {
        // get the client with the specified id
        int id = Integer.parseInt(idString);
        try {
            Client client = clientDao.buscarPorId(id);
            if (client != null) {
                JsonObjectBuilder builder = Json.createObjectBuilder();
                builder.add("name", client.getName());
                builder.add("email", client.getEmail());
                JsonObject jsonObject = builder.build();
                response.setContentType("application/json");
                PrintWriter out = response.getWriter();
                out.print(jsonObject);
                out.flush();
            } else {
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            }
        } catch (SQLException e) {
            e.printStackTrace();
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    } else {
        // get all the client
        try {
            // a different way to get the json and simpler TBH
            List<Client> client = clientDao.list();

            Gson gson = new Gson();
            String clientJSON = gson.toJson(client);

            PrintWriter printWriter = response.getWriter();
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
            printWriter.write(clientJSON);
            printWriter.close();

        } catch (SQLException e) {
            e.printStackTrace();
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }
}

问题根源

核心问题在于浏览器的预检请求(OPTIONS)未被处理:
当前端发送带Content-Type: application/json的POST请求时,浏览器会自动先发送OPTIONS请求,验证服务器是否允许该跨域操作。你的Servlet没有实现OPTIONS方法的处理逻辑,导致服务器返回的响应中缺失CORS头,浏览器因此拦截了后续的正式POST请求。而GET请求属于简单请求,不需要触发预检,所以能正常工作。


解决方案

方案1:在Servlet中添加OPTIONS方法处理

直接在当前Servlet中重写doOptions方法,返回符合要求的CORS响应头:

@Override
protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws IOException {
    // 设置跨域允许规则
    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
    response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
    response.setHeader("Access-Control-Max-Age", "86400");
    // 预检请求无需返回实体内容,直接返回200状态码
    response.setStatus(HttpServletResponse.SC_OK);
}

方案2:使用Filter统一处理CORS(推荐)

如果存在多个需要跨域的Servlet,使用Filter统一拦截所有请求并处理CORS,避免重复代码:

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter("/*") // 拦截所有请求路径
public class CORSFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        // 设置跨域规则
        httpResponse.setHeader("Access-Control-Allow-Origin", "*");
        httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
        httpResponse.setHeader("Access-Control-Max-Age", "86400");
        
        // 处理OPTIONS预检请求,直接返回200,无需执行后续逻辑
        if ("OPTIONS".equalsIgnoreCase(((javax.servlet.http.HttpServletRequest) request).getMethod())) {
            httpResponse.setStatus(HttpServletResponse.SC_OK);
            return;
        }
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {}
}

注:如果项目支持Servlet 3.0+,@WebFilter注解会自动注册Filter,无需手动在web.xml中配置。


注意事项

  • 浏览器插件仅适用于本地开发测试,生产环境必须通过服务器端配置CORS规则。
  • 生产环境中Access-Control-Allow-Origin建议指定具体的前端域名(如http://localhost:8081),而不是使用通配符*,提升安全性。

内容的提问来源于stack exchange,提问作者Raúl Peñate

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 02:38:12