MochiWeb : an Erlang library for building lightweight HTTP servers

本文提供了一套详细的步骤指南,演示如何利用MochiWeb库快速搭建Web应用,包括代码下载、项目创建、启动测试等关键操作,并通过实际代码展示了Web服务器配置及请求处理流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代码下载:https://github.com/mochi/mochiweb

最佳实践:http://alexmarandon.com/articles/mochiweb_tutorial/

========================================================

1. 代码下载:

$ git clone git://github.com/mochi/mochiweb.git

2. 创建新项目:

$ cd mochiweb
$ make app PROJECT=greeting
3. 启动测试代码

$ cd ../greeting/
$ make
$ ./start-dev.sh
========================================================

修改代码src/greeting_web.erl

%% @author Mochi Media <dev@mochimedia.com>
%% @copyright 2010 Mochi Media <dev@mochimedia.com>

%% @doc Web server for greeting.

-module(greeting_web).
-author("Mochi Media <dev@mochimedia.com>").

-export([start/1, stop/0, loop/2]).

%% External API

start(Options) ->
    {DocRoot, Options1} = get_option(docroot, Options),
    Loop = fun (Req) ->
                   ?MODULE:loop(Req, DocRoot)
           end,
    mochiweb_http:start([{name, ?MODULE}, {loop, Loop} | Options1]).

stop() ->
    mochiweb_http:stop(?MODULE).

loop(Req, DocRoot) ->
    "/" ++ Path = Req:get(path),
    try
        case dispatch(Req, greeting_views:urls()) of
            none ->
                % No request handler found
                case filelib:is_file(filename:join([DocRoot, Path])) of
                    true ->
                        % If there's a static file, serve it
                        Req:serve_file(Path, DocRoot);
                    false ->
                        % Otherwise the page is not found
                        Req:not_found()
                end;
            Response ->
                Response
        end
    catch
        Type:What ->
            Report = ["web request failed",
                      {path, Path},
                      {type, Type}, {what, What},
                      {trace, erlang:get_stacktrace()}],
            error_logger:error_report(Report),
            %% NOTE: mustache templates need \ because they are not awesome.
            Req:respond({500, [{"Content-Type", "text/plain"}],
                         "request failed, sorry\n"})
    end.

%% Internal API

get_option(Option, Options) ->
    {proplists:get_value(Option, Options), proplists:delete(Option, Options)}.

%%
%% Tests
%%
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").

you_should_write_a_test() ->
    ?assertEqual(
       "No, but I will!",
       "Have you written any tests?"),
    ok.

-endif.

% Iterate recursively on our list of {Regexp, Function} tuples
dispatch(_, []) -> none;
dispatch(Req, [{Regexp, Function}|T]) ->
    "/" ++ Path = Req:get(path),
  Method = Req:get(method),
  Match = re:run(Path, Regexp, [global, {capture, all_but_first, list}]),
  case Match of
    {match,[MatchList]} ->
      % We found a regexp that matches the current URL path
      case length(MatchList) of
        0 ->
          % We didn't capture any URL parameters
          greeting_views:Function(Method, Req);
        Length when Length > 0 ->
          % We pass URL parameters we captured to the function
          Args = lists:append([[Method, Req], MatchList]),
          apply(greeting_views, Function, Args)
      end;
    _ ->
      dispatch(Req, T)
  end.

新建src/greeting_shortcuts.erl

-module(greeting_shortcuts).
-compile(export_all).

render_ok(Req, TemplateModule, Params) ->
  render_ok(Req, [], TemplateModule, Params).

render_ok(Req, Headers, TemplateModule, Params) ->
  {ok, Output} = TemplateModule:render(Params),
  Req:ok({"text/html", Headers, Output}).

get_cookie_value(Req, Key, Default) ->
  case Req:get_cookie_value(Key) of
    undefined -> Default;
    Value -> Value
  end.

新建src/greeting_views.erl

-module(greeting_views).
-compile(export_all).
-import(greeting_shortcuts, [render_ok/3, render_ok/4, get_cookie_value/3]).

urls() -> [
  {"^hello/?$", hello},
  {"^hello/(.+?)/?$", hello}
].

% Return username input if present, otherwise return username cookie if
% present, otherwise return "Anonymous"
get_username(Req, InputData) ->
  proplists:get_value("username", InputData,
    get_cookie_value(Req, "username", "Anonymous")).

make_cookie(Username) ->
  mochiweb_cookies:cookie("username", Username, [{path, "/"}]).

handle_hello(Req, InputData) ->
  Username = get_username(Req, InputData),
  Cookie = make_cookie(Username),
  render_ok(Req, [Cookie], greeting_dtl, [{username, Username}]).

hello('GET', Req) ->
  handle_hello(Req, Req:parse_qs());

hello('POST', Req) ->
  handle_hello(Req, Req:parse_post()).

hello('GET', Req, Username) ->
  Cookie = make_cookie(Username),
  render_ok(Req, [Cookie], greeting_dtl, [{username, Username}]);

hello('POST', Req, _) ->
  hello('POST', Req).

新建priv/www/index.html

<html>
<head>
<title>Post Name</title>
</head>
<body>
<form method="POST" action="/hello">
    <p>Username: <input type="text" name="username"></p>
    <input type="submit">
</form>
</body>
</html>

修改rebar.config,启动ErlyDTL。

%% -*- erlang -*-
{erl_opts, [debug_info]}.
{deps, [
  {erlydtl, ".*",
   {git, "git://github.com/evanmiller/erlydtl.git", "master"}},
  {mochiweb, ".*",
   {git, "git://github.com/mochi/mochiweb.git", {branch, "master"}}}]}.
{cover_enabled, true}.
{eunit_opts, [verbose, {report,{eunit_surefire,[{dir,"."}]}}]}.

新建templates/greeting.dtl

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>MochiWeb Tutorial</title>
    <link rel="stylesheet" type="text/css" href="/style.css" media="screen">
</head>
<body>
<p>
    Hello {{ username }}!
</p>
</body>
</html>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值