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

如何扩展NanoHttpd实现以处理NOTIFY/SUBSCRIBE等自定义HTTP方法

解决NanoHttpd支持NOTIFY/SUBSCRIBE自定义HTTP方法的问题

我之前在基于NanoHttpd开发插件时也碰到过一模一样的问题,你的报错原因很明确:NanoHttpd在解析请求的阶段会校验HTTP方法是否属于它内置的Method枚举,而NOTIFY、SUBSCRIBE不在默认枚举列表里,所以请求还没走到你的serve()方法就被拦截返回BAD REQUEST了。而且你写的notify()方法根本不会被调用,因为NanoHttpd只会自动调用与内置Method枚举对应的方法(比如get()对应GET、post()对应POST)。

下面是我亲测有效的完整解决方案:

步骤1:自定义HTTPSession绕过方法校验

我们需要重写NanoHttpd的请求解析逻辑,让它允许自定义方法。创建一个继承自HTTPSession的自定义类,修改方法解析逻辑:

import fi.iki.elonen.NanoHTTPD;
import fi.iki.elonen.NanoHTTPD.HTTPSession;
import fi.iki.elonen.NanoHTTPD.Method;
import java.lang.reflect.Field;
import java.net.Socket;

public class CustomHttpSession extends HTTPSession {
    private String actualRequestMethod;

    public CustomHttpSession(Socket socket, int timeout) {
        super(socket, timeout);
    }

    @Override
    protected boolean parseRequest() {
        try {
            // 读取请求行的第一行
            String requestLine = readLine();
            if (requestLine == null) {
                return false;
            }

            // 拆分请求行:方法、URI、协议
            String[] requestParts = requestLine.split(" ", 3);
            if (requestParts.length != 3) {
                return false;
            }

            // 保存真实的请求方法(包括NOTIFY/SUBSCRIBE)
            actualRequestMethod = requestParts[0];

            // 尝试解析为内置Method,失败则用GET作为占位符(避免父类校验失败)
            Method parsedMethod;
            try {
                parsedMethod = Method.valueOf(actualRequestMethod);
            } catch (IllegalArgumentException e) {
                parsedMethod = Method.GET;
            }

            // 通过反射修改父类的private字段method,绕过校验
            Field methodField = HTTPSession.class.getDeclaredField("method");
            methodField.setAccessible(true);
            methodField.set(this, parsedMethod);

            // 继续完成URI和协议的设置
            setUri(requestParts[1]);
            setProtocol(requestParts[2]);

            // 解析请求头和请求体,沿用父类逻辑
            return parseHeaders() && parseBody();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    // 对外暴露真实的请求方法
    public String getActualRequestMethod() {
        return actualRequestMethod;
    }
}

步骤2:重写NanoHttpd的createSession方法

在你的NanoHttpd子类中,重写createSession方法,返回我们自定义的会话类:

@Override
protected HTTPSession createSession(Socket socket) {
    return new CustomHttpSession(socket, getTimeout());
}

步骤3:在serve方法中处理自定义方法

现在请求能正常走到你的serve()方法了,你需要在这里获取真实的请求方法,然后分发处理(或者传给Cordova前端):

修改你的serve()方法,先获取真实方法:

@Override public Response serve(IHTTPSession session) {
    Log.d(this.getClass().getName(), "New request is incoming!");
    String requestUUID = UUID.randomUUID().toString();
    PluginResult pluginResult = null;

    // 获取真实的请求方法(兼容自定义方法)
    String requestMethod = session instanceof CustomHttpSession 
        ? ((CustomHttpSession) session).getActualRequestMethod() 
        : session.getMethod().name();

    try {
        // 在createJSONRequest中把requestMethod也加入到JSON里,让前端知道是NOTIFY/SUBSCRIBE请求
        pluginResult = new PluginResult(
            PluginResult.Status.OK, this.createJSONRequest(requestUUID, session, requestMethod));
    } catch (JSONException e) {
        e.printStackTrace();
    }

    pluginResult.setKeepCallback(true);
    this.webserver.onRequestCallbackContext.sendPluginResult(pluginResult);

    // 等待前端响应的逻辑保持不变
    while (!this.webserver.responses.containsKey(requestUUID)) {
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    JSONObject responseObject = (JSONObject) this.webserver.responses.get(requestUUID);
    Log.d(this.getClass().getName(), "responseObject: " + responseObject.toString());
    Response response = null;
    try {
        response = newFixedLengthResponse(
            Response.Status.lookup(responseObject.getInt("status")),
            getContentType(responseObject),
            responseObject.getString("body")
        );
        Iterator<?> keys = responseObject.getJSONObject("headers").keys();
        while (keys.hasNext()) {
            String key = (String) keys.next();
            response.addHeader(
                key, responseObject.getJSONObject("headers").getString(key)
            );
        }
    } catch (JSONException e) {
        e.printStackTrace();
    }
    return response;
}

同时,修改你的createJSONRequest方法,把requestMethod作为字段加入到返回的JSON中,这样Cordova前端就能识别并处理NOTIFY/SUBSCRIBE请求了。

额外说明

  • 如果不需要把请求传给前端处理,而是直接在Java层处理NOTIFY/SUBSCRIBE,那可以在serve()方法里直接判断:
    if ("NOTIFY".equalsIgnoreCase(requestMethod)) {
        return handleNotify(session);
    } else if ("SUBSCRIBE".equalsIgnoreCase(requestMethod)) {
        return handleSubscribe(session);
    }
    
    然后实现handleNotify和handleSubscribe方法即可。
  • 反射修改父类字段的方式是因为NanoHttpd的method字段是private的,如果你的NanoHttpd版本有公开的方法设置method,那可以不用反射。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:46:17