转自:http://veniceweb.googlecode.com/svn/trunk/public/daily_tech_doc/mochiweb_20091030.txt
MochiWeb项目主页:
http://code.google.com/p/mochiweb/ 1. 网络资源
1.1 实战Mochiweb
http://erlang-china.org/start/mochiweb_intro.html 1.2 (译)用Mochiweb打造百万级Comet应用,第一二三部分
http://idisc.javaeye.com/blog/267028
http://idisc.javaeye.com/blog/270076
http://idisc.javaeye.com/blog/273074
(原文)
http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-1
http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-2
http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-3 1.3 MochiWeb的设计
http://www.javaeye.com/topic/348379 1.4 Comet web chat(with MochiWeb)
一个老外使用MochiWeb构造webchat的例子
http://yoan.dosimple.ch/blog/2008/05/15/
这篇文章的中文介绍
http://erlang-china.org/misc/facebook_comet_in_mochiweb.html 2. 安装 & Example:
2.1 svn checkout http://mochiweb.googlecode.com/svn/trunk/ mochiweb-read-only
2.2 进入到mochiweb的目录, 直接make.
2.3 escript scripts/new_mochiweb.erl basicweb (生成的新项目在当前目录的basicweb目录下)
escript scripts/new_mochiweb.erl basicweb /home/woomsgadmin/web (指定新项目的存放路径)
new_mochiweb.erl是一个escript脚本, 负责从mochiweb中拷贝使用mochiweb所必需的文件和目录,
形成新的项目的骨架,上面的命令生成了basicweb项目. 项目的目录结构:
./basicweb/ 项目目录
Makefile Make文件
start.sh 启动脚本
start-dev.sh 开发模式下的启动脚本(开启代码重载机制)
./basicweb/ebin/ 编译目录
./basicweb/support/ Make支持文件目录
include.mk Make包含脚本
./basicweb/deps/ 依赖目录,包含mochiweb自身
./basicweb/src/ 代码目录
basicweb.app OTP规范的应用定义文件
Makefile Make文件
basicweb_web.erl 应用的web服务器代码
basicweb_deps.erl 负责加载deps目录的代码
basicweb_sup.erl OTP规范的监控树
basicweb.hrl 应用的头文件
basicweb_app.erl OTP规范的应用启动文件
basicweb.erl 应用的API定义文件
./basicweb/doc/ 文档目录
./basicweb/include/ 包含文件目录
./basicweb/priv/ 项目附加目录
./basicweb/priv/www/ 项目附加的www目录
index.html 默认的项目首页 2.4 在新的项目中, 直接make
2.5 ./start-dev.sh启动新的项目. 3. 演示一个comet的例子,使用HTTP-CHUNKED的例子, Server端可以一直推送数据到客户端:
重点:
(下面使用Req:ok/1这是典型的参数化模板的例子)
<1> 使用 Req:get(path)获得当前的访问路径Path
<2> 使用 Req:get(method)获取当前的方法: GET, HEAD, POST
<3> 使用 Response = Req:ok({"text/plain", chunked})来产生一个chunk-response
<4> 使用 Response:write_chunk(Data)来发送响应到客户端, Data是发送给客户端的内容. -module(basicweb_web).
-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),
case Req:get(method) of
Method when Method =:= 'GET'; Method =:= 'HEAD' ->
case Path of
"timer" ->
Response = Req:ok({"text/plain", chunked}),
handle_timer(Response);
_ ->
Req:serve_file(Path, DocRoot)
end;
'POST' ->
case Path of
_ ->
Req:not_found()
end;
_ ->
Req:respond({501, [], []})
end. %% Internal API get_option(Option, Options) ->
{proplists:get_value(Option, Options), proplists:delete(Option, Options)}. handle_timer(Response) ->
Response:write_chunk(io_lib:format("time is: ~p~n", [calendar:local_time()])),
timer:sleep(1000),
handle_timer(Response). 测试:
当用户输入http://localhost:8000/timer的时候,
页面显示(每隔一秒钟打印一条信息):
time is: {{2009,12,9},{21,18,31}}
time is: {{2009,12,9},{21,18,32}}
time is: {{2009,12,9},{21,18,33}}
time is: {{2009,12,9},{21,18,34}}
time is: {{2009,12,9},{21,18,35}} 4. 分析mochiweb的内部结构::
总结:
a. 如果响应静态文件,通过Req:server_file(Path, DocRoot)来发送
b. 如果生成动态内容,通过Req:respond({Code, ResponseHeaders, Body})来发送(文本和二进制都可以)
c. 如果是chunked响应,第一次调用Req:respond()后返回一个Response对象(参数化模块mochiweb_response的一个实例),
以后的响应数据通过这个Response对象发送给客户端,调用Response:write_chunk(Data)来发送后续响应的数据. 补充:
mochiweb内部,每个HTTP请求一个Erlang Process来处理. 详细介绍:
<1> Web服务器分为三步和mochiweb_request
a. 接收HTTP请求
b. 处理HTTP请求,生成响应内容
c. 发送响应 mochiweb_request是处理HTTP请求的核心部分,负责了第二和第三步的工作,
由于使用了参数化模板的技术,这意味着具备了OO的特性,它的每个实例化对象
代表一个用户请求,我们可以像操作OO那样处理HTTP请求,获取请求的信息,
生成响应内容等等. <2> 通过Req直接发送给浏览器(Req是mochiweb_request的实例化对象).
Req:respond({Code, ResponseHeaders, Body})
Code是HTTP的状态码,例如200
ResponseHeaders是响应头
第三个参数是响应内容
a.
HResponse = mochiweb_headers:make([]),
HResponse1 = mochiweb_headers:enter("Content-Type", "text/plain", HResponse),
Req:respond({200, HResponse1, "<html><head></head><title>Timer</title><body><h2>this is h2</h2></body></html>"});
页面将显示(因为是text/plain):
<html><head></head><title>Timer</title><body><h2>this is h2</h2></body></html> b. 效果和a一样.
Req:respond({200, [{"Content-Type","text/plain"}], "<html><head></head><title>Timer</title><body><h2>this is h2</h2></body></html>"}); c. 上面的例子也可以直接使用Req:ok{ContextType, Body},实质上内部也是调用Rep:respond/1返回状态码为200的HTTP响应.
Req:ok({"text/html", "<html><head></head><title>Timer</title><body><h1>this is h1</h1></body></html>"});
页面显示
this is h1 d. URL不存在的404错误可以使用Req:not_found():
其内部使实现是:
not_found() ->
not_found([]).
not_found(ExtraHeaders) ->
respond({404, [{"Content-Type", "text/plain"} | ExtraHeaders], <<"Not found.">>}). %% Body使用Binary提高效率. e. 如果是静态文件,如图片, 可以使用Req:server_file(Path, DocRoot)来返回
告诉此函数文件的Web根目录(doc_root)和相对路径(Path)就可以将指定的静态文件发给浏览器了
doc_root目录一般在配置文件中设置,这个目录下的所有文件都可以通过HTTP访问, 默认是yourweb/priv/www. 如果我的一张图片在Server端得存放路径是priv/www/image/test.jpg.
Req:serve_file(Path, DocRoot) -> http://localhost:8000/image/test.jpg
或者
Req:serve_file(Path, DocRoot ++ "/image") -> http://localhost:8000/test.jpg <3> 通过Response模块对象将响应发送给客户端(本质还是调用Req:respond来发送).
Req:respond函数会返回一个mochiweb_response参数化模块实例对象(假设为Response),
Response实例对象包含有对应的Req实例对象。通过Response对象可以得到响应的相关信息
(如响应状态码,响应消息头,对应的Req对象),它还有一个 send函数可以将响应数据发还给浏览器
(它的实现其实还是调用Req对象的send函数进行的)。 Response之所以还要有send函数是为了发送 chunked数据(HTTP 1.1)的方便,在第一次响应完成后,
后继的chunk数据就可以通过最初返回的Response对象继续进行发送了,为此Response有个函数
write_chunk()专门干这事,write_chunk检查了请求消息头中的HTTP 版本消息后就调用Response:send。 因此,响应内容最终都是由参数化模块mochiweb_request的respond/1函数发送的。而这个respond(...)
函数内部最后调用了Req:send(Data)函数将响应通过socket连接(调用gen_tcp:send)返还给浏览器,
这一发送过程又分成两个阶段:响应消息头(Headers)的发送和消息体(Body)的发送,这两步都是通过Req:send完成的 5. Req:get(XXX)方法解析
mochiweb中,每个client请求其构造一个 Req 对象, Req可以理解成 mochiweb_request 的一个参数化或实例化.
<1> Req:get(method) -> ‘OPTIONS’ | ‘GET’ | ‘HEAD’ | ‘POST’ | ‘PUT’ | ‘DELETE’ | ‘TRACE’.
获取Http请求的方式.
<2> Req:get(raw_path) -> String().
获取raw_path.比如 http://www.nextim.cn/session/login?username=test#p,那/session/login?username=test#p就是这个raw_path.
<3> Req:get(path) -> String().
获取path.比如 http://www.nextim.cn/session/login?username=test#p,那/session/login就是这个raw_path.
<4> Req:parse_qs() -> [{strng(), string()}].
获取get参数.比如 http://www.nextim.cn/session/login?username=test#p,则返回[{"username","test"}].
<5> Req:parse_post() -> [{strng(), string()}].
确保post数据类型为: application/x-www-form-urlencoded, 否则不要调用(其内部会调用Req:recv_body),返回值类似Req:parse_qs().
<6> Req:get(peer) -> string().
返回值为client的ip
<7> Req:get_header_value(Key) -> undefined | string().
获取某个header,比如Key为”User-Agent”时,返回”Mozila…….”
<8> Req:get_primary_header_value(Key) -> undefined | string().
获取http headers中某个key对应的主值(value以分号分割).
举例: 假设 Content-Type 为 application/x-www-form-urlencoded; charset=utf8,则
Req:get_header_value(”content-type”) 返回 application/x-www-form-urlencoded
<9> Req:get(headers) -> dict().
获取所有headers
说明: 返回结果为stdlib/dict 数据结构,可以通过mochiweb_headers模块进行操作.
举例: 下面代码显示请求中所有headers:
Headers = Req:get(headers),
lists:foreach(fun(Key, Value) ->
io:format(”~p : ~p ~n”, [Key, Value])
end,
mochiweb_headers:to_list(Headers)).
<10> Req:parse_cookie() -> [{string(), string()}].
解析Cookie
<11> Req:get_cookie_value(Key) -> string() | undefined
类似Req:get_header_value(Key) 6. Cookie的例子:
下面是一个读取和设置Cookie的例子:
<1> 读取Cookie
Req:get_cookie_value(Key) -> string() | undefined
<2> 设置Cookie, 设置的cookie作为ResponseHeaders在Req:respond({Code, ResponseHeaders, Body})返回.
mochiweb_cookies:cookie(Key, Val, Options) 其中Key:string(), Val:string() 功能: 利用Cookie记录页面的访问次数
http://localhost:8000/timer的时候,第一次返回
Key: undefined,
然后是
Key: Number (其中Number是你访问这个页面的次数) loop(Req, DocRoot) ->
"/" ++ Path = Req:get(path),
case Req:get(method) of
Method when Method =:= 'GET'; Method =:= 'HEAD' ->
case Path of
"timer" ->
CookieKey = Req:get_cookie_value("key"), %% 读取Cookie
CookieKey1 = case CookieKey of
undefined ->
1;
_ ->
list_to_integer(CookieKey) + 1
end,
CookieKey2 = mochiweb_cookies:cookie("key", integer_to_list(CookieKey1), []), %% 设置Cookie
Req:respond({200, [{"Content-Type","text/html"}, CookieKey2], io_lib:format("<h1>Key: ~p~n</h1>", [CookieKey])});
_ ->
Req:serve_file(Path, DocRoot ++ "/image")
end;
'POST' ->
case Path of
_ ->
Req:not_found()
end;
_ ->
Req:respond({501, [], []})
end. 7. 重定向的例子: URL Redirect
loop(Req, DocRoot) ->
"/" ++ Path = Req:get(path),
case Req:get(method) of
Method when Method =:= 'GET'; Method =:= 'HEAD' ->
case Path of
"login" ->
Req:respond({302, [{"Location", "home/user/qiang"}], ""}); %% 重定向到 http://localhost:8000/home/user/qiang
"home/user/qiang" ->
Req:respond({200, [{"Content-Type", "text/html"}], "login success"});
_ ->
Req:serve_file(Path, DocRoot)
end;
'POST' ->
case Path of
_ ->
Req:not_found()
end;
_ ->
Req:respond({501, [], []})
end.

转载:MochiWeb一些资料的链接的更多相关文章

  1. Socket的用法——NIO包下SocketChannel的用法 ———————————————— 版权声明:本文为CSDN博主「茶_小哥」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/ycgslh/article/details/79604074

    服务端代码实现如下,其中包括一个静态内部类Handler来作为处理器,处理不同的操作.注意在遍历选择键集合时,没处理完一个操作,要将该请求在集合中移除./*模拟服务端-nio-Socket实现*/pu ...

  2. 云盘WEB资料下载链接

    入门三板斧:http://www.cnblogs.com/jikey/p/3613082.html 入门看这个:http://pan.baidu.com/s/1pJqJvAV 入门JS视频:http: ...

  3. 【转载】学习资料存档:jQuery的deferred对象详解

    我在以前的文章里提到promise和deferred,这两个东西其实是对回调函数的一种写法,javascript的难点之一是回调函数,但是我们要写出优秀的javascript代码又不得不灵活运用回调函 ...

  4. (转载)HTML:模拟链接被按下,在新标签页打开页面,不使用window.open(可能被拦截)

    原文: http://www.cppblog.com/biao/archive/2010/08/21/124196.html 当按下一个按钮时,想打开一个新的标签页,可以使用window.open去实 ...

  5. [转载] 不查资料确定int型整数的最大值和最小值

    原文地址:http://blog.csdn.net/zhanghuoding/article/details/42719213 想法来自于书中的习题. Java的话直接输出 Integer.MAX_V ...

  6. [笔记]Altera系列01:常用资料下载链接

    Altera官方文档 Altera Product Catalog 外部存储器规范估算器 To be continued.

  7. VueJs一些资料网站链接

    https://github.com/liukaijv/laravel-vue-cmshttps://github.com/allan2coder/VUE2-SPA-Tutorialhttps://g ...

  8. asp.net学习视频资料地址链接

    ASP.NET开发学习视频教程大全(共800集) http://felix520wj.blog.51cto.com/7129746/1548458 http://study.163.com/cours ...

  9. python相关资料链接

    后续的博客更新,会涉及到很多的python及python的框架相关的内容,这里将自己收藏的一些关于python相关资料的链接做一个整理,算是一个导航索引吧... PS:其中有些链接对应的技术团队文章, ...

随机推荐

  1. 【Android】3.4 图层展示

    分类:C#.Android.VS2015.百度地图应用: 创建日期:2016-02-04 3.4 示例4--图层展示 一.简介 1.地图类型 百度地图Android SDK 3.7.1提供了两种类型的 ...

  2. android framework-下载Android系统源代码

    □ apt-get install git-core curl #先下载这两个工具 □ mkdir android-froyo #建立下载目录 □ cd android-froyo #进入下载目录 □ ...

  3. Oracle数据库操作类及连接方法

    创建DataBaseAccess引用库下面包括DBConnectionString.cs,DBType.cs ,SysName.cs 三个类 DBConnectionString: using Sys ...

  4. ny20 吝啬的国度

    吝啬的国度 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 在一个吝啬的国度里有N个城市,这N个城市间只有N-1条路把这个N个城市连接起来.现在,Tom在第S号城市,他有 ...

  5. VC++ 知识点

    1.寻找文件时,CFileFind类的使用. 2.寻找目录时使用BROWSEINFO,其中包含了用户选中目录的重要信息. 3.LPITEMIDLIST类 4.目录选择对话框的使用SHBrowseFor ...

  6. c++包管理工具conan

    Conan is a portable package manager, intended for C and C++ developers, but it is able to manage bui ...

  7. C#委托举例

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  8. 基于jQuery/CSS3实现拼图效果的相册插件

    今天我们要来分享一款很酷的jQuery相册插件,首先相册中的图片会以一定的角度倾斜放置在页面上,点击图片缩略图就可以展开图片,并且图片是由所有缩略图拼接而成,图片展开和收拢的动画效果也非常不错.当然图 ...

  9. js获取相同name文本框的value

    <html xmlns="http://www.w3.org/1999/xhtml"><head runat="server"> < ...

  10. linux实现防止恶意扫描 PortSentry

    linux实现防止恶意扫描 PortSentry   脚本 open 摘要: 端口做为服务器的大门安全很重要,当服务器运行很多服务时并向外提供服务,为防止有人恶意侦测服务器用途,可使用portsent ...