Servlet,当由浏览器直接调用servlet时,出现的The specified HTTP method is not allowed for the requested resource.

本文解释了当浏览器直接访问Servlet时,默认使用Get方法。若未重写doGet方法,将导致父类GenericServlet处理Http请求失败的问题。文章提供了解决方案:通过重写doGet方法来确保正确处理Http请求。

HttpServlet 是继承父类GenericServlet,当直接由浏览器转入Servlet时,默认的是Get方法,若未重写父类的doGet类,当你访问时,则子类自然交给父类,但父类不认识Http协议,自然会报出该错误。

解决方案:重写父类。

package com.taixin.dvbc2; import com.google.gson.Gson; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.net.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class LoadProgramServlet extends HttpServlet { private static final String MCAST_IP = "236.78.78.1"; private static final int PORT = 59000; private static final int MTU = 7 * 188; private static final Gson GSON = new Gson(); private static final Map<Integer, Integer> progToPmtPid = new HashMap<Integer, Integer>(); private static final Map<Integer, String> programNames = new HashMap<Integer, String>(); private static final Map<Integer, Integer> pidToProgram = new HashMap<Integer, Integer>(); private static final Map<Integer, Long> pidBytesCounter = new HashMap<Integer, Long>(); private static final long REPORT_INTERVAL_NS = 3000000000L; // 3秒 private static long lastReportNs = System.nanoTime(); private static final Map<Integer, SectionAssembler> assemblers = new HashMap<Integer, SectionAssembler>(); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // 获取参数 final String ip = request.getParameter("ip"); final String portStr = request.getParameter("port"); if (ip == null || portStr == null) { response.setStatus(400); response.getWriter().write("{\"error\":\"缺少参数 ip 或 port\"}"); return; } final int port = Integer.parseInt(portStr); // SSE 响应头 response.setContentType("text/event-stream;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Connection", "keep-alive"); final PrintWriter writer = response.getWriter(); new Thread(new Runnable() { @Override public void run() { try { processInputChannelSSE(ip, port, writer); } catch (Exception e) { e.printStackTrace(); } } }).start(); } private void processInputChannelSSE(String ip, int port, PrintWriter writer) throws Exception { MulticastSocket sock = new MulticastSocket(port); InetAddress group = InetAddress.getByName(ip); sock.joinGroup(group); byte[] buf = new byte[MTU]; DatagramPacket pkt = new DatagramPacket(buf, buf.length); lastReportNs = System.nanoTime(); while (true) { // 一直接收数据 sock.receive(pkt); int len = pkt.getLength(); int off = 0; while (off + 188 <= len) { parseTsPacket(buf, off); off += 188; } long now = System.nanoTime(); if (now - lastReportNs > REPORT_INTERVAL_NS) { String jsonResponse = buildProgramReportJson(); if (jsonResponse != null) { writer.write("data: " + jsonResponse + "\n\n"); writer.flush(); } lastReportNs = now; } } } private String buildProgramReportJson() { Map<Integer, Long> programBytes = new HashMap<Integer, Long>(); for (Map.Entry<Integer, Long> e : pidBytesCounter.entrySet()) { int pid = e.getKey(); long bytes = e.getValue(); Integer prog = pidToProgram.get(pid); if (prog != null) { Long oldVal = programBytes.containsKey(prog) ? programBytes.get(prog) : 0L; programBytes.put(prog, oldVal + bytes); } } pidBytesCounter.clear(); if (programBytes.isEmpty()) return null; List<Map<String, Object>> programsData = new ArrayList<Map<String, Object>>(); for (Map.Entry<Integer, Long> e : programBytes.entrySet()) { int program = e.getKey(); long bytes = e.getValue(); double bits = bytes * 8.0; double mbps = bits / 3.0 / 1000000.0; // 3秒间隔 String name = programNames.containsKey(program) ? programNames.get(program) : "(未知节目)"; Map<String, Object> programData = new HashMap<String, Object>(); programData.put("name", name); programData.put("no", program); programData.put("rate", String.format("%.3f Mbit/s", mbps)); programsData.add(programData); } return GSON.toJson(programsData); } // --- 这里保留你原来的 parseTsPacket / parsePAT / parseSDT / parsePMT / SectionAssembler 等方法 --- // (我不再重复全部代码,只保留修改重点) private static void parseTsPacket(byte[] buf, int off) { if ((buf[off] & 0xFF) != 0x47) return; // sync int tei = (buf[off + 1] & 0x80) >>> 7; int pusi = (buf[off + 1] & 0x40) >>> 6; int pid = ((buf[off + 1] & 0x1F) << 8) | (buf[off + 2] & 0xFF); int afc = (buf[off + 3] & 0x30) >>> 4; if (tei == 1) return; // 计数 if (pid != 0x1FFF) { Long old = pidBytesCounter.get(pid); pidBytesCounter.put(pid, (old == null ? 0L : old) + 188L); } int i = off + 4; if (afc == 2) return; if (afc == 3) { int afl = buf[i] & 0xFF; i += 1 + afl; } if (i >= off + 188) return; if (pid == 0x0000 || pid == 0x0011 || containsValue(progToPmtPid, pid)) { SectionAssembler sa = assemblers.get(pid); if (sa == null) { sa = new SectionAssembler(); assemblers.put(pid, sa); } sa.push(buf, i, off + 188 - i, pusi == 1, (buf[off + 3] & 0x0F)); while (sa.hasSection()) { byte[] sec = sa.pollSection(); parseSection(pid, sec); } } } private static boolean containsValue(Map<Integer, Integer> map, int val) { for (Integer v : map.values()) { if (v != null && v == val) return true; } return false; } private static void parseSection(int pid, byte[] sec) { if (sec.length < 3) return; int tableId = sec[0] & 0xFF; int sectionLength = ((sec[1] & 0x0F) << 8) | (sec[2] & 0xFF); if (sectionLength + 3 != sec.length) return; if (pid == 0x0000 && tableId == 0x00) { parsePAT(sec); } else if (pid == 0x0011 && (tableId == 0x42 || tableId == 0x46)) { parseSDT(sec); } else if (tableId == 0x02) { parsePMT(sec); } } private static void parsePAT(byte[] sec) { int pos = 8; int end = sec.length - 4; while (pos + 4 <= end) { int programNumber = ((sec[pos] & 0xFF) << 8) | (sec[pos + 1] & 0xFF); int pmtPid = ((sec[pos + 2] & 0x1F) << 8) | (sec[pos + 3] & 0xFF); pos += 4; if (programNumber == 0) continue; progToPmtPid.put(programNumber, pmtPid); pidToProgram.put(pmtPid, programNumber); } } private static void parsePMT(byte[] sec) { int programNumber = ((sec[3] & 0xFF) << 8) | (sec[4] & 0xFF); programNames.put(programNumber, "Program " + programNumber); } private static void parseSDT(byte[] sec) { int pos = 11; int end = sec.length - 4; while (pos + 5 <= end) { int serviceId = ((sec[pos] & 0xFF) << 8) | (sec[pos + 1] & 0xFF); int descriptorsLoopLen = ((sec[pos + 3] & 0x0F) << 8) | (sec[pos + 4] & 0xFF); int descPos = pos + 5; int descEnd = descPos + descriptorsLoopLen; String name = null; while (descPos + 2 <= descEnd && descEnd <= end) { int tag = sec[descPos] & 0xFF; int len = sec[descPos + 1] & 0xFF; if (descPos + 2 + len > descEnd) break; if (tag == 0x48 && len >= 3) { int serviceProviderNameLen = sec[descPos + 3] & 0xFF; int nameLenPos = descPos + 4 + serviceProviderNameLen; if (nameLenPos + 1 <= descPos + 2 + len) { int serviceNameLen = sec[nameLenPos] & 0xFF; int nameStart = nameLenPos + 1; int nameEnd = Math.min(nameStart + serviceNameLen, descPos + 2 + len); byte[] nameBytes = Arrays.copyOfRange(sec, nameStart, nameEnd); name = decodeDvbString(nameBytes); } } descPos += 2 + len; } if (name != null && !name.isEmpty()) { programNames.put(serviceId, name); } pos = descEnd; } } static String decodeDvbString(byte[] bs) { if (bs == null || bs.length == 0) return ""; int offset = 0; String charset = "GB18030"; int first = bs[0] & 0xFF; if (first == 0x10 && bs.length >= 3) { charset = "UTF-16BE"; offset = 1; } else if (first == 0x15 || first == 0x14) { charset = "GB2312"; offset = 1; } else if (first == 0x1F) { charset = "UTF-8"; offset = 1; } else if (first < 0x20) { offset = 1; } try { return new String(bs, offset, bs.length - offset, charset).trim(); } catch (Exception e) { return ""; } } static class SectionAssembler { private final ByteArrayOutputStream cur = new ByteArrayOutputStream(); private Integer expectedLen = null; private final Queue<byte[]> ready = new LinkedList<byte[]>(); void push(byte[] src, int off, int len, boolean payloadStart, int continuityCounter) { int i = off; if (payloadStart) { int pointer = src[i] & 0xFF; i += 1 + pointer; startNew(); } while (i < off + len) { if (expectedLen == null) { if (off + len - i < 3) { write(src, i, off + len - i); return; } int sl = ((src[i + 1] & 0x0F) << 8) | (src[i + 2] & 0xFF); expectedLen = sl + 3; } int remaining = expectedLen - cur.size(); int copy = Math.min(remaining, off + len - i); write(src, i, copy); i += copy; if (cur.size() == expectedLen) { ready.add(cur.toByteArray()); startNew(); } } } boolean hasSection() { return !ready.isEmpty(); } byte[] pollSection() { return ready.poll(); } private void startNew() { cur.reset(); expectedLen = null; } private void write(byte[] src, int off, int len) { cur.write(src, off, len); } } } document.getElementById('btnParse').addEventListener('click', () => { const selectedGroup = groups.find(g => g.selected); if (!selectedGroup) { alert("请先选择一个输入通道"); return; } const { ip, port, name, id } = selectedGroup; const es = new EventSource(`/loadProgramServlet?ip=${ip}&port=${port}&name=${name}&id=${id}`); es.onmessage = function (event) { try { const data = JSON.parse(event.data); console.log('收到节目列表:', data); selectedGroup.programs = data; renderTree(); } catch (e) { console.error("解析 SSE 数据失败", e); } }; es.onerror = function (err) { console.error("SSE 连接出错", err); }; }); 报错 AdminServlet:1101 加载节目数据失败 405 <html><head><title>Apache Tomcat/6.0.29 - Error report</title><style><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 405 - HTTP method POST is not supported by this URL</h1><HR size="1" noshade="noshade"><p><b>type</b> Status report</p><p><b>message</b> <u>HTTP method POST is not supported by this URL</u></p><p><b>description</b> <u>The specified HTTP method is not allowed for the requested resource (HTTP method POST is not supported by this URL).</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/6.0.29</h3></body></htm
09-06
<think>我们正在处理一个SAP相关的HTTP方法错误。错误信息是:"The specified HTTP method is not allowed for the resource identified by the Data Service Request URI"。 根据引用[1],这个错误表示对于数据服务请求URI所标识的资源,不允许使用指定的HTTP方法。 引用[2]提到了303和302状态码,以及当使用不兼容HTTP/1.1的客户端,可能需要使用302代替303。 但是,用户的问题是关于SAP中出现这个错误的解决方法。我们需要结合SAP环境来分析。 常见的HTTP方法有:GET, POST, PUT, DELETE, PATCH等。在SAP数据服务中,当我们通过OData服务或者其他Web服务进行数据交互,可能会遇到此类错误。 可能的原因及解决方法: 1. **检查HTTP方法是否正确**:确认你使用的HTTP方法(GET, POST等)是否符合服务端期望。例如,如果服务端只允许GET,而你使用了POST,则会出现此错误。 - 解决方法:查阅SAP服务的API文档,确认该资源允许的HTTP方法,并调整请求。 2. **检查SAP网关的配置**:在SAP系统中,SAP Gateway是用于暴露OData服务的关键组件。需要检查服务定义中是否允许该HTTP方法。 - 解决方法:在SAP Gateway中检查服务定义(SEGW事务码),确保在实现类中已经实现了对应HTTP方法(如GET_ENTITY, CREATE_ENTITY等)的相应方法。如果没有实现,则需要添加。 3. **检查SICF服务处理程序**:有,错误可能是由于SICF服务配置不正确导致的。 - 解决方法:使用事务码SICF,检查对应的服务节点,确保处理程序类正确,并且支持该HTTP方法。 4. **检查URL和资源路径**:确认请求的URI是否正确,是否存在拼写错误或者资源不存在的情况。 - 解决方法:仔细检查请求的URI,确保与API文档一致。 5. **检查服务元数据**:OData服务的元数据(metadata)定义了资源的结构和允许的操作。如果元数据中没有声明该HTTP方法,则请求会被拒绝。 - 解决方法:检查元数据(通常通过访问$metadata获取),确认该资源是否支持你使用的HTTP方法。 6. **使用正确的HTTP状态码重定向**:根据引用[2]的内容,当需要重定向,应该使用303(或302)状态码,并在Location字段中给出新的URI。但这通常是在服务端开发需要处理的,而不是客户端。 - 如果这个错误是由于服务端配置了重定向而导致的,那么需要检查服务端的重定向配置是否正确。 7. **检查用户代理兼容性**:引用[2]提到,对于不支持HTTP/1.1的用户代理(客户端),使用303状态码可能会出现问题。但这种情况在现代系统中较少见。 - 解决方法:确保客户端支持HTTP/1.1,或者服务端对旧客户端进行兼容处理(使用302)。 8. **检查SAP Note**:SAP可能会发布相关的Note来解决此类问题。 - 解决方法:在SAP支持门户上搜索相关的Note,看看是否有官方提供的补丁或解决方案。 9. **查看服务器日志**:在SAP系统中查看相关的应用日志和网关日志,可能会提供更详细的错误信息。 - 解决方法:使用事务码ST22查看ABAP dump,或者查看网关日志。 10. **网络跟踪**:在客户端和服务器端进行网络抓包,查看实际的HTTP请求和响应,以确定问题。 由于这是一个比较具体的错误,以上步骤需要根据实际情况逐一排查。 注意:在SAP中开发OData服务,通常会在SAP Gateway Service Builder(事务码SEGW)中定义服务,然后生成实现类。在实现类中,需要重写对应HTTP方法的方法。例如,对于GET请求,需要实现GET_ENTITY或GET_ENTITYSET方法。 如果以上方法都不能解决问题,可能需要联系SAP支持。 下面,我们将生成一些相关问题: </think>### 解决 SAP 中 HTTP 方法不被允许错误的方案 在 SAP 中出现 `The specified HTTP method is not allowed for the resource identified by the Data Service Request URI` 错误,通常表示客户端使用了服务端未配置的 HTTP 方法(如 GET、POST、PUT 等)访问 OData 服务。以下是系统化的解决步骤: --- ### 1. **验证 HTTP 方法类型** - 确认请求使用的 HTTP 方法(GET/POST/PUT/DELETE)是否与 SAP 服务定义一致。 - **检查点**: - 对于数据读取应使用 `GET`,数据创建应使用 `POST`,更新使用 `PUT/PATCH`。 - 示例:若服务仅支持 `GET`,但客户端发送了 `POST` 请求,则触发此错误。 --- ### 2. **检查 SAP Gateway 服务配置** - **事务码 `SEGW`**: 打开 Gateway Service Builder,检查目标服务的 HTTP 方法映射: 1. 定位服务项目 → 展开 `Service Implementation`。 2. 右键点击实体集 → 选择 `Map to Data Source`。 3. 在 `Operation` 选项卡中确认已实现所需方法(如 `Query` 对应 GET,`Create` 对应 POST)[^1]。 - **未实现的方法**:需通过 ABAP 代码补全缺失方法。 --- ### 3. **检查 SICF 处理程序配置** - **事务码 `SICF`**: 验证服务的处理程序是否支持目标 HTTP 方法: 1. 搜索服务节点(如 `/sap/opu/odata`)。 2. 双击节点 → 切换到 `Handler List` 选项卡。 3. 确认处理程序类(如 `CL_REST_HTTP_HANDLER`)已注册所有允许的方法(GET/POST/PUT 等)[^2]。 --- ### 4. **分析 OData 元数据** - 访问服务的元数据 URL(如 `https://<server>:<port>/sap/opu/odata/sap/<service_name>/$metadata`)。 - **验证点**: - 检查 `<EntitySet>` 元素的 `EntityType` 是否包含 `<NavigationProperty>`。 - 确认 `<EntityType>` 下定义了 `<Property>` 且无冲突约束。 --- ### 5. **重定向兼容性处理** - 若涉及 HTTP 重定向(如 302/303 状态码): - 确保响应头 `Location` 字段包含新 URI(引用[^2])。 - 对旧版客户端(不支持 HTTP/1.1),改用 302 状态码替代 303 以兼容[^2]。 - 示例 ABAP 重定向代码: ```abap DATA(lo_redirect) = cl_http_response=>create_from_http2( ). lo_redirect->set_status( code = 302 reason = 'Found' ). lo_redirect->set_header_field( name = 'Location' value = '<new_uri>' ). ``` --- ### 6. **调试与日志分析** - 启用 SAP Gateway 日志: 1. 事务码 `/IWFND/TRACES` → 激活跟踪。 2. 复现错误 → 下载日志分析。 - 检查点: - 定位 `METHOD_NOT_ALLOWED` 错误条目。 - 验证请求 URI 与 HTTP 方法是否匹配服务契约。 --- ### 7. **测试与验证** - 使用 **Postman** 或 **SAP Gateway Client**(事务码 `/IWFND/GW_CLIENT`): - 发送不同 HTTP 方法的请求,确认服务响应。 - 示例成功响应: ```http HTTP/1.1 200 OK Allow: GET, POST ``` --- ### 总结流程图 ```mermaid graph TD A[触发HTTP方法错误] --> B{验证HTTP方法类型} B -->|不匹配| C[修正客户端请求方法] B -->|匹配| D[检查SEGW服务配置] D --> E[补全缺失方法实现] E --> F[测试服务响应] D -->|配置正常| G[检查SICF处理程序] G --> H[注册允许的方法] H --> F F -->|成功| I[问题解决] F -->|失败| J[分析OData元数据] J --> K[修正实体集定义] ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值