代码下载: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>