Erlang 读取 post 请求

Erlang read post request

我正在尝试构建一个基于 Erlang 的简单 Web 服务器,到目前为止,我能够使用以下代码启动服务器。 tutorial ref

-module(helloworld).
-export([
  main/1,
  run_server/0,
  start/0,
  service/3,
]).

main(_) ->
  start(),
  receive
    stop -> ok
  end.

run_server() ->
  ok = inets:start(),
  {ok, _} = inets:start(httpd, [

    {modules, [ 
         mod_alias, 
         mod_auth, 
         mod_esi, 
         mod_actions, 
         mod_cgi, 
         mod_dir,
         mod_get, 
         mod_head, 
         mod_log, 
         mod_disk_log 
      ]}, 
    {port, 8000},
    {server_name,"helloworld"}, 
      {server_root,"/tmp"}, 
      {document_root,"."}, 
      {erl_script_alias, {"/erl", [helloworld]}}, 
      {error_log, "error.log"}, 
      {security_log, "security.log"}, 
      {transfer_log, "transfer.log"}, 

      {mime_types,[ 
         {"html","text/html"}, {"css","text/css"}, {"js","application/x-javascript"} ]} 
   ]). 

start() -> run_server().

service(SessionID, _Env, _Input) -> mod_esi:deliver(SessionID, [ 
   "Content-Type: text/html\r\n\r\n", "<html><body>Hello, World!</body></html>" ]).

我正在尝试获取响应,但出现权限错误。

You don't have permission to access /erl/hello_world:servie on this server.

我的目标是构建基于 Erlang 的服务器,它可以读取 post 请求并将 post 数据存储在 MYSQL 中。

如果有人可以通过说明或一些代码帮助我启动 Erlang 服务器以读取 POST 请求,那将非常有帮助。

谢谢

您正在调用 inets:start/0 和 inets:start/2,在示例中它只是一个或另一个,不知道这是否会产生影响。

I'm aiming to build Erlang based server which can read post request and store the post data in MYSQL.

我用 rebar3 创建了一个应用程序:

$ rebar3 new app myserver

然后我指定了mysql-otp as a dependency so that I could use mysql. I basically ignored the OTP app, and I added some source code to start an inets server. See

这是我用来启动 inets httpd 服务器、处理请求并将 post 数据插入 mysql 数据库的模块:

my.erl:

-module(my).
-compile(export_all).

ensure_inets_start() ->
    case inets:start() of
        ok -> ok;
        {error,{already_started,inets}} -> ok
    end.

start() ->
    ok = my:ensure_inets_start(),

    %Construct path to myserver/priv/server.conf:
    PrivDir = code:priv_dir(myserver),
    ServerConfPath = filename:join(PrivDir, "server.conf"),

    {ok, Server} = inets:start(httpd, 
        [{proplist_file, ServerConfPath}]
    ),
    Server.


stop(Server) ->
    ok = inets:stop(httpd, Server).

log(Data) ->
    {ok, IoDevice} = file:open(
        %"/Users/7stud/erlang_programs/inets_post_request/myserver/logs/mylog.log",
        "./logs/mylog.log",
        [append]
    ),

    file:write(IoDevice, Data),
    file:close(IoDevice).

handle_request(SessionID, Env, Input) ->
    Headers = "Content-Type: text/html\r\n\r\n",
    Data = [
        <<"Hello, ">>, 
        "esi!\n"
    ],

    log(io_lib:format(
        "Inside my:handle_request()\nSessionId=~p\nEnv=~p\nInput=~p\n", 
        [SessionID, Env, Input]
    )),

    PostData = list_to_binary(Input),
    [NamePair, InfoPair] = binary:split(PostData, <<"&">>),
    [<<"name">>, Name] = binary:split(NamePair, <<"=">>),
    [<<"info">>, Info] = binary:split(InfoPair, <<"=">>),
    do_mysql(Name, Info),

    mod_esi:deliver(SessionID, Headers),  %Headers must be a string.
    mod_esi:deliver(SessionID, Data).     %Data can be an iolist.

do_mysql(Name, Info) ->
    {ok, MysqlPid} = mysql:start_link(
                  [{host, "localhost"}, 
                   {user, "root"},
                   {password, ""}, 
                   {database, "mydb"}
                  ]
                ),

    %{ok, ColumnNames, Rows} = mysql:query(
    %            Pid, 
    %            <<"SELECT * FROM people">>),
    %io:format("ColumnNames: ~p~nRows: ~p~n", [ColumnNames, Rows]).

    ok = mysql:query(
           MysqlPid, 
           "INSERT INTO people (name, info) VALUES (?, ?)", [Name, Info]
        ).

我将该代码放在我的应用程序的 src 目录中,该目录名为 myserver。这是我的目录结构:

~/erlang_programs/inets_post_request$ tree myserver

myserver
├── htdocs
├── logs
│   ├── errors.log
│   ├── mylog.log
│   └── requests.log
├── priv
│   └── server.conf
├── rebar.config  (created by rebar3)
├── rebar.lock    (created by rebar3)
└── src
    ├── my.erl
    ├── myserver.app.src  (created by rebar3)
    ├── myserver_app.erl  (created by rebar3)
    └── myserver_sup.erl  (created by rebar3)

(compiling with rebar3 creates all this stuff:)

├── _build
│   └── default
│       └── lib
│           ├── myserver
│           │   ├── ebin
│           │   │   ├── my.beam
│           │   │   ├── myserver.app
│           │   │   ├── myserver_app.beam
│           │   │   └── myserver_sup.beam
│           │   ├── include -> ../../../../include
│           │   ├── priv -> ../../../../priv
│           │   └── src -> ../../../../src
│           └── mysql
│               ├── CHANGELOG.md
│               ├── COPYING
│               ├── COPYING.LESSER
│               ├── Makefile
│               ├── README.md
│               ├── changelog.sh
│               ├── doc
│               │   └── overview.edoc
│               ├── ebin
│               │   ├── mysql.app
│               │   ├── mysql.beam
│               │   ├── mysql_cache.beam
│               │   ├── mysql_encode.beam
│               │   ├── mysql_protocol.beam
│               │   ├── mysql_sock_ssl.beam
│               │   └── mysql_sock_tcp.beam
│               ├── erlang-mk.build.config
│               ├── erlang.mk
│               ├── include
│               │   ├── protocol.hrl
│               │   ├── records.hrl
│               │   └── server_status.hrl
│               ├── priv
│               │   └── edoc-style.css
│               ├── src
│               │   ├── mysql.app.src
│               │   ├── mysql.erl
│               │   ├── mysql_cache.erl
│               │   ├── mysql_encode.erl
│               │   ├── mysql_protocol.erl
│               │   ├── mysql_sock_ssl.erl
│               │   └── mysql_sock_tcp.erl
│               └── test
│                   ├── error_logger_acc.erl
│                   ├── mock_tcp.erl
│                   ├── mysql_encode_tests.erl
│                   ├── mysql_protocol_tests.erl
│                   ├── mysql_tests.erl
│                   ├── ssl
│                   │   ├── Makefile
│                   │   └── my-ssl.cnf.template
│                   ├── ssl_tests.erl
│                   └── transaction_tests.erl

这是 server.conf 文件:

[
  {modules, [
    mod_alias,
    mod_actions,
    mod_esi,
    mod_cgi,
    mod_get,
    mod_log
  ]},
  {bind_address, "localhost"}, 
  {port,0},
  {server_name,"httpd_test"},
  {server_root,"./"},
  {document_root,"./htdocs"},
  {erl_script_alias, {"/erl", [my]} },
  {erl_script_nocache, true},
  {error_log, "./logs/errors.log"},
  {transfer_log, "./logs/requests.log"}
].

然后,运行我做的应用:

~/erlang_programs/inets_post_request/myserver$ rebar3 compile (may not be necessary)
...
...
~/erlang_programs/inets_post_request/myserver$ rebar3 shell (looks like this also will fetch the dependencies, then compile)
===> Verifying dependencies...
===> Compiling myserver
/Users/7stud/erlang_programs/inets_post_request/myserver/_build/default/lib/myserver/src/my.erl:2: Warning: export_all flag enabled - all functions will be exported

Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V9.2  (abort with ^G)
1> S = my:start().
<0.127.0>

2> httpd:info(S). 
[{mime_types,[{"htm","text/html"},{"html","text/html"}]},
 {server_name,"httpd_test"},
 {erl_script_nocache,true},
 {bind_address,{127,0,0,1}},
 {modules,[mod_alias,mod_actions,mod_esi,mod_cgi,mod_get,
           mod_log]},
 {server_root,"/Users/7stud/erlang_programs/inets_post_request/myserver/src"},
 {erl_script_alias,{"/erl",[my]}},
 {port,55804},
 {transfer_log,<0.134.0>},
 {error_log,<0.133.0>},
 {document_root,"./htdocs"}]

3>

我查看了该输出以获取服务器的端口:55804,我曾经使用 curl 发送 post 请求:

~$ curl -v --data "name=Kathy&info=xyz" "http://localhost:55804/erl/my:handle_request"
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 55804 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 55804 (#0)
> POST /erl/my:handle_request HTTP/1.1
> Host: localhost:55804
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Length: 19
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 19 out of 19 bytes
< HTTP/1.1 200 OK
< Date: Wed, 09 May 2018 00:10:42 GMT
< Server: inets/6.4.5
< Cache-Control: no-cache
< Pragma: no-cache
< Expires: Wed, 09 May 2018 00:10:42 GMT
< Transfer-Encoding: chunked
< Content-Type: text/html
< 
Hello, esi!
* Connection #0 to host localhost left intact
~$ 

然后我停止了 inets httpd 服务器:

4> my:stop(S).
ok

5> 

然后我检查了 mysql 数据库中的新条目:

mysql> select * from people;
+----+-------+------+
| id | name  | info |
+----+-------+------+
|  1 | Fred  | abc  |
|  2 | Alice | xxx  |
|  3 | Kathy | xyz  |
+----+-------+------+
4 rows in set (0.00 sec)

成功!

这是我的 rebar.config 文件:

{erl_opts, [debug_info]}.
{deps, [
  {mysql, {git, "https://github.com/mysql-otp/mysql-otp",
          {tag, "1.3.2"}}}
]}.

我对同样写得不好的教程也有同样的问题。最后他们告诉你去:

http://localhost:8081/erl/hello_world:service

然而,URL应该是:

http://localhost:8081/erl/helloworld:service

(注意删除下划线。)