zoukankan      html  css  js  c++  java
  • 转载:MochiWeb一些资料的链接

    转自: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.
    
    
    
    
    
    
    
    
    
    
                        
  • 相关阅读:
    《有毒》读后感 读书笔记
    《自动时代》读后感 读书笔记
    HTML 中有用的字符实体
    input 光标在 chrome下不兼容 解决方案
    form注册表单圆角 demo
    横向导航二级菜单
    鼠标悬浮,下拉菜单,距离一定距离
    纵向折叠二级菜单(无限点击)
    纵向折叠二级菜单
    纵向导航二级弹出菜单
  • 原文地址:https://www.cnblogs.com/ribavnu/p/3364793.html
Copyright © 2011-2022 走看看