javaee论坛

普通会员

225648

帖子

353

回复

367

积分

楼主
发表于 2017-07-30 06:50:46 | 查看: 555 | 回复: 0
cherrypy的文档说明比较少,看官网的例子,在python2.6中不能直接运行,需要修改如下:

#!/usr/bin/python2.6'''see also:http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htmhttp://docs.cherrypy.org/stable/progguide/REST.html '''import cherrypyclass Resource(object):    def __init__(self, content):        self.content = content            exposed = True        def GET(self):        return self.to_html()            def PUT(self):        self.content = self.from_html(cherrypy.request.body.read())            def to_html(self):        html_item = lambda (name,value): '<div>%s:%s</div>'%(name, value)        items = map(html_item, self.content.items())        items = ''.join(items)        return '<html>{items}</html>'.format(**vars())            @staticmethod    def from_html(data):        pattern = re.compile(r'\<div\>(?P<name>.*?)\:(?P<value>.*?)\</div\>')        items = [match.groups() for match in pattern.finditer(data)]        return dict(items)        class ResourceIndex(Resource):    def to_html(self):        html_item = lambda (name,value): '<div><a href="%s">%s</a></div>'%(value, name)        items = map(html_item, self.content.items())        items = ''.join(items)        return '<html>{items}</html>'.format(**vars())        class Root(object):    pass    root = Root()root.sidewinders = Resource({'color': 'red', 'weight': 176, 'type': 'stable'})root.teebird = Resource({'color': 'green', 'weight': 173, 'type': 'overstable'})root.blowfly = Resource({'color': 'purple', 'weight': 169, 'type': 'putter'})# http://192.168.20.94:1970/resource_indexroot.resource_index = ResourceIndex({'sidewinder': 'sidewinder', 'teebird': 'teebird', 'blowfly': 'blowfly'})conf = {    'global': {        'server.socket_host': '0.0.0.0',        'server.socket_port': 1970,    },    '/': {        'request.dispatch': cherrypy.dispatch.MethodDispatcher(),    }}cherrypy.quickstart(root, '/', conf)

我写了另外一个例子,默认的根就是链接列表:

#!/usr/bin/python2.6import cherrypyimport time;class Resource(object):    exposed = True    def __init__(self, content):        self.content = content    def GET(self):        return self.content    def PUT(self):        self.content = cherrypy.request.body.read()        class Empty(object):    pass;    class Root(object):    exposed = True    def __init__(self, links):        self.links = links    def GET(self):        html_item = lambda (name, url): "<a href='%s'>%s</a><br/>"%(url, name);        return "".join(map(html_item, self.links.items()));    # http://192.168.20.94:1970/root = Root({"the red color": "/red", "the green color":"/others/green"})# http://192.168.20.94:1970/redroot.red = Resource('red')# http://192.168.20.94:1970/others/greenroot.others = Empty();root.others.green = Resource('green');conf = {    'global': {        'server.socket_host': '0.0.0.0',        'server.socket_port': 1970,    },    '/': {        'request.dispatch': cherrypy.dispatch.MethodDispatcher(),    }}cherrypy.quickstart(root, '/', conf)

一个比较全的例子,和redmine的类似:

#!/usr/bin/python2.6# -*- coding: utf-8 -*-'''see also:http://tools.cherrypy.org/wiki/RestfulDispatchhttp://www.redmine.org/projects/redmine/wiki/Rest_api'''import sys;import cherrypyimport time;# set the default encoding to utf-8# reload sys model to enable the getdefaultencoding method.reload(sys);# using exec to set the encoding, to avoid error in IDE.exec("sys.setdefaultencoding('utf-8')");assert sys.getdefaultencoding().lower() == "utf-8";class Stream(object):    exposed = True;    def __init__(self, stream_id):        self.stream_id = stream_id;    def GET(self):        print("[Stream][GET] get a stream. id=%s"%(self.stream_id));        return self.stream_id    def PUT(self):        print("[Stream][PUT] update a stream. id=%s"%(self.stream_id));        self.stream_id = cherrypy.request.body.read()    def DELETE(self):        print("[Streams][DELETE] delete a stream. id=%s"%(self.stream_id));    def POST(self):        print("[Streams][POST] create a stream. NotAllowed");        raise cherrypy.HTTPError(405)class Streams(object):    exposed = True    def __init__(self):        pass;    def GET(self):        print("[Streams][GET] get all streams");        return "all stream list";    def PUT(self):        print("[Streams][PUT] update all streams. NotAllowed");        raise cherrypy.HTTPError(405)    def DELETE(self):        print("[Streams][DELETE] delete all streams. NotAllowed");        raise cherrypy.HTTPError(405)    def POST(self):        print("[Streams][POST] create a new streams");        info = cherrypy.request.body.read()        print("[Streams][POST] new stream created. info=%s"%(info));    def __getattr__(self, name):        # stream operations.        if name.isdigit():            return Stream(name);        return object.__getattr__(name);    class Root(object):    exposed = True    def __init__(self):        pass;    def GET(self):        file = open("http.method.html");        data = file.read();        file.close();        return data;    # http://192.168.20.94:1970/root = Root()# http://192.168.20.94:1970/streams# http://192.168.20.94:1970/streams/100root.streams = Streams();conf = {    'global': {        'server.socket_host': '0.0.0.0',        'server.socket_port': 1970,        'tools.encode.on':True,         'tools.encode.encoding':'utf8',     },    '/': {        'request.dispatch': cherrypy.dispatch.MethodDispatcher(),    }}cherrypy.quickstart(root, '/', conf)


用作测试的http页面:

<!-- http.method.html --><head>    <title>SmartSystem1.0-HttpMethod</title>    <meta http-equiv=Content-Type content="text/html;charset=utf-8"></head><style>    div.item{        background-color: #00EEEE;        width: 620px;        margin-bottom: 3px;        padding: 3 5 10 10;        font-size: 12px;    }    div.title{        font-weight: bold;        margin-bottom: 0px;    }    div.get_content{        background-color: #FFFFFF;        width: 600px;        height: 120px;        padding: 3 5 3 5;    }    div.put_content{    }    div.delete_content{    }    div.post_content{    }    font.warn{        color:#FF0000;        background-color:#FFFF00;    }</style><body><div>    <div>        <div class="item">            URL: <input type="text" id="url" size="50"></input><br/>            <font class="warn">推荐用相对目录,因为AJAX不能跨域访问,</font>        </div>    </div>    <div>        <div class="item">            <div class="title">                HTTP GET: <input type="button" value="Do GET" onclick="do_get()"></input>            </div>            <div>                <div class="get_content" id="get_content"></div>            </div>        </div>    </div>    <div>        <div class="item">            <div class="title">                HTTP PUT: <input type="button" value="Do PUT" onclick="do_put()"></input>            </div>            <div>                <textarea class="put_content" id="put_content" cols=84 rows=9></textarea>            </div>        </div>    </div>    <div>        <div class="item">            <div class="title">                HTTP DELETE: <input type="button" value="Do DELETE" onclick="do_delete()"></input>            </div>            <div>                <textarea class="delete_content" id="delete_content" cols=84 rows=9></textarea>            </div>        </div>    </div>    <div>        <div class="item">            <div class="title">                HTTP POST: <input type="button" value="Do POST" onclick="do_post()"></input>            </div>            <div>                <textarea class="post_content" id="post_content" cols=84 rows=9></textarea>            </div>        </div>    </div></div>    <script type="text/javascript">        var url = document.getElementById("url");        var get_content = document.getElementById("get_content");        var put_content = document.getElementById("put_content");        var delete_content = document.getElementById("delete_content");        var post_content = document.getElementById("post_content");                // set default values.        url.value = "streams/100";        put_content.value = "new_task";        delete_content.value = "100";        post_content.value = "color=red";                function do_get(){            get_content.innerText = "";                        var ajax = new XMLHttpRequest();            ajax.open("GET", url.value, false);            ajax.send(null);                        get_content.innerText = ajax.responseText;        }        function do_put(){            var ajax = new XMLHttpRequest();            ajax.open("PUT", url.value, false);            //console.log("content:" + put_content.value);            ajax.send(put_content.value);        }        function do_delete(){            var ajax = new XMLHttpRequest();            ajax.open("DELETE", url.value, false);            ajax.send(delete_content.value);        }        function do_post(){            var ajax = new XMLHttpRequest();            ajax.open("POST", url.value, false);            ajax.send(post_content.value);        }    </script></body>


另外一个例子,可以支持将一个目录的所有静态文件(命名不能有句号)作为ui:

#!/usr/bin/python2.6# -*- coding: utf-8 -*-'''see also:http://tools.cherrypy.org/wiki/RestfulDispatchhttp://www.redmine.org/projects/redmine/wiki/Rest_api'''import sys;import cherrypyimport time;import re;import os;# set the default encoding to utf-8# reload sys model to enable the getdefaultencoding method.reload(sys);# using exec to set the encoding, to avoid error in IDE.exec("sys.setdefaultencoding('utf-8')");assert sys.getdefaultencoding().lower() == "utf-8";class Stream(object):    exposed = True;    def __init__(self, stream_id):        self.stream_id = stream_id;    def GET(self):        print("[Stream][GET] get a stream. id=%s"%(self.stream_id));        return "id=%s"%(self.stream_id);    def PUT(self):        print("[Stream][PUT] update a stream. id=%s"%(self.stream_id));        self.stream_id = cherrypy.request.body.read()    def DELETE(self):        print("[Streams][DELETE] delete a stream. id=%s"%(self.stream_id));    def POST(self):        print("[Streams][POST] create a stream. NotAllowed");        raise cherrypy.HTTPError(405)class Streams(object):    exposed = True    def __init__(self):        pass;    def GET(self):        print("[Streams][GET] get all streams");        return "all stream list";    def PUT(self):        print("[Streams][PUT] update all streams. NotAllowed");        raise cherrypy.HTTPError(405)    def DELETE(self):        print("[Streams][DELETE] delete all streams. NotAllowed");        raise cherrypy.HTTPError(405)    def POST(self):        print("[Streams][POST] create a new streams");        info = cherrypy.request.body.read()        print("[Streams][POST] new stream created. info=%s"%(info));        return "new stream created";    def __getattr__(self, name):        # stream operations.        if name.isdigit():            return Stream(name);        return object.__getattr__(name);    class StaticFile(object):    exposed = True    def __init__(self, filename):        self.filename = filename;    def GET(self):        file = open(self.filename);        data = file.read();        file.close();        return data;class UI(object):    exposed = True    def __init__(self):        pass;    def __getattr__(self, name):        if os.path.exists(os.path.join("ui", name)):            return StaticFile(os.path.join("ui", name));        return object.__getattr__(name);        class Root(object):    exposed = True    def __init__(self):        pass;    def GET(self):        raise cherrypy.HTTPRedirect("ui");    # http://192.168.20.94:1970/root = Root()# http://192.168.20.94:1970/ui# http://192.168.20.94:1970/ui/method3root.ui = UI()# http://192.168.20.94:1970/streams# http://192.168.20.94:1970/streams/100root.streams = Streams();conf = {    'global': {        'server.socket_host': '0.0.0.0',        'server.socket_port': 1970,        'tools.encode.on':True,         'tools.encode.encoding':'utf8',     },    '/': {        'request.dispatch': cherrypy.dispatch.MethodDispatcher(),    }}cherrypy.quickstart(root, '/', conf)

ui的目录结构如下:

[winlin@dev6 cherrypy]$ tree uiui├── default└── method3

这样访问:http://192.168.20.94:1970

就会定向到ui目录:http://192.168.20.94:1970/ui/method3

其中method3的内容和上面的http.method.html是一样的。

ui/default是默认页面,可以用script跳转:

[winlin@dev6 cherrypy]$ cat ui/default <script>window.location.href="ui/method3"</script>[winlin@dev6 cherrypy]$ 

若需要开自己的线程,需要写cherrypy的插件来启动和停止:

http://docs.cherrypy.org/stable/progguide/extending/customplugins.html

http://stackoverflow.com/questions/2004514/force-cherrypy-child-threads

其中,cherrypy的main事件,是个loop,是另外一个线程启动的:

[cycle][Thread #139682221430528] tasks: {'2646': <__main__.Task instance at 0x7f0a30017d40>}

[Thread #139682016102160] delete task, id=2646

[Thread #139682026592016] delete task, id=2646

所以如果需要写同样的数据,是需要lock的。


js/ajax如何跨域,浏览器现在是有支持了的:

http://www.iteye.com/topic/600682

在没有CORS之前, 浏览器要发出跨域的请求时必须要得到被请求域的许可,而许可必须要以大家公认的方式,否则就被认为是不安全的。    那么CORS就定义了许可方式 它定义了 8个头信息来表示许可形式 分别是     * 4.1 Access-Control-Allow-Origin Response Header     * 4.2 Access-Control-Max-Age Response Header     * 4.3 Access-Control-Allow-Credentials Response Header     * 4.4 Access-Control-Allow-Methods Response Header     * 4.5 Access-Control-Allow-Headers Response Header     * 4.6 Origin Request Header     * 4.7 Access-Control-Request-Method Request Header     * 4.8 Access-Control-Request-Headers Request Header 浏览器请求时, 必须带上Origin及Access-Control-Request-Method头信息(这些信息浏览器自动加的)。 分别表示来源网站及要使用的http方法, 当然这个请求一般是一个http Options方法。 例如: OPTIONS  http://incubator.vicp.net/p/liuhan Host: incubator.vicp.net User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn,zh;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Origin: http://www.google.cn Access-Control-Request-Method: POST Access-Control-Request-Headers: x-requested-with 接下来关键来了, 服务器要在相应头里给予许可 即通过 Access-Control-Allow-Origin 头 表示允许的网站。 Access-Control-Allow-Methods 表示允许的方法 例如  响应 HTTP/1.1 200 OK Access-Control-Allow-Origin: http://arunranga.com Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: X-PINGOTHER Access-Control-Max-Age: 1728000 浏览器拿到这些响应信息就可以发出正常的调用了。 

死活试了很久,不知道如何用cherrypy支持跨域ajax访问。后来终于知道:

1. 除了要实现GET/POST/PUT/DELETE等,还需要实现OPTIONS。

2. 每个方法都需要设置response的头。

代码如下:

#!/usr/bin/python2.6# -*- coding: utf-8 -*-'''see also:http://tools.cherrypy.org/wiki/RestfulDispatchhttp://www.redmine.org/projects/redmine/wiki/Rest_api'''import sys;import cherrypyimport time;# set the default encoding to utf-8# reload sys model to enable the getdefaultencoding method.reload(sys);# using exec to set the encoding, to avoid error in IDE.exec("sys.setdefaultencoding('utf-8')");assert sys.getdefaultencoding().lower() == "utf-8";# supprt crossdomain ajax scriptdef enable_crossdomain():    cherrypy.response.headers["Access-Control-Allow-Origin"] = "*";    cherrypy.response.headers["Access-Control-Allow-Methods"] = "GET, POST, HEAD, PUT, DELETE";    #cherrypy.response.headers["Access-Control-Allow-Headers"] = "Cache-Control, X-Proxy-Authorization, X-Requested-With";    #cherrypy.response.headers["Access-Control-Max-Age"] = "604800";class Stream(object):    exposed = True;    def __init__(self, stream_id):        self.stream_id = stream_id;    def GET(self):        enable_crossdomain();        print("[Stream][GET] get a stream. id=%s"%(self.stream_id));        return "id=%s"%(self.stream_id);    def PUT(self):        enable_crossdomain();        print("[Stream][PUT] update a stream. id=%s"%(self.stream_id));        self.stream_id = cherrypy.request.body.read()    def DELETE(self):        enable_crossdomain();        print("[Streams][DELETE] delete a stream. id=%s"%(self.stream_id));    def POST(self):        enable_crossdomain();        print("[Streams][POST] create a stream. NotAllowed");        raise cherrypy.HTTPError(405)    def OPTIONS(self):        enable_crossdomain();class Streams(object):    exposed = True    def __init__(self):        pass;    def GET(self):        enable_crossdomain();        print("[Streams][GET] get all streams");        return "all stream list";    def PUT(self):        enable_crossdomain();        print("[Streams][PUT] update all streams. NotAllowed");        raise cherrypy.HTTPError(405)    def DELETE(self):        enable_crossdomain();        print("[Streams][DELETE] delete all streams. NotAllowed");        raise cherrypy.HTTPError(405)    def POST(self):        enable_crossdomain();        print("[Streams][POST] create a new streams");        info = cherrypy.request.body.read()        print("[Streams][POST] new stream created. info=%s"%(info));        return "new stream created";    def __getattr__(self, name):        # stream operations.        if name.isdigit():            return Stream(name);        return object.__getattr__(name);    def OPTIONS(self):        enable_crossdomain();    class Root(object):    exposed = True    def __init__(self):        pass;    def GET(self):        file = open("http.method5.html");        data = file.read();        file.close();        return data;    # http://192.168.20.94:1970/root = Root()# http://192.168.20.94:1970/streams# http://192.168.20.94:1970/streams/100root.streams = Streams();conf = {    'global': {        'server.socket_host': '0.0.0.0',        'server.socket_port': 1970,        'tools.encode.on':True,         'tools.encode.encoding':'utf8',     },    '/': {        'request.dispatch': cherrypy.dispatch.MethodDispatcher(),    }}cherrypy.quickstart(root, '/', conf)
客户端没有特殊需求,用ajax就可以。

<!-- http.method.html --><head>    <title>SmartSystem1.0-HttpMethod</title>    <meta http-equiv=Content-Type content="text/html;charset=utf-8"></head><style>    div.item{        background-color: #00EEEE;        width: 620px;        margin-bottom: 3px;        padding: 3 5 10 10;        font-size: 12px;    }    div.title{        font-weight: bold;        margin-bottom: 0px;    }    div.get_content{        background-color: #FFFFFF;        width: 600px;        height: 120px;        padding: 3 5 3 5;    }    div.put_content{    }    div.delete_content{    }    div.post_content{    }    font.warn{        color:#FF0000;        background-color:#FFFF00;    }</style><body><div>    <div>        <div class="item">            URL: <input type="text" id="url" size="50"></input><br/>            <font class="warn">支持AJAX跨域访问.</font>        </div>    </div>    <div>        <div class="item">            <div class="title">                HTTP GET: <input type="button" value="Do GET" onclick="do_get()"></input>            </div>            <div>                <div class="get_content" id="get_content"></div>            </div>        </div>    </div>    <div>        <div class="item">            <div class="title">                HTTP PUT: <input type="button" value="Do PUT" onclick="do_put()"></input>            </div>            <div>                <textarea class="put_content" id="put_content" cols=84 rows=9></textarea>            </div>        </div>    </div>    <div>        <div class="item">            <div class="title">                HTTP DELETE: <input type="button" value="Do DELETE" onclick="do_delete()"></input>            </div>            <div>                <textarea class="delete_content" id="delete_content" cols=84 rows=9></textarea>            </div>        </div>    </div>    <div>        <div class="item">            <div class="title">                HTTP POST: <input type="button" value="Do POST" onclick="do_post()"></input>            </div>            <div>                <textarea class="post_content" id="post_content" cols=84 rows=9></textarea>            </div>        </div>    </div></div>    <script type="text/javascript">        var url = document.getElementById("url");        var get_content = document.getElementById("get_content");        var put_content = document.getElementById("put_content");        var delete_content = document.getElementById("delete_content");        var post_content = document.getElementById("post_content");                // set default values.        url.value = "http://192.168.20.94:1970/streams";        put_content.value = "new_task";        delete_content.value = "100";        post_content.value = "color=red";                function do_get(){            get_content.innerText = "";                        var ajax = new XMLHttpRequest();                        ajax.open("GET", url.value, false);                        ret = ajax.send(null);                        get_content.innerText = ajax.responseText;        }        function do_put(){            var ajax = new XMLHttpRequest();            ajax.open("PUT", url.value, false);            //console.log("content:" + put_content.value);            ajax.send(put_content.value);        }        function do_delete(){            var ajax = new XMLHttpRequest();            ajax.open("DELETE", url.value, false);            ajax.send(delete_content.value);        }        function do_post(){            var ajax = new XMLHttpRequest();            ajax.open("POST", url.value, false);            ajax.send(post_content.value);        }    </script></body>


响应头的charset,如果在配置中设置了:'tools.encode.encoding': 'utf-8',则永远是utf-8。

如果这个设置错误,cherrypy.response.headers是没有办法改过来的。

如果服务器返回的header的Content-Type的charset是错误的,ie的ajax会报错。

另外,ie的脚本解析也和chrome不太一样。

ie的js的语法更严格,局部变量要用var,最后一个参数后面不能有逗号等。


您需要登录后才可以回帖 登录 | 立即注册

触屏版| 电脑版

技术支持 历史网 V2.0 © 2016-2017