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

为什么Java Servlet中PUT方法抛出NotFoundException仍返回500错误

问题描述

我正在Java Servlet应用中处理异常,当客户端发送携带不存在id的PUT请求时,期望返回404错误,但实际返回500状态码。相同逻辑的GET方法可以正常返回404,说明异常处理机制本身是生效的,仅PUT方法存在问题。

相关代码

doPut 方法实现

@Override
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    cors(response);
    String requestBody = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
    HumanBeingDTO humanBeingDTO = gson.fromJson(requestBody, HumanBeingDTO.class);
    HumanBeing humanBeingToUpdate = humanBeingMapper.mapHumanBeingDTOToHumanBeing(humanBeingDTO);
    entityValidator.validateHumanBeing(humanBeingToUpdate);
    entityValidator.validateCoordinates(humanBeingToUpdate.getCoordinates());
    entityValidator.validateCar(humanBeingToUpdate.getCar());
    String pathInfo = request.getPathInfo();
    String id = null;
    if (pathInfo != null)
        id = pathInfo.substring(1);

    if (id != null) {
        try {
            long intId = Long.parseLong(id);
            repository.findById((int) intId);
            humanBeingToUpdate.setId(Long.parseLong(id));
            repository.update(humanBeingToUpdate);
        } catch (NumberFormatException e) {
            throw new BadRequestException("id格式错误:" + id + ",应为正整数(1,2,...)");
        } catch (NoResultException e) {
            throw new NotFoundException("id为" + id + "的HumanBeing不存在");
        }
    }
}

404异常处理类

@WebServlet(name = "NotFoundExceptionHandler", value = "/notFoundExceptionHandler")
public class NotFoundExceptionHandler extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.setStatus(404);
        Throwable error = (Throwable) req.getAttribute("javax.servlet.error.exception");
        resp.getWriter().println(error.getMessage());
    }
}

已捕获到的异常日志

exceptions.NotFoundException: No HumanBeing with id 80
    at servlets.HumanBeingServlet.doPut(HumanBeingServlet.java:117)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)

正常返回404的doGet方法

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    cors(response);
    String perPage = UrlParametersUtil.getField(request, "pageSize");
    String curPage = UrlParametersUtil.getField(request, "pageNumber");
    String sortBy = UrlParametersUtil.getField(request, "orderBy");
    String filterBy = UrlParametersUtil.getField(request, "filterBy");

    String pathInfo = request.getPathInfo();
    String id = null;
    if (pathInfo != null)
        id = pathInfo.substring(1);

    if (id != null) {
        String finalId = id;
        try {
            int intId = Integer.parseInt(id);
            if (intId <= 0) throw new BadRequestException("id格式错误:" + id + ",应为正整数(1,2,...)");
            HumanBeing humanBeing = (repository.findById(intId)).orElseThrow(() -> new NotFoundException("id为" + finalId + "的HumanBeing不存在"));
            HumanBeingDTO humanBeingDTO = humanBeingMapper.mapHumanBeingToHumanBeingDTO(humanBeing);
            response.getWriter().write(gson.toJson(humanBeingDTO));
            return;
        }catch (NumberFormatException e){
            throw new BadRequestException("id格式错误:" + id + ",应为正整数(1,2,...)");
        }catch (NoResultException e){
            throw new NotFoundException("id为" + id + "的HumanBeing不存在");
        }
    }

    PagedHumanBeingList pagedHumanBeingList = repository.findAll(perPage, curPage, sortBy, filterBy);
    HumanBeingDTOList dto = new HumanBeingDTOList((humanBeingMapper.mapHumanBeingListToHumanBeingDTOList(pagedHumanBeingList.getHumanBeingList())), pagedHumanBeingList.getCount());
    response.getWriter().write(gson.toJson(dto));
    repository.clearEntityManager();
}
解决方案

这个问题绝大多数是事务回滚导致的容器判定请求处理失败,优先级最高的排查方向:

  1. 确认你的NotFoundException是否继承自RuntimeException,如果你的项目使用JTA/EJB/Spring事务管理,默认抛出未声明的RuntimeException会标记事务为回滚状态,容器会将回滚的写请求判定为处理失败,强制返回500,即便你配置了异常映射。
    • 修复方法:在自定义的NotFoundException类上添加@ApplicationException(rollback = false)注解(JTA环境),或者在Spring的@Transactional注解中添加noRollbackFor = NotFoundException.class属性,让抛出该异常时事务不标记为回滚。
  2. 如果没有使用事务管理,排查以下常见问题:
    • 确认你的cors方法没有提前获取response.getWriter()或者flush响应缓冲区,缓冲区一旦提交,容器无法重置响应状态码,会直接返回500。
    • 确认异常处理Servlet没有被方法拦截规则限制:你重写的是service方法所以不会限制HTTP方法,但若配置了全局过滤器或者安全约束拦截PUT请求,也会导致异常处理器无法正常执行。
    • 临时验证方案:在doPut的catch块中不抛出异常,直接调用response.sendError(404, "id为" + id + "的HumanBeing不存在"),如果可以正常返回404,说明是异常映射配置的问题,检查web.xml中<error-page>的exception-type是否完全匹配你自定义的NotFoundException全类名。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 06:36:02