为什么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(); }
解决方案
这个问题绝大多数是事务回滚导致的容器判定请求处理失败,优先级最高的排查方向:
- 确认你的
NotFoundException是否继承自RuntimeException,如果你的项目使用JTA/EJB/Spring事务管理,默认抛出未声明的RuntimeException会标记事务为回滚状态,容器会将回滚的写请求判定为处理失败,强制返回500,即便你配置了异常映射。- 修复方法:在自定义的
NotFoundException类上添加@ApplicationException(rollback = false)注解(JTA环境),或者在Spring的@Transactional注解中添加noRollbackFor = NotFoundException.class属性,让抛出该异常时事务不标记为回滚。
- 修复方法:在自定义的
- 如果没有使用事务管理,排查以下常见问题:
- 确认你的
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
相关产品推荐
相关产品推荐

