🧠 本文不只是教学代码,而是帮助你理解「Erlang 写 Web 服务」的底层原理,用最野性但真实的方式。
你可能已经习惯了 Java 的 Spring Boot、Node.js 的 Express、Python 的 Flask —— 但这些都有框架。而这次,我们直接用 Erlang 写 HTTP 服务,不依赖 cowboy、mochiweb 或 yaws,完全裸写 socket 层,硬核程度拉满。
👩💻 目标:
- 写一个能处理 GET 和 POST 请求的 Web 服务器
 - 路由分发基本 API 请求路径
 - 接收 JSON(模拟即可)
 - 返回标准 HTTP 响应
 
建立最原始的 TCP 服务
Erlang 的 gen_tcp 模块就是我们所有操作的起点:
start() ->
    {ok, ListenSocket} = gen_tcp:listen(8080, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]),
    loop_accept(ListenSocket).
loop_accept(ListenSocket) ->
    {ok, Socket} = gen_tcp:accept(ListenSocket),
    spawn(fun() -> handle(Socket) end),
    loop_accept(ListenSocket).
- 监听本地 8080 端口
 - 接收到连接后,spawn 一个进程专门处理这个 Socket(这就是 Erlang 并发威力!)
 
解析 HTTP 请求(简单模拟)
真正的 HTTP 协议超复杂,这里我们做「最小可运行子集」:
handle(Socket) ->
    {ok, Data} = gen_tcp:recv(Socket, 0),
    Request = binary_to_list(Data),
    io:format("Request: ~s~n", [Request]),
    Response = route(Request),
    gen_tcp:send(Socket, Response),
    gen_tcp:close(Socket).
现在的重点是 route/1 —— 我们来构造最基础的路由逻辑。
基于请求构建路由
route(Request) ->
    %% 基于最简单的方式分析请求头第一行
    case string:tokens(Request, " \r\n") of
        ["GET", "/", _] ->
            ok("Hello from Erlang REST API!");
        ["GET", "/ping", _] ->
            ok("pong");
        ["POST", "/echo", _ | _] ->
            ok("echo placeholder");
        _ ->
            not_found()
    end.
构造 HTTP 响应(手撸)
ok(Body) ->
    list_to_binary(
        "HTTP/1.1 200 OK\r\n" ++
        "Content-Type: text/plain\r\n" ++
        "Content-Length: " ++ integer_to_list(length(Body)) ++ "\r\n\r\n" ++
        Body
    ).
not_found() ->
    list_to_binary(
        "HTTP/1.1 404 Not Found\r\n" ++
        "Content-Length: 0\r\n\r\n"
    ).
完整代码
%%%-------------------------------------------------------------------
%%% @doc 超轻量级 HTTP 服务器,无框架,无依赖,纯 Erlang socket 实现
%%%-------------------------------------------------------------------
-module(simple_http_server).
-export([start/0]).
start() ->
    {ok, ListenSocket} = gen_tcp:listen(8080, [
        binary,
        {packet, 0},
        {active, false},
        {reuseaddr, true}
    ]),
    io:format("🚀 Server started at http://localhost:8080~n"),
    loop(ListenSocket).
loop(ListenSocket) ->
    {ok, Socket} = gen_tcp:accept(ListenSocket),
    spawn(fun() -> handle(Socket) end),
    loop(ListenSocket).
handle(Socket) ->
    case gen_tcp:recv(Socket, 0) of
        {ok, Data} ->
            Request = binary_to_list(Data),
            io:format("📥 Incoming Request:\n~s~n", [Request]),
            Response = dispatch(Request),
            gen_tcp:send(Socket, Response),
            gen_tcp:close(Socket);
        {error, closed} ->
            gen_tcp:close(Socket);
        {error, Reason} ->
            io:format("❌ Error: ~p~n", [Reason]),
            gen_tcp:close(Socket)
    end.
dispatch(Request) ->
    case string:tokens(Request, " \r\n") of
        ["GET", "/", _] ->
            ok("🌈 Welcome to Erlang REST API");
        ["GET", "/ping", _] ->
            ok("pong 🏓");
        ["POST", "/echo", _ | _] ->
            ok("🔁 Echo endpoint reached (body parsing TBD)");
        ["GET", Path, _] ->
            case is_static(Path) of
                true -> ok("[static] no content");
                false -> not_found()
            end;
        _ ->
            not_found()
    end.
is_static(Path) ->
    lists:any(fun(Ext) -> string:ends_with(Path, Ext) end, [".css", ".js", ".less", ".png", ".jpg"]).
ok(Body) ->
    list_to_binary(
        "HTTP/1.1 200 OK\r\n" ++
        "Content-Type: text/plain; charset=utf-8\r\n" ++
        "Content-Length: " ++ integer_to_list(length(Body)) ++ "\r\n\r\n" ++
        Body
    ).
not_found() ->
    list_to_binary(
        "HTTP/1.1 404 Not Found\r\n" ++
        "Content-Type: text/plain\r\n" ++
        "Content-Length: 13\r\n\r\n404 Not Found"
    ).
测试一下
erl
然后
Erlang/OTP 27 [erts-15.2.6] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit] [dtrace]
Eshell V15.2.6 (press Ctrl+G to abort, type help(). for help)
1> c(simple_http_server).
{ok,simple_http_server}
2> simple_http_server:start().
运行一下
curl http://localhost:8080/                     # Hello from Erlang REST API
curl http://localhost:8080/ping                 # pong
curl -X POST http://localhost:8080/echo
你到底做了什么?
✅ 你构建了一个最小可运行的 HTTP Server(支持 GET/POST)
✅ 没有用任何框架,全靠你自己解析请求、构造响应
✅ 所有逻辑都是原生进程,不靠 gen_server,也没 OTP 套路
其实很多人觉得 Erlang 写 Web 太麻烦、过时了,但只要你理解其并发模型和分布式能力,这种原始的 socket 编程可以成为你探索更强大后端架构的基石

    
Top comments (1)
不错,学习了
Some comments may only be visible to logged-in visitors. Sign in to view all comments.