1. 简介
NanoHTTPD 是一个轻量级的开源 Java Web 服务器实现。它简单、嵌入式,适合用于小型项目、本地服务、测试环境或 IoT 场景。
本教程中,我们将使用 NanoHTTPD 创建几个 REST 接口,探索其核心功能。
2. 项目初始化
首先,在 pom.xml
中添加 NanoHTTPD 核心依赖:
<dependency>
<groupId>org.nanohttpd</groupId>
<artifactId>nanohttpd</artifactId>
<version>2.3.1</version>
</dependency>
然后创建一个简单的 HTTP 服务:
public class App extends NanoHTTPD {
public App() throws IOException {
super(8080);
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
}
public static void main(String[] args) throws IOException {
new App();
}
@Override
public Response serve(IHTTPSession session) {
return newFixedLengthResponse("Hello world");
}
}
上面的代码启动了一个监听 8080 端口的 HTTP 服务,访问 http://localhost:8080/
将返回 Hello world
。
使用 cURL 验证:
curl 'http://localhost:8080/'
Hello world
3. REST API 支持
NanoHTTPD 支持 GET、POST、PUT、DELETE 等常见 HTTP 方法。
3.1. GET 请求处理
要处理 GET 请求,我们可以通过 session.getMethod()
判断请求类型,并从 session.getParameters()
获取参数:
@Override
public Response serve(IHTTPSession session) {
if (session.getMethod() == Method.GET) {
String itemId = session.getParameters().get("itemId").get(0);
return newFixedLengthResponse("Requested itemId = " + itemId);
}
return newFixedLengthResponse(Response.Status.NOT_FOUND, MIME_PLAINTEXT,
"The requested resource does not exist");
}
测试:
curl 'http://localhost:8080/?itemId=23Bk8'
Requested itemId = 23Bk8
3.2. POST 请求处理
处理 POST 请求需要调用 session.parseBody()
来解析请求体:
@Override
public Response serve(IHTTPSession session) {
if (session.getMethod() == Method.POST) {
try {
session.parseBody(new HashMap<>());
String body = session.getQueryParameterString();
return newFixedLengthResponse("Request body = " + body);
} catch (IOException | ResponseException e) {
// handle exception
}
}
return newFixedLengthResponse(Response.Status.NOT_FOUND, MIME_PLAINTEXT,
"The requested resource does not exist");
}
测试:
curl -X POST -d 'deliveryAddress=Washington nr 4&quantity=5' 'http://localhost:8080/'
Request body = deliveryAddress=Washington nr 4&quantity=5
其他方法如 PUT、DELETE 类似,此处省略。
4. 跨域请求支持 (CORS)
通过在响应中添加 Access-Control-Allow-Origin
头即可支持跨域请求:
@Override
public Response serve(IHTTPSession session) {
Response response = newFixedLengthResponse("Hello world");
response.addHeader("Access-Control-Allow-Origin", "*");
return response;
}
验证:
curl -v 'http://localhost:8080'
...
Access-Control-Allow-Origin: *
...
5. 文件上传
NanoHTTPD 提供了独立的文件上传模块。添加依赖:
<dependency>
<groupId>org.nanohttpd</groupId>
<artifactId>nanohttpd-apache-fileupload</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
然后使用 NanoFileUpload
解析上传文件:
@Override
public Response serve(IHTTPSession session) {
try {
List<FileItem> files = new NanoFileUpload(new DiskFileItemFactory()).parseRequest(session);
int uploadedCount = 0;
for (FileItem file : files) {
try {
String fileName = file.getName();
byte[] content = file.get();
Files.write(Paths.get(fileName), content);
uploadedCount++;
} catch (Exception e) {
// handle
}
}
return newFixedLengthResponse(Response.Status.OK, MIME_PLAINTEXT,
"Uploaded files " + uploadedCount + " out of " + files.size());
} catch (IOException | FileUploadException e) {
throw new IllegalArgumentException("Could not handle files from API request", e);
}
}
测试:
curl -F 'filename=@/pathToFile.txt' 'http://localhost:8080'
Uploaded files: 1
6. 多路由支持
NanoHTTPD 提供了 RouterNanoHTTPD
类用于支持多个路由。添加依赖:
<dependency>
<groupId>org.nanohttpd</groupId>
<artifactId>nanohttpd-nanolets</artifactId>
<version>2.3.1</version>
</dependency>
创建主类并注册路由:
public class MultipleRoutesExample extends RouterNanoHTTPD {
public MultipleRoutesExample() throws IOException {
super(8080);
addMappings();
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
}
@Override
public void addMappings() {
addRoute("/", IndexHandler.class);
}
public static void main(String[] args) throws IOException {
new MultipleRoutesExample();
}
}
定义 UserHandler
示例:
public static class UserHandler extends DefaultHandler {
@Override
public String getText() {
return "UserA, UserB, UserC";
}
@Override
public String getMimeType() {
return MIME_PLAINTEXT;
}
@Override
public Response.IStatus getStatus() {
return Response.Status.OK;
}
}
绑定路由:
addRoute("/users", UserHandler.class);
测试:
curl -X POST 'http://localhost:8080/users'
UserA, UserB, UserC
动态路由示例:
public static class StoreHandler extends GeneralHandler {
@Override
public Response get(UriResource uriResource, Map<String, String> urlParams, IHTTPSession session) {
return newFixedLengthResponse("Retrieving store for id = " + urlParams.get("storeId"));
}
}
绑定:
addRoute("/stores/:storeId", StoreHandler.class);
测试:
curl 'http://localhost:8080/stores/123'
Retrieving store for id = 123
7. HTTPS 支持
要启用 HTTPS,你需要一个证书。可以使用 Let’s Encrypt 或生成自签名证书:
keytool -genkey -keyalg RSA -alias selfsigned \
-keystore keystore.jks -storepass password -validity 360 \
-keysize 2048 -ext SAN=DNS:localhost,IP:127.0.0.1 -validity 9999
将 keystore.jks
放入资源目录(如 src/main/resources
),并在代码中加载:
public class HttpsExample extends NanoHTTPD {
public HttpsExample() throws IOException {
super(8080);
makeSecure(NanoHTTPD.makeSSLSocketFactory("/keystore.jks", "password".toCharArray()), null);
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
}
@Override
public Response serve(IHTTPSession session) {
return newFixedLengthResponse("HTTPS call is a success");
}
public static void main(String[] args) throws IOException {
new HttpsExample();
}
}
测试:
curl --insecure 'https://localhost:8080'
HTTPS call is a success
⚠️ 注意:--insecure
是因为使用了自签名证书。
8. WebSocket 支持
NanoHTTPD 同样支持 WebSocket。添加依赖:
<dependency>
<groupId>org.nanohttpd</groupId>
<artifactId>nanohttpd-websocket</artifactId>
<version>2.3.1</version>
</dependency>
创建 WebSocket 服务端:
public class WsdExample extends NanoWSD {
public WsdExample() throws IOException {
super(8080);
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
}
@Override
protected WebSocket openWebSocket(IHTTPSession ihttpSession) {
return new WsdSocket(ihttpSession);
}
private static class WsdSocket extends WebSocket {
public WsdSocket(IHTTPSession handshakeRequest) {
super(handshakeRequest);
}
@Override
protected void onMessage(WebSocketFrame frame) {
try {
send(frame.getTextPayload() + " to you");
} catch (IOException e) {
// handle
}
}
}
public static void main(String[] args) throws IOException {
new WsdExample();
}
}
测试使用 wscat:
wscat -c localhost:8080
hello
hello to you
bye
bye to you
9. 总结
NanoHTTPD 是一个轻量、灵活的嵌入式 Java Web 服务器库,适用于本地服务、测试、原型开发等场景。
✅ 优点:
- 简洁 API
- 无需外部容器
- 支持 HTTPS 和 WebSocket
- 易于扩展
❌ 缺点:
- 不适合高并发、复杂业务场景
- 缺乏 Spring 级别的 MVC 支持
适合场景:
- 内部服务
- 嵌入式设备
- 快速原型
- 本地调试工具
完整代码示例可参考:GitHub 示例仓库