Java EE中HttpServlet doPut发送PUT请求抛出异常求助
问题根因
你遇到的Servlet.service() for servlet [VariablesCalculator] in context with path [] threw exception是Servlet容器抛出的通用异常,本质是请求处理链路中出现了未捕获的异常,常见触发原因分两类:
- 客户端侧:请求构造不合法,包括URI格式不符合要求、请求实体未指定编码/Content-Type、传入的变量名不符合「单个小写拉丁字符」的规则
- 服务端侧:Servlet路径映射配置错误、路径参数提取逻辑有bug、请求体读取失败、业务逻辑未做异常捕获,导致异常直接抛到Servlet容器层
可行解决方案
客户端请求修复(以Apache HttpClient 4.x为例)
你之前的代码缺失了请求初始化、编码配置等关键步骤,参考如下可运行的代码构造请求:
import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import java.nio.charset.StandardCharsets; public class CalcClient { public static void main(String[] args) throws Exception { // 变量名严格遵守规则:单个a-z范围的小写拉丁字符 String varName = "x"; // 要设置的变量值 String varValue = "1024"; // 拼接符合规则的全路径请求URI,替换为实际的服务地址和端口 HttpPut httpPut = new HttpPut(String.format("http://localhost:8080/calc/%s", varName)); // 构造请求实体,必须显式指定UTF-8编码,避免乱码 StringEntity entity = new StringEntity(varValue, StandardCharsets.UTF_8); // 纯文本值设置对应Content-Type,和服务端读取逻辑匹配 entity.setContentType("text/plain; charset=utf-8"); httpPut.setEntity(entity); // 执行请求并处理响应 try (CloseableHttpClient httpclient = HttpClients.createDefault()) { HttpResponse response = httpclient.execute(httpPut); int statusCode = response.getStatusLine().getStatusCode(); String respContent = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); System.out.println("响应状态码:" + statusCode); System.out.println("响应内容:" + respContent); } } }
客户端注意事项:
- 变量名必须严格校验,禁止传入多字符、大写字母、数字或特殊符号
- 禁止使用无参的StringEntity构造方法,必须显式指定编码,否则会使用默认ISO-8859-1编码,服务端解析会乱码报错
- Content-Type要和服务端的解析逻辑匹配,如果传JSON格式值就设置为
application/json; charset=utf-8 - 实际调用时URI要写全路径,不要写相对路径
服务端doPut逻辑修复
参考如下代码修复Servlet配置和处理逻辑,避免未捕获异常抛出:
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.regex.Pattern; // 配置Servlet映射,匹配所有/calc/开头的请求 @WebServlet(urlPatterns = "/calc/*") public class VariablesCalculator extends HttpServlet { // 校验变量名规则:路径最后一段必须是单个小写拉丁字母 private static final Pattern VALID_VAR_PATTERN = Pattern.compile("^/([a-z])$"); @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { // 提取路径参数 String pathSeg = req.getPathInfo(); // 校验变量名合法性 if (pathSeg == null || !VALID_VAR_PATTERN.matcher(pathSeg).matches()) { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "变量名非法,必须为单个小写拉丁字符"); return; } String varName = VALID_VAR_PATTERN.matcher(pathSeg).group(1); // 读取请求体中的变量值 StringBuilder valueBuilder = new StringBuilder(); try (BufferedReader reader = req.getReader()) { String line; while ((line = reader.readLine()) != null) { valueBuilder.append(line); } } String varValue = valueBuilder.toString().trim(); // 执行业务逻辑,示例为存入全局上下文,替换为你自己的存储逻辑即可 getServletContext().setAttribute(varName, varValue); // 返回成功响应 resp.setStatus(HttpServletResponse.SC_OK); resp.setContentType("text/plain; charset=utf-8"); resp.getWriter().write("变量设置成功," + varName + " = " + varValue); } catch (Exception e) { // 捕获所有业务异常,避免抛到容器层触发通用错误 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "请求处理失败:" + e.getMessage()); } } }
服务端注意事项:
- 如果使用web.xml配置Servlet,需确保
<url-pattern>配置为/calc/*,否则请求无法匹配到对应Servlet - 不要在读取请求体之前调用
getParameter()、getParameterMap()等方法,会导致请求体流被提前消费,后续无法读取 - 所有业务逻辑必须包裹在异常捕获中,不要把数据库操作、参数解析等环节的异常直接抛出
- 如果请求体是JSON格式,替换对应的Content-Type配置,使用JSON工具解析请求体即可
内容的提问来源于stack exchange,提问作者Kazikwow
相关产品推荐
相关产品推荐

