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

Java REST API偶发未映射HTTP 100状态码问题排查求助

问题

我调用一个Java编写的REST API服务时,偶尔会出现未映射的HTTP状态码,定位不到问题。用Postman执行相同请求始终能得到预期状态码,但通过应用调用时偶尔失败。

相关代码

@Component
public class FessCalculator implements Processor {
    private static Map<String, Object> headers;
    private static int responseStatusCode = 200;

    @Override
    public void process(Exchange exchange) throws Exception {
        String message = (String) exchange.getIn().getHeader("exception.message");
        String supplierName = exchange.getProperty("supplierName",String.class);
        Map<String, companyFess> output = new HashMap<>();
        companyFess companyFess = new companyFess();
        if (StringUtils.isBlank(message)) {
            Map<String, Object> map = exchange.getIn().getBody(Map.class);
            String ebsResult = (String) map.get("x_msg_data");
            if (StringUtils.isBlank(ebsResult)) {
                companyFess.setFees((List<TaxLineOut>) map.get("lines_tab"));
                output.put(supplierName, companyFess);
            } else {
                /*  API executed with errors. Fees will be empty  */
                companyFess.setMessage(ebsResult);
                companyFess.setFees(new ArrayList<TaxLineOut>());
                output.put(supplierName, companyFess);
            }
        } else {
            /* API could not be executed. Fees will be null */
            companyFess.setMessage("API Could not\n be executed");
            companyFess.setFees(new ArrayList<TaxLineOut>());
            output.put(supplierName, companyFess);
        }
        companyFessResponse response = new companyFessResponse();
        if (output == null || output.isEmpty()) {
            responseStatusCode = 400;
        } else {
            response.setCompanyFees(output);
            for (companyFess companyFess1 : output.values()) {
                if (StringUtils.isNotBlank(companyFess1.getMessage())) {
                    response.setStatus(500);
                }
            }
        }
        exchange.getOut().setBody(response);
        headers = exchange.getIn().getHeaders();
        headers.put(Exchange.HTTP_RESPONSE_CODE, responseStatusCode);
        exchange.getOut().setHeaders(headers);
    }
}

调用方错误日志

8444-[2023-02-02 19:44:17] feesCalculator.CRITICAL: "feesCalculator API Exception: "{\n \"status\" : 200,\n \"companyTaxes\" : {\n \"Company\" : {\n \"message\" : \"\",\n \"taxes\" : [ {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"STATE\",\n \"tax_rate_id\" : 12500,\n \"tax_rate_code\" : \"STATE NGRP 13M\",\n \"tax_rate_name\" : \"VAT RAT BSAS\",\n \"operand\" : 1.1,\n \"amount\" : 11499.92\n }, {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"USCF\",\n \"tax_rate_id\" : 13779,\n \"tax_rate_code\" : \"GRP07_IIBB_CF\",\n \"tax_rate_name\" : \"VAT RAT USA Res 364 AGIP 1%\",\n \"operand\" : 1,\n \"amount\" : 10454.49\n }, {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"USMI\",\n \"tax_rate_id\" : 13881,\n \"tax_rate_code\" : \"USMI CONV\",\n \"tax_rate_name\" : \"VAT RAT USA\",\n \"operand\" : 3.31,\n \"amount\" : 0\n }, {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"USA-VAT\",\n \"tax_rate_id\" : 13710,\n \"tax_rate_code\" : \"USA-VAT CM 2,5\",\n \"tax_rate_name\" : \"VAT RAT Tucum\",\n \"operand\" : 2.5,\n \"amount\" : 501.81\n }, {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"USFO\",\n \"tax_rate_id\" : 12035,\n \"tax_rate_code\" : \"USFO CONV\",\n \"tax_rate_name\" : \"VAT RAT Formos\",\n \"operand\" : 1,\n \"amount\" : 0\n }, {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"VAT\",\n \"tax_rate_id\" : 11756,\n \"tax_rate_code\" : \"VAT 21% PP BA\",\n \"tax_rate_name\" : \"VAT 21%\",\n \"operand\" : 21,\n \"amount\" : 47326\n }, {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"USER\",\n \"tax_rate_id\" : 12200,\n \"tax_rate_code\" : \"USER 3%\",\n \"tax_rate_name\" : \"VAT Washington\",\n \"operand\" : 3,\n \"amount\" : 0\n }, {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"VAT\",\n \"tax_rate_id\" : 11765,\n \"tax_rate_code\" : \"VAT 21% RVTA\",\n \"tax_rate_name\" : \"VAT 21%\",\n \"operand\" : 21,\n \"amount\" : 172218.1\n } ]\n }\n }\n}"" [] []}

调用feesCalculator处理器计算费用时,偶尔会抛出HTTP 100状态码,但响应内容已正常加载。请问哪里出错了?为什么会出现这种情况?我预期只返回已映射的HTTP状态码。


分析与解决方案

问题核心是静态变量的线程安全问题:

  • responseStatusCode和headers是类级别的静态变量,而Spring容器中FessCalculator是单例Bean,多并发请求会共享这两个变量。当多个请求同时进入process方法时,前一个请求的状态码还没写完,后一个请求就可能覆盖它的值,导致最终返回的状态码混乱(比如出现的100状态码,就是其他请求遗留的异常值)。
  • Postman测试时请求量小,并发概率低,所以很难复现;应用调用时并发请求多,就会触发这个问题。

修改后的代码

将静态变量改为方法内的局部变量,让每个请求独立处理自己的状态码和头信息:

@Component
public class FessCalculator implements Processor {

    @Override
    public void process(Exchange exchange) throws Exception {
        String message = (String) exchange.getIn().getHeader("exception.message");
        String supplierName = exchange.getProperty("supplierName",String.class);
        Map<String, companyFess> output = new HashMap<>();
        companyFess companyFess = new companyFess();
        if (StringUtils.isBlank(message)) {
            Map<String, Object> map = exchange.getIn().getBody(Map.class);
            String ebsResult = (String) map.get("x_msg_data");
            if (StringUtils.isBlank(ebsResult)) {
                companyFess.setFees((List<TaxLineOut>) map.get("lines_tab"));
                output.put(supplierName, companyFess);
            } else {
                /*  API executed with errors. Fees will be empty  */
                companyFess.setMessage(ebsResult);
                companyFess.setFees(new ArrayList<TaxLineOut>());
                output.put(supplierName, companyFess);
            }
        } else {
            /* API could not be executed. Fees will be null */
            companyFess.setMessage("API Could not\n be executed");
            companyFess.setFees(new ArrayList<TaxLineOut>());
            output.put(supplierName, companyFess);
        }
        companyFessResponse response = new companyFessResponse();
        // 改为局部变量,默认值200
        int responseStatusCode = 200;
        if (output == null || output.isEmpty()) {
            responseStatusCode = 400;
        } else {
            response.setCompanyFees(output);
            for (companyFess companyFess1 : output.values()) {
                if (StringUtils.isNotBlank(companyFess1.getMessage())) {
                    response.setStatus(500);
                    // 同步设置HTTP响应码为500
                    responseStatusCode = 500;
                }
            }
        }
        exchange.getOut().setBody(response);
        // 复制头信息,避免修改原请求头集合
        Map<String, Object> headers = new HashMap<>(exchange.getIn().getHeaders());
        headers.put(Exchange.HTTP_RESPONSE_CODE, responseStatusCode);
        exchange.getOut().setHeaders(headers);
    }
}

额外说明

  • 原代码存在逻辑漏洞:当companyFess1.getMessage()不为空时,只设置了响应体的状态为500,但未同步修改HTTP响应码,导致响应体和HTTP状态码不一致,修改后的代码补上了这一点。
  • 用new HashMap<>(exchange.getIn().getHeaders())复制头信息,避免直接修改原请求的头集合,防止对后续流程产生意外影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 06:52:05