Now that we have a Cowboy server that can handle static pages, we need to set it up to handle dynamic pages. In comes ErlyDTL. This is basically going to be the same as OJ's tutorial but for Cowboy instead of webmachine.
ErlyDTL Install
Change your rebar.config to include the ErlyDTL dependency.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%%-*- mode: erlang -*- | |
{sub_dirs, [ | |
"rel" | |
]}. | |
{erl_opts, [debug_info]}. %, fail_on_warning]}. | |
{require_otp_vsn, "R15"}. | |
{deps_dir, ["deps"]}. | |
{deps, | |
[ | |
{cowboy, ".*", {git, "https://github.com/extend/cowboy.git", {branch, "master"}}}, | |
{erlydtl, ".*", {git, "https://github.com/evanmiller/erlydtl.git", "HEAD"}} | |
] | |
}. |
Make a page controlled by code
Modify src/simple_server_http.erl to add a path and handler name to the dynamic page you're creating in dispatch_rules/0. In this case, requests for the 'pony' resource (http://127.0.0.1/pony) should be handled by simple_server_http_pony.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(simple_server_http). | |
-behaviour(gen_server). | |
-define(SERVER, ?MODULE). | |
-export([start_link/0]). | |
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). | |
-record(state, {}). | |
%% ------------------------------------------------------------------ | |
%% API Function Definitions | |
%% ------------------------------------------------------------------ | |
start_link() -> | |
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). | |
%% ------------------------------------------------------------------ | |
%% gen_server Function Definitions | |
%% ------------------------------------------------------------------ | |
dispatch_rules() -> | |
%% {Host, list({Path, Handler, Opts})} | |
[{'_', [ | |
{[], simple_server_http_static, [<<"html">>,<<"index.html">>]} | |
, {[<<"pony">>], simple_server_http_pony, []} | |
, {'_', simple_server_http_catchall, []} | |
]}]. | |
confval(Key, Default) -> | |
case application:get_env(Key) of | |
undefined -> Default; | |
Val -> Val | |
end. | |
init([]) -> | |
Port = confval(port, 80), | |
Ip = confval(ip, "127.0.0.1"), | |
NumAcceptors = confval(num_acceptors, 16), | |
IpStr = case is_list(Ip) of true -> Ip; false -> inet_parse:ntoa(Ip) end, | |
error_logger:info_msg("simple_server listening on http://~s:~B/~n", [IpStr,Port]), | |
%% | |
%% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts | |
cowboy:start_listener(http, NumAcceptors, | |
cowboy_tcp_transport, [{port, Port}], | |
cowboy_http_protocol, [{dispatch, dispatch_rules()}] | |
), | |
{ok, #state{}}. | |
handle_call(_Request, _From, State) -> | |
{noreply, ok, State}. | |
handle_cast(_Msg, State) -> | |
{noreply, State}. | |
handle_info(_Info, State) -> | |
{noreply, State}. | |
terminate(_Reason, _State) -> | |
ok. | |
code_change(_OldVsn, State, _Extra) -> | |
{ok, State}. | |
%% ------------------------------------------------------------------ | |
%% Internal Function Definitions | |
%% ------------------------------------------------------------------ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% | |
%% show details on a specific process | |
%% | |
-module(simple_server_http_pony). | |
-behaviour(cowboy_http_handler). | |
-export([init/3, handle/2, terminate/2]). | |
init({tcp, http}, Req, _Opts) -> | |
{ok, Req, undefined_state}. | |
handle(Req, State) -> | |
{ok, Body} = mylittlepony_dtl:render([{pony_name, "Dazzleglow"}]), | |
Headers = [{<<"Content-Type">>, <<"text/html">>}], | |
{ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), | |
{ok, Req2, State}. | |
terminate(_Req, _State) -> | |
ok. |
Create templates/mylittlepony.dtl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html><body>Your favorite Little Pony is {{ pony_name }}.</body></html> |
Remove the previous release, compile, generate, run.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
rm -R rel/simple_server | |
./rebar compile generate | |
sudo erl -sname simple_server -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s simple_server |
Take in user input
If only the world were so simple that we could just create answers... The following shows how to deal with GET/POST parameters so we can take input from those pesky users.
Create a new dispatch rule and handler for a page called captainflint. I'm going to assume you know how to add a dispatch rule to src/simpleserver_http.erl at this point.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<body> | |
GET squawk:<br /> | |
{{ squawk_get }}<br /><br /> | |
POST squawk:<br /> | |
{{ squawk_post }}<br /><br /> | |
<form method="POST"> | |
post_input: <input type="text" name="post_input" /><br /> | |
post_input: <input type="text" name="post_input" /><br /> | |
<input type="submit" value="POST it!" /><br /> | |
post_input2:<input type="text" name="post_input2" /><br /> | |
</form> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% | |
%% show details on a specific process | |
%% | |
-module(simple_server_http_captainflint). | |
-behaviour(cowboy_http_handler). | |
-export([init/3, handle/2, terminate/2]). | |
init({tcp, http}, Req, _Opts) -> | |
{ok, Req, undefined_state}. | |
handle(Req, State) -> | |
{GetVal, _} = cowboy_http_req:qs_val(<<"get_input">>, Req), | |
PostValArr = get_post_value(<<"post_input">>, Req), | |
PostVal = case PostValArr of | |
undefined -> undefined; | |
_ -> string:join([binary_to_list(X) || X <- PostValArr], ",") | |
end, | |
{ok, Body} = captainflint_dtl:render([{squawk_get, GetVal}, {squawk_post, PostVal}]), | |
Headers = [{<<"Content-Type">>, <<"text/html">>}], | |
{ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), | |
{ok, Req2, State}. | |
get_post_values(Req) -> | |
{Method, _} = cowboy_http_req:method(Req), | |
get_post_values(Method, Req). | |
get_post_values('POST', Req) -> | |
{Vals, _} = cowboy_http_req:body_qs(Req), | |
Vals; | |
get_post_values(_, _) -> | |
undefined. | |
get_post_value(Name, Req) -> | |
PostVals = get_post_values(Req), | |
extract_post_value(Name, PostVals). | |
extract_post_value(_, undefined) -> | |
undefined; | |
extract_post_value(Name, PostVals) -> | |
Matches = [X || X <- PostVals, Name =:= element(1,X)], | |
process_post_value(Matches). | |
process_post_value([]) -> | |
undefined; | |
process_post_value(Vals) -> | |
{_, Result} = lists:unzip(Vals), | |
Result. | |
terminate(_Req, _State) -> | |
ok. |
Go to http://127.0.0.1/captainflint?get_input=Arrr. Thar she blows. Now you have a working example of how to access GET and multi-valued POST parameters.