zoukankan      html  css  js  c++  java
  • Python的web开发

    一、Web开发

                                    

                                    Tcp   udp

     
       

    Cs即客户端、服务器端编程,客户端和服务器端之间需要使用socket,约定协议、版本(协议使用的是tcp或者udp)。Tcp协议和udp协议,指定地址和端口,就可以通信了。

    客户端、服务器端传输数据,数据可以有一定的格式,双方必须先约定好。

    1、BS

    Bs:在tcp协议之上的http协议开发的,浏览器和服务器,是基于cs开发之上的。

    Browser、server开发

    Browser浏览器,一种特殊的客户端,支持HTTP(s)协议,能够通过URL向服务器端发起请求,等待服务端返回HTML,并在浏览器内可视化展示的程序。

    server,支持HTTP(s)协议,能够介绍众多客户端发起的HTTP协议请求,经过处理,将HTML等数据返回给浏览器。

    是特殊的cs,即客户端必须是一种支持HTTP协议且能解析并渲染HTML的软件,服务器端必须是能够接受多客户端http访问的软件服务器。

    http协议底层是基于tcp协议实现的。

    2、分类

    客户器端开发:HTML,css,Javascript

    服务器端开发:Python有wsgi,Django,flask,tornado。

    3、HTTP协议

    1)简介

    安装httpd

    #yum install httpd

    HTTP协议是无状态协议。

    同一个客户端的两次请求之间没有任何关系,从服务器端角度来说,不知道两个请求是来自同一个客户端。

    2)cookie

    键值对信息,浏览器发起每一请求,都会把cookie信息发给服务器端,是一种客户端、服务器端传递数据的技术。

    服务器端可以通过判断这些信息,来确定这次请求是否和之前的请求有关联,

    一般来说cookie信息是在服务器端生成的,返回给客户端。

    客户端可以自己设置cookie信息。

    3)URL

    URL就是地址,统一资源定位符uniform resource locator。每一个连接指向一个资源供客户端访问。

    URL组成:方言,host,path路径,查询字符串。

    访问静态资源的时候,通过URL访问的是网站某路径下的index.html文件,而这个文件对应磁盘上的真实文件,就会从磁盘上读取这个文件, 把这个文件发回给浏览器。

    Scheme模式、协议:

    http、ftp、https、file、mailto等等,都是类似

    host:port

    www.xxxx.com:80默认端口80不写,域名会使用dns解析,

    /path/to.resource

    Path,指向资源的路径。

    ?key1=value&key2=value2

    Query string,查询字符串,问号分割,后面的可以=value形式,用&符号分割。

    4、HTTP消息

    消息分为request和response

    Request:浏览器向服务器端发起请求。

    Response:服务器对客户端的请求响应。

    请求和响应的消息都由请求行,header消息报头,body消息正文组成。

    1)请求

    (1)请求消息行:请求方法method、请求路径、协议版本、CRLF(回车换行)。Get  /  http /1.1 

    (2)请求方法method

    Get:请求获取对应URL对应的资源。把请求的内容到放到了header的里面。

    Post:提交数据至服务器端,请求报文头部。

    Head:和get相似,不过不返回消息正文。

    (3)常见传递信息的方式

    使用get方法使用querystring

             通过查询字符串在URL中传递参数。

    Post方法提交数据。

    URL本身就包含着信息

    2)响应

    响应消息行:协议版本,状态码,消息描述CRLF(回车换行)。    http/1.1  200 ok

    状态码在响应第一行:

    1xx:提示信息,表示请求已被成功接收,继续处理。

    2xx:表示正常响应。

             200.正常的返回了网页消息

    3xx:重定向

             301 页面永久移走,永久重定向,返回新的URL,浏览器会根据返回的URL发起新的request请求。

             302 临时重定向

             304资源未修改,浏览器使用本地缓存。

    4xx:客户端请求错误

             404 not found 网页找不到,客户端请求的资源有错误。

             400  请求语法错误

             401 请求要求身份验证

             403服务器拒绝请求。

    5xx:服务器端错误

             500 服务器内部错误

    502上游服务器错误

    5、无状态、有连接和短连接

    无状态:服务器不知道两次连接请求的之间的关系,同一个浏览器两次访问,服务器端不知道两次之间是有联系的。后来可以根据cookie,session判断。

    有连接:基于tcp协议,是面向连接的,需要3次握手,4次挥手。

    短连接:http1.1之前,都是一个请求一个连接,而tcp的连接创建销毁成本太高,对服务器影响很大。

    从http1.1开始,支持keep-live。默认开启,一个连接打开后,会保持一段时间(可以设置),浏览器再次访问该服务器就会使用这个tcp连接,减轻了服务器的压力,提高了效率。

    6、wsgi

    Wsgi 服务器的通用网关接口。基于http协议的server。

    Wsgi主要规定了服务器端和应用程序间的接口

    Python Web Server Gateway Interface

    通用网关接口cgi

    (1)重点

     基于http协议,wsgi app和server之间的应用。

             对应的函数进行处理。

    不同的路径代表不同的请求。函数和请求之间的映射关系。

    框架对应URL,

    Server把处理逻辑提出去,提给app

    浏览器发送请求给server,server把请求头解包,封装成为字典,调用wsgi的app,app进行逻辑处理,处理完毕后直接返回状态码和报文头给浏览器,返回正文到wsgi到server,server发送http协议正文给浏览器。

    from wsgiref.util import setup_testing_defaults

    from wsgiref.simple_server import make_server

     

    # A relatively simple WSGI application. It's going to print out the

    # environment dictionary after being updated by setup_testing_defaults

    def simple_app(environ, start_response):

        setup_testing_defaults(environ)

     

        status = '200 OK'

        headers = [('Content-type', 'text/plain; charset=utf-8')]

     

        start_response(status, headers)  #返回正文之间必须先返回状态码

     

        ret = [("%s: %s " % (key, value)).encode("utf-8")

               for key, value in environ.items()]

        return ret

     

    httpd = make_server('', 8000, simple_app)

    print("Serving on port 8000...")

    httpd.serve_forever()

    7、wsgi服务器 ----wsgiref

    Wsgiref.simple_server模块实现简单的wsgi http服务器端。

    Wsgiref.Simple_server. make_server(ip,port,demo_app,server_class=wsgisercer,handler_class=wsgirequesthandler)

    启动一个wsgi服务器。

    Wsgiref.simple_server.demo_app(environ,start_response)一个函数,完成了wsgi的应用程序端。

    ###简单的服务器端代码:

    from wsgiref.simple_server import make_server,demo_app

    ip = '127.0.0.1'
    port = 9999
    server = make_server(ip,port,demo_app)# demo_app应用程序,可调用
    server.serve_forever()

    Wsgi服务器的作用:

    监听HTTP服务端口(tcpserver,默认端口80)

    接受浏览器端的HTTP请求并解析封装成environ环境数据。

    负责调用应用程序,将environ和start_response方法传入。

    将应用程序响应的正文封装成http响应报文返回浏览器端。

    8、wsgi app应用程序端

    1)应用程序应该是一个可调用对象。

    2)这个可调用对象接收两个参数。

    3)可调用对象,返回的是一个可迭代对象。函数和class__call__返回值是[]  class中__init__ 要有yield.

    def application(environ,start_response):
        pass

    class Application:
        def __init__(self,environ,start_response):
            pass

    def __iter__(self):
            yield self.ret

    class Application:
        def __call__(self, environ,start_response):
            pass

    app程序端三种实现:

    from wsgiref.simple_server import make_server,demo_app

    #
    def application(environ,start_response):
        status = '200 ok'
        headers = [('Content-Type','text/html;charset=utf-8')]
        start_response(status,headers)
        ret_sre1 = b'welcome to this '
        ret_sre = '{}'.format('welcome').encode()
        return [ret_sre]

    class Application:
        def __init__(self,environ,start_response):
            status = '200 ok'
            headers = [('Content-Type', 'text/html;charset=utf-8')]
            start_response(status, headers)
            ret_sre1 = b'welcome to this '
            # ret_sre = '{}'.format('welcome').encode()
            self.ret = ret_sre1
        def __iter__(self):
            yield self.ret

    #
    class Application:
        def __call__(self, environ,start_response):
            status = '200 ok'
            headers = [('Content-Type', 'text/html;charset=utf-8')]
            start_response(status, headers)
            ret_sre1 = b'welcome to this '
            # ret_sre = '{}'.format('welcome').encode()
            return [ret_sre1]
    ip = '127.0.0.1'
    port = 9999
    server = make_server(ip,port,Application())# demo_app应用程序,可调用
    server.serve_forever()

    8、environ

    是包含HTTP请求信息的dict对象

    名称

    含义

    REQUEST_METHOD

    请求方法,get、post等

    PATH_INFO

    URL中的路径部分

    QUERY_STRING

    查询字符串

    SERVER_NAME,SERVER_POST

    服务器名、端口

    HTTP_HOST

    地址和端口

    SERVER_PROTOCOL

    协议

    HTTP_USER_AGENT

    Useragent信息

    9、start_response

    是一个可调用对象,有3个参数,

    Status:状态码和描述语。

    Response_headers:可迭代对象,是一个元素为二元组的列表。[('Content-Type', 'text/html;charset=utf-8')]    响应头部

    Exc_info=None:在错误处理的时候使用.

    start_reaponse 应该在返回可迭代对象之前调用,因为其返回的是response header,返回的可迭代对象是response body。

    10、服务器端

    服务器程序需要调用符合上述定义的可调用对象app,传入environ、start_response,app处理后,返回响应头和可迭代对象的正文,由服务器封装返回浏览器端。

    from wsgiref.simple_server import make_server

    def application(*args):
        environ = args[0]
        start_response = args[1]
        for k,v in environ.items():
            print(k,v)
        print('====================')
        status = '200 ok'
        headers = [('Content-Type', 'text/html;charset=utf-8')]
        start_response(status,headers)
        ret = [("%s :%s " % (key,value)).encode('utf-8') for key,value in environ.items()]
        return ret

    ip = '127.0.0.1'
    port = 9000

    server = make_server(ip,port,application)
    server.serve_forever()

    测试命令:curl  -I 使用head方法。

             curl   -X 指定方法,-d传输数据

    11、Web服务器

    本质上就是一个tcp协议,监听在特定的端口上

    支持HTTP协议,能够将HTTP请求的报文进行解析,能够把响应数据进行HTTP协议的报文封装并返回浏览器。

    实现wsgi协议,协议约定了和应用程序之间接口。

    HTTP协议是应用层。

    12、app应用程序

    遵从wagi协议

    本身是一个可调用对象

    调用start_response,返回响应头部。

    返回包含正文的可迭代对象。

    响应的是调用app的调用,response的是status,headers。

    app的两个参数environ和start_response

    二、flask框架的实现

    1、wsgi请求environment处理

    Wsgi服务器会帮忙处理http请求报文,但是提供的environ是一个用起来很不方便的字典。

    2、query_string查询字符串的解析

    from wsgiref.simple_server import make_server

    #name=tome&age=3
    def simple_app(environ,start_response):
        query_string = environ.get('QUERY_STRING')
        print(query_string)
        #1字典
        # d = {}
        # for item in query_string.split('&'):
        #     k,_,v  = item.partition('=')
        #     d[k] = v
        #     print('k={},v={}'.format(k,v))
        #     print(d)
        #2字典解析式
        d = {k:v for k,_,v in map(lambda item:item.partition('='),query_string.split('&'))}
        print(d)
        status = '200 ok'
        headers = [('Content-Type', 'text/html;charset=utf-8')]
        start_response(status,headers)

        ret = 'welcome to {}'.format('item').encode()
        return [ret]


    ip = '127.0.0.1'
    port = 9000

    server = make_server(ip,port,simple_app)
    server.serve_forever()

    name=tome&age=3&age=2

    127.0.0.1 - - [26/Jun/2018 16:40:14] "POST /xxx?name=tome&age=3&age=2 HTTP/1.1" 200 15

    {'name': 'tome', 'age': '2'}

    3、cgi模块查询

    import cgi

    from wsgiref.simple_server import make_server

    #name=tome&age=3
    def simple_app(environ,start_response):
        query_string = environ.get('QUERY_STRING')
        print(query_string)
        #1字典
        # d = {}
        # for item in query_string.split('&'):
        #     k,_,v  = item.partition('=')
        #     d[k] = v
        #     print('k={},v={}'.format(k,v))
        #     print(d)
        #2字典解析式
        # d = {k:v for k,_,v in map(lambda item:item.partition('='),query_string.split('&'))}
        # print(d)
        qs = cgi.parse_qs(query_string)
        print(qs)
        status = '200 ok'
        headers = [('Content-Type', 'text/html;charset=utf-8')]
        start_response(status,headers)

        ret = 'welcome to {}'.format('item').encode()
        return [ret]


    ip = '127.0.0.1'
    port = 9000

    server = make_server(ip,port,simple_app)
    server.serve_forever()

    name=tome&age=3&age=2

    127.0.0.1 - - [26/Jun/2018 17:19:06] "POST /xxx?name=tome&age=3&age=2 HTTP/1.1" 200 15

    {'age': ['3', '2'], 'name': ['tome']}

    Cgi模块value的值是列表,因为value可以有多个值。

    3、urllib库

    urllib:请求解析重要的库。

    多值是age=1&age=2这个是多值。

    age=1,2只是一个值。

    Parse_se函数,将同一个名称的多值,保存在字典中,使用列表保存。

    import cgi
    from urllib.parse import parse_qs
    from wsgiref.simple_server import make_server

    #name=tome&age=3
    def simple_app(environ,start_response):
        query_string = environ.get('QUERY_STRING')
        print(query_string)
        #1字典
        # d = {}
        # for item in query_string.split('&'):
        #     k,_,v  = item.partition('=')
        #     d[k] = v
        #     print('k={},v={}'.format(k,v))
        #     print(d)
        #2字典解析式
        # d = {k:v for k,_,v in map(lambda item:item.partition('='),query_string.split('&'))}
        # print(d)
        # qs = cgi.parse_qs(query_string)
        # print(qs)
        qs = parse_qs(query_string)
        print(qs)
        status = '200 ok'
        headers = [('Content-Type', 'text/html;charset=utf-8')]
        start_response(status,headers)

        ret = 'welcome to {}'.format('item').encode()
        return [ret]


    ip = '127.0.0.1'
    port = 9000

    server = make_server(ip,port,simple_app)
    server.serve_forever()

    name=tome&age=3&age=2

    {'age': ['3', '2'], 'name': ['tome']}

    127.0.0.1 - - [26/Jun/2018 17:28:24] "POST /xxx?name=tome&age=3&age=2 HTTP/1.1" 200 15

    name=tome&age=3&age=2,3

    {'age': ['3', '2,3'], 'name': ['tome']}

    4、webob库

    环境数据有很多,都是存在字典中,字典的存取方式没有对象的属性访问方便。

    利用第三方库webob库,可以把环境数据的解析、封装成为类。

    1)webob简介

    安装pip install webob

    官方文档:https://docs.pylonsproject.org/projects/webob/en/stable/#webob

    2)webob.request对象

    from wsgiref.simple_server import make_server
    import webob
    #name=tome&age=3
    def simple_app(environ,start_response):
        query_string = environ.get('QUERY_STRING')
        #print(query_string)
        request = webob.Request(environ)
        print(1,request.headers)
        print(2,request.method)
        print(3,request.path)
        print(4,request.query_string)
        print(5,request.GET)
        print(6,request.POST)
        print('params={}'.format(request.params))

        status = '200 ok'
        headers = [('Content-Type', 'text/html;charset=utf-8')]
        start_response(status,headers)

        ret = 'welcome to {}'.format('item').encode()
        return [ret]


    ip = '127.0.0.1'
    port = 9000

    server = make_server(ip,port,simple_app)
    server.serve_forever()

    1 <webob.headers.EnvironHeaders object at 0x00000050EDB88F60>

    2 POST

    3 /xxx

    4 name=tome&age=3&age=2,3

    5 GET([('name', 'tome'), ('age', '3'), ('age', '2,3')])

    6 <NoVars: Not an HTML form submission (Content-Type: text/plain)>

    params=NestedMultiDict([('name', 'tome'), ('age', '3'), ('age', '2,3')])

    127.0.0.1 - - [26/Jun/2018 17:46:55] "POST /xxx?name=tome&age=3&age=2,3 HTTP/1.1" 200 15

    3)multidict

    是允许一个key存多个值 的类型

    from webob.multidict import MultiDict
    md = MultiDict()

    md.add(1,'abc')
    md.add(1,'cde')
    md.add('a',1)
    md.add('a',2)
    for pair in md.items():
        print(pair)

    print(md.getall(1))
    print(md.get('a'))
    print(md.get(1))

    (1, 'abc')

    (1, 'cde')

    ('a', 1)

    ('a', 2)

    ['abc', 'cde']

    2

    Cde

    4)webob.response对象

    from wsgiref.simple_server import make_server
    import webob
    #name=tome&age=3
    def simple_app(environ,start_response):
        query_string = environ.get('QUERY_STRING')
        #print(query_string)
        request = webob.Request(environ)
        print(1,request.headers)
        print(2,request.method)
        print(3,request.path)
        print(4,request.query_string)
        print(5,request.GET)
        print(6,request.POST)
        print('params={}'.format(request.params))

        res = webob.Response()
        # res.status_code = 250
        # print(res.content_type)

        # print(res.status)
        # print(res.headerlist)
        # status = '200 ok'
        # headers = [('Content-Type', 'text/html;charset=utf-8')]
        # start_response(status,headers)
        start_response(res.status,res.headerlist)

        ret = 'welcome to {}'.format('item').encode('utf-8')
        # res.body = ret
        # return res(environ,start_response)
        return [ret]


    ip = '127.0.0.1'
    port = 9000

    server = make_server(ip,port,simple_app)
    server.serve_forever()

    from wsgiref.simple_server import make_server
    import webob
    #name=tome&age=3
    def simple_app(environ,start_response):
        query_string = environ.get('QUERY_STRING')
        #print(query_string)
        request = webob.Request(environ)
        print(1,request.headers)
        print(2,request.method)
        print(3,request.path)
        print(4,request.query_string)
        print(5,request.GET)
        print(6,request.POST)
        print('params={}'.format(request.params))

        res = webob.Response()       #[('Content-Type', 'text/html;charset=utf-8')]  
        res.status_code = 250      #自己可以设置属性
        print(res.content_type)

        # print(res.status)
        # print(res.headerlist)
        # status = '200 ok'
        # headers = [('Content-Type', 'text/html;charset=utf-8')]
        # start_response(status,headers)
        # start_response(res.status,res.headerlist)

        ret = 'welcome to {}'.format('item').encode('utf-8')
        res.body = ret
        return res(environ,start_response)   返回res
        # return [ret]


    ip = '127.0.0.1'
    port = 9000

    server = make_server(ip,port,simple_app)
    server.serve_forever()

    5)webob.dec装饰器类型

    Wsgify装饰器

    https://docs.pylonsproject.org/projects/webob/en/stable/api/dec.html

    from webob.dec import wsgify
    import webob
    from wsgiref.simple_server import make_server

    @wsgify
    def app(request:webob.Request):
        res = webob.Response('welcome to item')
        return res

    ip = '127.0.0.1'
    port = 9000

    server = make_server(ip,port,app)
    server.serve_forever()

     

     

    wsgify装饰器装饰的函数应该具有一个参数,这个参数应该是webob。Request类型,是对字典environ的对象化后的实例。

     

    返回值:

    是一个webob. Response 类型实例。

    可以是一个bytes类型实例,会被封装成webob. Response类型实例的body属性。

    可以是一个字符串型实例,会被封装转换为bytes类型实例,然后会被封装成为webob. Response类型实例的body属性。

    都会被封装成webob.Response

     

     

     

    一个请求,一个响应:

    from webob.dec import wsgify
    import webob
    from wsgiref.simple_server import make_server

    @wsgify
    def app(request:webob.Request):
        res = webob.Response('welcome to item')
        return res

    def application(environ,start_response):
        request = webob.Request(environ)
        res = webob.Response()
        res.status_code = 200
        ret = 'welcome'.encode('utd-8')
        res.body = ret
        return res(environ,start_response)

    if __name__ == '__main__':

        ip = '127.0.0.1'
        port = 9000

        server = make_server(ip,port,app)
        try:
            server.serve_forever()
        except Exception as e:
            print(e)
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    封装成类

    from webob import Response,Request
    from webob.dec import wsgify
    from wsgiref.simple_server import make_server

    class App:
        @wsgify
        def __call__(self,request:Request):
            return 'welcome to'

    if __name__ == '__main__':
        ip = '127.0.0.1'
        port = 9000
        server = make_server(ip,port,App())
        try:
            server.serve_forever()
        except Exception as e:
            print(e)
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    三、类flask框架实现

    Restfull:request.post

    1、路由route简介

    路由:路怎么走,按照不同的路径分发数据。

    URL代表对不同的资源的访问,认为请求不同的路径对应的数据。对动态网页,不同的路径对应不同的应用程序来处理,返回数据,用户以为是访问的静态网页。

    不管是静态web还是动态web服务器,都需要路径和资源或处理程序的映射,最终返回HTML的文本。

    静态web服务器,解决路径和文件之间的映射。

    动态web服务器,解决路径和应用程序之间的映射。

    所有框架都是如此,都是由路由配置。

    路由的映射关系。

    from webob.dec import wsgify
    from wsgiref import simple_server
    from webob import Request,Response
    import logging


    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT,level=logging.INFO)




    def indexhandler(request):
        return '<h1></h1>'

    def pythonhandler(request):
        return '<h2></h2>'




    class App:

        @wsgify
        def __call__(self, request:Request):
            path = request.path
            if path == '/':
                return indexhandler(request)
            elif path == '/python':
                return pythonhandler(request)

    if __name__ == '__main__':
        server = simple_server.make_server('127.0.0.1',9000,App())
        try:
            server.serve_forever()
        except Exception as e:
            logging.info(e)
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    2、路由功能实现

    路由功能,使用路由类实现。

    路由类实现的功能主要就是完成path到handler函数的映射,使用字典保存合适。

    理由映射建立注册方法,两个参数path和handler。

    1)普通实现

    from webob.dec import wsgify
    from wsgiref import simple_server
    from webob import Request,Response
    import logging
    from webob.exc import HTTPNotFound

    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT,level=logging.INFO)


    class Router:
        ROUTERTABLE = {}

        # @classmethod
        def register(self,path,handler):
            self.ROUTERTABLE[path] = handler
            return handler


    def indexhandler(request):
        return '<h1></h1>'

    def pythonhandler(request):
        return '<h2></h2>'

    router = Router()
    router.register('/',indexhandler)
    router.register('/python',pythonhandler)


    class App:
        _Router = Router
        @wsgify
        def __call__(self, request:Request):
            path = request.path
            try:
                return self._Router.ROUTERTABLE.get(path)(request)
            except Exception as e:
                logging.info(e)
                raise HTTPNotFound()
            # if path == '/':
            #     return indexhandler(request)
            # elif path == '/python':
            #     return pythonhandler(request)

    if __name__ == '__main__':
        server = simple_server.make_server('127.0.0.1',9000,App())
        try:
            server.serve_forever()
        except Exception as e:
            logging.info(e)
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    2)装饰器实现

    from webob.dec import wsgify
    from wsgiref import simple_server
    from webob import Response,Request
    import logging
    from webob.exc import HTTPNotFound

    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT,level=logging.INFO)

    class Router:
        ROUTERABLES = {}

        @classmethod
        def register(cls,path):
            def _register(handler):
                cls.ROUTERABLES[path] = handler
                return handler
            return _register


    @Router.register('/')
    def indexhandler(request):
        return '<h1></h1>'

    @Router.register('/python')
    def pythonhandler(requset):
        return '<h2></h2>'

    # router = Router()
    # router.register('/',indexhandler)
    # router.register('/python',pythonhandler)


    class App:

        _Router = Router
        @wsgify
        def __call__(self, request:Request):
            path = request.path
            try:
                return self._Router.ROUTERABLES.get(path)(request)
            except Exception as e:
                logging.info(e)
                raise HTTPNotFound()

    if __name__ == '__main__':

        server = simple_server.make_server('127.0.0.1',9000,App())

        try:
            server.serve_forever()
        except Exception as e:
            logging.info(e)
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    3、404处理

    Webob.exc提供了异常模块

    使用webob.exc.HTTPNoteFound表示路由表找不到对象的处理函数。

    4、正则表达式匹配模式

    使用字符串匹配的模式,路径匹配很死板,使用正则表达式,可以更好的匹配路径,导入re模块。注册的时候传入模式pattern。而不是字符串。

    类app中实现模式和传入的路径 的匹配。

    因为字典是无序的,在遍历的匹配的时候需要用到有序的,所以采用列表,列表里面的元素用二元组。(编译后的正则对象,handler)

    from webob.exc import HTTPNotFound
    from webob.dec import wsgify
    import logging
    from webob import Request,Response
    import re
    from wsgiref import simple_server



    class Router:
        ROUTERABLE = []


        @classmethod
        def register(cls,pattern):
            def _register(hanler):
                cls.ROUTERABLE.append((re.compile(pattern),hanler))
                return hanler
            return _register

    @Router.register('^/$')
    def indexhandler(request):
        return '<h1></h1>'

    @Router.register('^python$')
    def pythonhandler(request):
        return '<h2></h2>'



    class App:
        _Router = Router
        @wsgify

        def __call__(self, request:Request):
            path = request.path
            for pattern,handler in self._Router.ROUTERABLE:
                if pattern.match(path):
                    return handler(request)

            raise HTTPNotFound()

    if __name__ == '__main__':
        server = simple_server.make_server('127.0.0.1',9000,App())
        try:
            server.serve_forever()
        except Exception as e:
            logging.info(e)
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    分组捕获:

    from webob.dec import wsgify
    from webob.exc import HTTPNotFound
    from webob import Request,Response
    import re
    import logging
    from wsgiref import simple_server


    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT,level=logging.INFO)


    class Rouer:

        ROUTERABLES = []
        @classmethod
        def register(cls,pattern):
            def _register(handler):
                cls.ROUTERABLES.append((re.compile(pattern),handler))
                return handler
            return _register

    @Rouer.register('^/$')
    def indexhandler(request):
        return '<h1></h1>'
    @Rouer.register('^/python$')
    @Rouer.register('^/python/(?P<id>d+)$')
    def pythonhandler(request):
        return '<h2></h2>{}'.format(request.groupdict)

    class App:

        _Router = Rouer
        @wsgify
        def __call__(self, request:Request):
            path = request.path
            for  pattern,handler in self._Router.ROUTERABLES:
                matcher = pattern.match(path)
                if matcher:
                    request.groups = matcher.groups()  #动态增加属性
                    request.groupdict = matcher.groupdict()
                    return handler(request)
            raise HTTPNotFound()

    if __name__ == '__main__':
        server = simple_server.make_server('127.0.0.1',9000,App())
        try:
            server.serve_forever()
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    5、method方法匹配

    请求方法,即使是同一个URL,因为请求方法不同,处理方式也不同。

    需要按照要求匹配请求方法之后才能决定执行什么函数。

    方法

    含义

    Get

    请求指定的页面信息,并返回报头和正文

    HEAD

    类似get请求,只不过返回的响应中没有具体内容,用于获取报头

    Post

    向指定的资源提交数据进行出路请求(例如提交表单或者上传文件),数据被包含在请求报文中。

    Post请求客户你能会导致新的资源建立或已有的资源修改

    Put

    从客户端向服务器端传递的数据取代指定的文档的内容

    Delete

    请求服务器删除指定的内容

    增加注册属性method方法。

    from webob.dec import wsgify
    from webob.exc import HTTPNotFound
    from webob import Request,Response
    import re
    import logging
    from wsgiref import simple_server


    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT,level=logging.INFO)


    class Rouer:

        ROUTERABLES = []
        @classmethod
        def register(cls,method,pattern):
            def _register(handler):
                cls.ROUTERABLES.append((method.upper(),re.compile(pattern),handler))
                return handler
            return _register

    @Rouer.register("GET",r'^/$')
    def indexhandler(request):
        return '<h1></h1>'

    @Rouer.register("GET",r'^/python$')
    @Rouer.register("GET",r'^/python/(?P<id>d+)$')
    def pythonhandler(request):
        return '<h2></h2>{}'.format(request.groupdict)

    class App:

        _Router = Rouer
        @wsgify
        def __call__(self, request:Request):
            path = request.path
            for method, pattern,handler in self._Router.ROUTERABLES:
                if not method == request.method.upper():
                    continue
                matcher = pattern.match(path)
                if matcher:
                    request.groups = matcher.groups()
                    request.groupdict = matcher.groupdict()
                    return handler(request)
            raise HTTPNotFound()

    if __name__ == '__main__':
        server = simple_server.make_server('127.0.0.1',9000,App())
        try:
            server.serve_forever()
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    get的方法和post方法:

    from webob.dec import wsgify
    from webob.exc import HTTPNotFound
    from webob import Request,Response
    import re
    import logging
    from wsgiref import simple_server


    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT,level=logging.INFO)


    class Rouer:

        ROUTERABLES = []
        @classmethod
        def register(cls,pattern,*method):
            def _register(handler):
                cls.ROUTERABLES.append((
                    tuple(map(lambda x:x.upper(),method)),
                    re.compile(pattern),handler))
                print(cls.ROUTERABLES)
                return handler
            return _register

        @classmethod
        def get(cls,pattern):
            return cls.register(pattern,"GET")
        @classmethod
        def post(cls,pattern):
            return cls.register(pattern,"POST")

    # @Rouer.register(r'^/$','GET')   #  1注册一个
    # @Rouer.register(r'^/(?P<id>d+)$')   #method没有指定,支持所有的
    # @Rouer.register(r'^/$')    #method没有指定,支持所有的
    # @Rouer.register(r'^/$',"GET","POST")   #  1注册多个
    @Rouer.get(r'/')
    def indexhandler(request):
        return '<h1></h1>'

    # @Rouer.register(r'^/python$','POST')   #1注册一个
    # @Rouer.register(r'^/python/(?P<id>d+)$',"GET","HEAD")     #2注册多个get head
    # # @Rouer.register(r'^/python/(?P<id>d+)$')  #3不指定,all
    @Rouer.post(r'/python')
    def pythonhandler(request):
        return '<h2></h2>{}'.format(request.groupdict)

    class App:

        _Router = Rouer
        @wsgify
        def __call__(self, request:Request):
            path = request.path
            for method, pattern,handler in self._Router.ROUTERABLES:
                if not method or request.method.upper() in method:
                    matcher = pattern.match(path)
                    if matcher:
                        request.groups = matcher.groups()
                        request.groupdict = matcher.groupdict()
                        return handler(request)
            raise HTTPNotFound()

    if __name__ == '__main__':
        server = simple_server.make_server('127.0.0.1',9000,App())
        try:
            server.serve_forever()
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    map函数转换的结果是惰性求值的,利用tuple。

    from webob.dec import wsgify
    from webob.exc import HTTPNotFound
    from webob import Request,Response
    import re
    import logging
    from wsgiref import simple_server


    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT,level=logging.INFO)


    class Rouer:

        ROUTERABLES = []
        @classmethod
        def register(cls,pattern,*method):
            def _register(handler):
                cls.ROUTERABLES.append((
                    tuple(map(lambda x:x.upper(),method)),
                    re.compile(pattern),handler))
                print(cls.ROUTERABLES)
                return handler
            return _register

    # @Rouer.register(r'^/$','GET')   #  1注册一个
    # @Rouer.register(r'^/(?P<id>d+)$')   #method没有指定,支持所有的
    # @Rouer.register(r'^/$')    #method没有指定,支持所有的
    @Rouer.register(r'^/$',"GET","POST")   #  1注册多个
    def indexhandler(request):
        return '<h1></h1>'

    # @Rouer.register(r'^/python$','POST')   #1注册一个
    @Rouer.register(r'^/python/(?P<id>d+)$',"GET","HEAD")     #2注册多个get head
    # # @Rouer.register(r'^/python/(?P<id>d+)$')  #3不指定,all

    def pythonhandler(request):
        return '<h2></h2>{}'.format(request.groupdict)

    class App:

        _Router = Rouer
        @wsgify
        def __call__(self, request:Request):
            path = request.path
            for method, pattern,handler in self._Router.ROUTERABLES:
                if not method or request.method.upper() in method:
                    matcher = pattern.match(path)
                    if matcher:
                        request.groups = matcher.groups()
                        request.groupdict = matcher.groupdict()
                        return handler(request)
            raise HTTPNotFound()

    if __name__ == '__main__':
        server = simple_server.make_server('127.0.0.1',9000,App())
        try:
            server.serve_forever()
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    注册多个URL的method。

    四、类flask框架

    1、路由分组:

    路由分组,就是按照前缀分别映射。

    常见的一级目录:

    /admin后台管理。   /product  这些目录都是根目录下的第一级。

    两次注册:路由注册,和实例的注册。

    from webob.dec import wsgify
    from webob.exc import HTTPNotFound
    from webob import Request,Response
    import logging
    import re
    from wsgiref import simple_server

    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT,level=logging.INFO)



    class Router:
        def __init__(self,predix:str=''):
            self.__prefix = predix.rstrip('/\')  #前缀的
            self.__routables = []  #存三元组,有序的列表

        def route_register(self,pattern,*methods):
            def _route_register(handler):
                self.__routables.append((tuple(map(lambda x:x.upper(),methods)),
                        re.compile(pattern),handler))
                return handler
            return _route_register

        def get(self,pattern):
            return self.route_register(pattern,'GET')

        def post(self,pattern):
            return self.route_register(pattern,'POST')

        def head(self,pattern):
            return self.route_register(pattern,"HEAD")

        def match(self,request:Request):
            if not request.path.startswith(self.__prefix):
                return None

            for method,patter,handler in self.__routables:
                if not method or request.method.upper() in method:
                    matcher = patter.match(request.path.replace(self.__prefix
                                                                ,'',1))
                    if matcher:
                        request.groups = matcher.groups()
                        request.groupdict = matcher.groupdict()
                        return handler(request)


    class App:
        _ROUTES = []
        @classmethod
        def register(cls,*routers:Router):
            for router in routers:
                cls._ROUTES.append(router)

        @wsgify
        def __call__(self,request):
            for router in self._ROUTES:
                response = router.match(request)
                if response:#匹配返回非None的router对象
                    return response   #匹配则返回
            raise HTTPNotFound()

    idx = Router()
    py = Router('/python')

    App.register(idx,py)


    @idx.get(r'^/$')
    @idx.route_register(r'^/(?P<id>d+)$')
    def indexhandler(request):
        return '<h1></h1>'

    @py.get('^/(w+)$')
    @py.route_register(r'^/(?P<id>d+)$')
    def pythonhandler(request):
        return '<h2></h2>'



    if __name__ == '__main__':
        server = simple_server.make_server('127.0.0.1',9000,App())
        try:
            server.serve_forever()
        except Exception as e:
            logging.info(e)
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    2、字典的属性方法

    属性变成字典的方式来进行访问。

    让kwargs这个字典,不使用[]访问元素,而是使用.点号访问元素。如同属性一样访问。

    class AttrDict:
        def __init__(self,d):
            self.__dict__.update(d)


        def __setattr__(self, key, value):
            raise NotImplementedError



    d = {'a':1,'b':2}
    obj = AttrDict(d)
    print(obj.a)

    加上属性字典:

    from webob.exc import HTTPNotFound
    from webob.dec import wsgify
    from webob import Request,Response
    import re
    from wsgiref import simple_server
    import logging


    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT,level=logging.INFO)

    class AttrDict:
        def __init__(self,d:dict):
            self.__dict__.update(d if isinstance(d,dict)else {})

        def __setattr__(self, key, value):
            raise NotImplementedError

        def __repr__(self):
            return '<AttrDict{}>'.format(self.__dict__)

        def __len__(self):
            return len(self.__dict__)


    class Router:

        def __init__(self,prefix:str=''):
            self._prefix = prefix.rstrip('\/') #前缀
            self.routerables = []   #三元组


        def router_ables(self,pattern,*methods):
            def _router_ables(handler):    #三元组
                self.routerables.append((
                    tuple(map(lambda x:x.upper(),methods)),
                    re.compile(pattern),handler))
                return handler
            return _router_ables

        def get(self,pattern):
            return self.router_ables(pattern,"GET")

        def post(self,pattern):
            return self.router_ables(pattern,"POST")

        def head(self,pattern):
            return self.router_ables(pattern,"HEAD")

        def matcher(self,request:Request):
            if not request.path.startswith(self._prefix):
                return None

            for method,pattern,handler in self.routerables:
                if not method or request.method.upper() in method:
                    matcher = pattern.match(request.path.replace(self._prefix,'',1))
                    if matcher: #匹配上
                        request.groups = matcher.groups()
                        request.groupdict = AttrDict(matcher.groupdict())  #命名分组的字典被属性化
                        print(request.groupdict.id)
                        return handler(request)



    class App:     #一级注册函数
        _ROUTERS = []

        @classmethod
        def app_register(cls,*routers:Router):
            for router in routers:
                cls._ROUTERS.append(router)

        @wsgify
        def __call__(self, request:Request):
            for router in self._ROUTERS:
                response = router.matcher(request)  #前缀匹配
                if response:
                    return response   #匹配上返回,没返回直接报错404
            raise HTTPNotFound()



    idx = Router()
    py = Router('/python')

    App.app_register(idx,py)

    @idx.get(r'^/$')
    @idx.router_ables(r'^/(?P<id>d+)$')
    def indexhanler(request):
        return '<h1></h1>'

    # @idx.get(r'^/$')
    # @idx.router_register(r'^/(?P<id>d+)$')
    # @py.get('^/(w+)$')
    # @py.router_register(r'^/(?P<id>d+)$')


    @idx.get(r'^/(w+)$')
    @py.router_ables(r'^/(?P<id>d+)$')
    def pythonhandler(request):
        return '<h2></h2>{}'.format(request.groupdict.id)

    if __name__ == '__main__':
        server = simple_server.make_server('127.0.0.1',9000,App())
        try:
            server.serve_forever()
        except Exception as e:
            logging.info(e)
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    3、正则表达式化简

    类型

    含义

    对应的正则

    str

    不包含/的任意字符,默认类型

    [^/]+

    word

    字母和数字

    w+

    int

    纯数字,正负数

    [+-]?d+

    float

    正负号,数字,包含

    [+-]?d+.d+

    any

    包含/的任意字符。

    .+

    类型映射到正则表达式;就是使用format函数的填空模式。
    TYPEPATTERNS = {
        'str'  : r'[^/]+',
        'word' : r'w+',
        'int'  : r'[+-]?d+',
        'float': r'[+-]?d+.d+',
        'any' : r'.+'
    }

    name = 'id'
    t = 'int'
    print('/(?P<{}>{})'.format(name,TYPEPATTERNS.get(t,TYPEPATTERNS['str'])))

    捕获匹配用户的输入,捕获到分组名,然后对应正则表达式。

     
       

     

    src = '/student/{name:str}/{id:int}'
    import re
    # pattern = r'/{(w+):(w+)}'
    pattern = r'/{([^{}:]*):([^{}:]*)}'
    regix = re.compile(pattern)
    matcher = regix.search(src)

    def repl(mathcer):
        name = matcher.group(1)
        t = matcher.group(2)
        return '/(?P<{}>{})'.format(name,TYPEPATTERNS.get(t,TYPEPATTERNS['str']))
    # print(matcher.group(1))
    # print(matcher.group(2))

    print(regix.sub(repl,src))

    根据用户输入/{id:int}/{name:str}  把这些信息转化为对应的正则表达式。

    from webob.dec import wsgify
    from webob.exc import HTTPNotFound
    from webob import Response,Request
    import logging
    import re
    from wsgiref import simple_server


    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT,level=logging.INFO)



    class AttrDict:
        def __init__(self,d:dict):
            self.__dict__.update(d if isinstance(d,dict)else {})

        def __setattr__(self, key, value):
            raise NotImplementedError

        def __repr__(self):
            return '<AttrDict{}>'.format(self.__dict__)

        def __len__(self):
            return len(self.__dict__)

    class RouterAbles:
        pa = r'/{([^{}:]+):?([^{}:]*)}'
        _regex = re.compile(pa)

        TYPEPATTERNS = {
            'str': r'[^/]+',
            'word': r'w+',
            'int': r'[+-]?d+',
            'float': r'[+-]?d+.d+',
            'any': r'.+'
        }

        def repl(self,matcher):
            name = matcher.group(1)
            t = matcher.group(2)
            return  '/(?P<{}>{})'.format(name, self.TYPEPATTERNS.get(t, self.TYPEPATTERNS['str']))

        def pase(self,src):
            return self._regex.sub(self.repl,src)


        def __init__(self,prefix:str=''):
            self.__prefix = prefix.rstrip('\/')
            self.__ROUTERABLES = []

        def router_register(self,rule,*method):
            def _router_register(handler):
                self.__ROUTERABLES.append(
                    (tuple(map(lambda x:x.upper(),method)),
                     re.compile(self.pase(rule)),handler))
                print(1,self.__ROUTERABLES)
                return handler
            return _router_register

        def get(self,pattern):
            return self.router_register(pattern,'GET')

        def post(self,pattern):
            return self.router_register(pattern,'POST')

        def head(self,pattern):
            return self.router_register(pattern,"HEAD")


        def match(self,request:Request):
            if not request.path.startswith(self.__prefix):
                return None

            for method,pattern,handler in self.__ROUTERABLES:
                if not method or request.method.upper() in method:
                    matcher = pattern.match(request.path.replace(self.__prefix,'',1))
                    if matcher:
                        # request.groups = matcher.groups()
                        # print(2,request.groups)
                        request.groupdict = AttrDict(matcher.groupdict())
                        print(3,request.groupdict)
                        return handler(request)


    class App:

        _ROUTER = []

        @classmethod
        def app_regsieter(cls,*routerables:RouterAbles):
            for routerable in routerables:
                cls._ROUTER.append(routerable)
                print(4,cls._ROUTER)

        @wsgify
        def __call__(self, request):
            for routerable in self._ROUTER:
                response = routerable.match(request)
                if response:
                    return response
            raise HTTPNotFound()


    idx = RouterAbles('/')
    py = RouterAbles('/python')

    App.app_regsieter(idx,py)

    # @idx.get(r'^/$')
    # @idx.router_register(r'^/(?P<id>d+)$')
    @idx.router_register(r'^/{id:int}$')
    def handler(request):
        id = ''
        if request.groupdict:
            id = request.groupdict.id
        return '<h1></h1>{}'.format(request.groupdict.id)

    # @py.get(r'^/$')
    # @py.get('^/(w+)$')
    # @py.router_register(r'^/(?P<id>d+)$')
    @py.router_register(r'^/{id}$')
    def pythonhandler(request):
        if request.groupdict:
            id = request.groupdict.id
            return '<h2></h2>{}'.format(id)

    if __name__ == '__main__':
        server = simple_server.make_server('127.0.0.1',9000,App())
        try:
            server.serve_forever()
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    访问方式:

    127.0.0.1:9000/python/1234

    127.0.0.1:9000/123

    上例中缺点是捕获的信息sub函数捕获的全部为str。转化的话。需要改造sub函数。

    方法:1、路由注册的时候,将用户指定的rule(@py.router_register(r'^/{id}$'))添加入路由表三元组中。添加的时候调用parse函数。

    2、parse函数中的返回值为:模式匹配sub匹配用户rule中的name和类型,sub(两个参数,调用另外一个函数depl,rule)。

    3、depl函数中,根据信息。提前指定的表格,按照名字和类型,将类型转化为对应的正则表达式类型。

    用户输入三种的形式的匹配,没写的话采用默认值。

    拼接字符串的形式:

    改造parse函数的代码


    from webob.dec import wsgify
    from webob.exc import HTTPNotFound
    from webob import Response,Request
    import logging
    import re
    from wsgiref import simple_server


    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT,level=logging.INFO)


    class AttrDict:
        def __init__(self,d:dict):
            self.__dict__.update(d if isinstance(d,dict)else {})

        def __setattr__(self, key, value):
            raise NotImplementedError

        def __repr__(self):
            return '<AttrDict{}>'.format(self.__dict__)

        def __len__(self):
            return len(self.__dict__)

    class RouterAbles:
        pa = r'/{([^{}:]+):?([^{}:]*)}'
        _regex = re.compile(pa)

        TYPEPATTERNS = {
            'str': r'[^/]+',
            'word': r'w+',
            'int': r'[+-]?d+',
            'float': r'[+-]?d+.d+',
            'any': r'.+'
        }

        # def repl(self,matcher):
        #     print(matcher,']]]')
        #     name = matcher.group(1)
        #     t = matcher.group(2)
        #     s1 = '/(?P<{}>{})'.format(name, self.TYPEPATTERNS.get(t, self.TYPEPATTERNS['str']))
        #     print('----',s1)
        #     return  s1
        #
        # def pase(self,rule):
        #     s2= self._regex.sub(self.repl,rule)
        #     print('+-+-',rule)
        #     print('=====',s2)
        #     return s2

        def parse(self,src: str):
            start = 0
            repl = ''
            types = {}

            TYPECAST = {
                'str': str,
                'word': str,
                'int': int,
                'float': float,
                'any': str
            }
            matchers = self._regex.finditer(src)
            for i, matcher in enumerate(matchers):
                name = matcher.group(1)
                t = matcher.group(2)
                types[name] = TYPECAST.get(t, str)

                repl += src[start:matcher.start()]

                tmp = '/(?P<{}>{})'.format(name, self.TYPEPATTERNS.get(t, self.TYPEPATTERNS['str']))

                repl += tmp

                start = matcher.end()
            else:
                repl += src[start:]

            return repl, types

        def __init__(self,prefix:str=''):
            self.__prefix = prefix.rstrip('\/')
            self.__ROUTERABLES = []

        def router_register(self,rule,*method):
            def _router_register(handler):
                pattern ,types = self.parse(rule)
                self.__ROUTERABLES.append(
                    (tuple(map(lambda x:x.upper(),method)),
                     re.compile(pattern),types,handler))
                print(1,self.__ROUTERABLES)
                return handler
            return _router_register

        def get(self,pattern):
            return self.router_register(pattern,'GET')

        def post(self,pattern):
            return self.router_register(pattern,'POST')

        def head(self,pattern):
            return self.router_register(pattern,"HEAD")


        def match(self,request:Request):
            if not request.path.startswith(self.__prefix):
                return None

            for method,pattern,trans,handler in self.__ROUTERABLES:
                if not method or request.method.upper() in method:
                    matchers = pattern.match(request.path.replace(self.__prefix,'',1))
                    if matchers:
                        newdict ={}
                        for k,v in matchers.groupdict().items():
                            newdict[k] = trans[k](v)
                        # request.groups = matcher.groups()
                        # print(2,request.groups)
                        request.vars = AttrDict(matchers.groupdict())
                        # print(3,request.groupdict)
                        return handler(request)


    class App:

        _ROUTER = []

        @classmethod
        def app_regsieter(cls,*routerables:RouterAbles):
            for routerable in routerables:
                cls._ROUTER.append(routerable)
                print(4,cls._ROUTER)

        @wsgify
        def __call__(self, request):
            for routerable in self._ROUTER:
                response = routerable.match(request)
                if response:
                    return response
            raise HTTPNotFound()


    idx = RouterAbles('/')
    py = RouterAbles('/python')

    App.app_regsieter(idx,py)

    # @idx.get(r'^/$')
    # @idx.router_register(r'^/(?P<id>d+)$')
    @idx.router_register(r'^/{id:int}$')
    def handler(request):
        id = ''
        if request.vars:
            id = request.vars.id
        return '<h1></h1>{}'.format(id)

    # @py.get(r'^/$')
    # @py.get('^/(w+)$')
    # @py.router_register(r'^/(?P<id>d+)$')
    @py.router_register(r'^/{id}$')
    def pythonhandler(request):
        if request.vars:
            id = request.vars.id
            return '<h2></h2>{}'.format(id)

    if __name__ == '__main__':
        server = simple_server.make_server('127.0.0.1',9000,App())
        try:
            server.serve_forever()
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    app中,可以使用字典保存所有的router实例。因为每一个router的实例的前缀不同,完全使用前缀为key,router实例为value组成字典。,以后在call方法中,就不需要遍历列表了。只是需要提取request的path的前缀就可以和字典的key匹配了。提高了效率。

    4、框架处理流程

    客户端发来http请求,被wsgi服务器处理后传递给app的__call__方法。

    App中遍历已经注册的router ,router的match来判断自己能不能处理,前缀匹配,看到注册的规则。(规则是装饰器已经转换成了命名分组的正则表达式了)

    如果由某一个注册的正则表达式匹配,就把回去的参数放到request中,调用注册时候的映射handler传入request。

    Handler处理以后,返回response。App中拿到response的数据,返回给wsgi。

    Handler返回的仅仅是数据,将数据填入HTML中,将新生成的HTML字符串返回给客户端,就是网页技术。模板技术。

    五、flask实现

    1、模板

    HTML:就是格式和数据混在一起的超文本。数据格式的描述。

    XML:数据描述。

    动态数据很难缓存。缓存是没有用的。

    import re
    from io import StringIO,BytesIO

    d = {'id':5,'name':'tom','age':20}


    class Template:
        _pattern = '{{([a-zA-Z0-9_]+)}}'
        regex = re.compile(_pattern)

        @classmethod
        def render(cls,template,data:dict):
            html = StringIO()

            with open(template,encoding='utf-8')as f:
                for line in f :
                    start = 0
                    newline = ''
                    for matcher in cls.regex.finditer(line):
                        newline  += line[start:matcher.start()]
                        print(matcher,matcher.group(1))
                        key = matcher.group(1)
                        tmp = data.get(key,'')
                        newline += str(tmp)
                        start = matcher.end()

     


                    else:
                        newline += line[start:]
                    html.write(newline)

                print(html.getvalue())
            html.close()

    filename = 'index.html'
    Template.render(filename,d)

     

     

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>wcl</title>
    </head>

    <body>
    显示数据<br>
    {{id}}{{name}}{{age}}
    </body>
    </html>

    渲染后的代码:

    2 <_sre.SRE_Match object; span=(3, 9), match='{{id}}'> id

    2 <_sre.SRE_Match object; span=(9, 17), match='{{name}}'> name

    2 <_sre.SRE_Match object; span=(17, 24), match='{{age}}'> age

    1 <!DOCTYPE html>

    <html lang="en">

    <head>

        <meta charset="UTF-8">

        <title>wcl</title>

    </head>

    <body>

    <h1>显示数据</h1>

    <p>5tom20</p>

    </body>

    </html>

    2、jinja

    1)简介

    基于模块的引擎。设计思想来自Django的模板引擎,与其及其相似。

    文档:http://jinja.pocoo.org/docs/2.10/

    http://docs.jinkan.org/docs/jinja2/

    2)安装

    安装:pip install jinjia2

    pip install Markupsafe

    3)模板构建

    当前目录下,新建包webarch,下面闯进一个目录templates,该目录下新建一个HTML模板文件index.html

    常用的模式

    模板代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    显示数据<br>
    <ul>
        {% for id,name,age in userlist %}
        <li>{{loop.index}}{{id}} ,{{name}},{{age}}</li>li>
        {% endfor %}
    </ul>
    total{{usercount}}ren
    </body>
    </html>

    Template文件代码:

    from jinja2 import Environment ,PackageLoader,select_autoescape,FileSystemLoader

    env = Environment(
        loader=PackageLoader('webarch','templates'),
        autoescape=select_autoescape(['html','xml'])
    )
    # env1 = Environment(loader=FileSystemLoader('templates'))

    # d = {'id':5,
    #      'name':'tom',
    #      'age':20,
    #      'list':[]
    # }
    # d = {
    #     'userlist':[
    #         (1,'tom',20),
    #         (3,'jeery',23),
    #         (7,'ll',28)
    #     ]
    # }

    def render(name,data:dict):

        template = env.get_template('index.html')
        html = template.render(**data)
        return html

    3、json

    from .web import RouterAbles,Request,Response,App
    from .template import render


    idx = RouterAbles('/')
    py = RouterAbles('/python')

    App.app_regsieter(idx,py)

    # @idx.get(r'^/$')
    # @idx.router_register(r'^/(?P<id>d+)$')
    @idx.router_register(r'^/{id:int}$')
    def handler(request):
        userlist = [
            (3,'tom',20),
            (5,'jerry',23),
            (6,'ll',25),
            (7,'ls',30)
        ]

        d = {'userlist':userlist,'usercount':len(userlist)}
        # res = Response(json=d)      #json支持
        # return res
        return render('index.html',d)
        # id = ''
        # if request.vars:
        #     id = request.vars.id
        # return '<h1></h1>{}'.format(id)

    # @py.get(r'^/$')
    # @py.get('^/(w+)$')
    # @py.router_register(r'^/(?P<id>d+)$')
    @py.router_register(r'^/{id}$')
    def pythonhandler(request):
        userlist = [
            (3, 'tom', 20),
            (5, 'jerry', 23),
            (6, 'll', 25),
            (7, 'ls', 30)
        ]

        d = {'userlist': userlist, 'usercount': len(userlist)}
        return render('index.html', d)
        # res = Response(json=d)      #json支持
        # return res
        # if request.vars:
        #     id = request.vars.id
        #     return '<h2></h2>{}'.format(id)

    4、模块化

    将所有的代码组织成包和模块。

    1.app.py

    2.webarch(package)

      (1)__init__.py

    (2)template.py

    (3)web.py

    (4)templates(package)

      Index.html

    完整代码:

    (1)app.py    (server)

    from wsgiref import simple_server
    from webarch import App

    if __name__ == '__main__':
        server = simple_server.make_server('127.0.0.1',9000,App())
        try:
            server.serve_forever()
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    (2)__init__.py    (handler函数)

    from .web import RouterAbles,Request,Response,App
    from .template import render


    idx = RouterAbles('/')
    py = RouterAbles('/python')

    App.app_regsieter(idx,py)

    # @idx.get(r'^/$')
    # @idx.router_register(r'^/(?P<id>d+)$')
    @idx.router_register(r'^/{id:int}$')
    def handler(request):
        userlist = [
            (3,'tom',20),
            (5,'jerry',23),
            (6,'ll',25),
            (7,'ls',30)
        ]

        d = {'userlist':userlist,'usercount':len(userlist)}
        return render('index.html',d)
        # id = ''
        # if request.vars:
        #     id = request.vars.id
        # return '<h1></h1>{}'.format(id)

    # @py.get(r'^/$')
    # @py.get('^/(w+)$')
    # @py.router_register(r'^/(?P<id>d+)$')
    @py.router_register(r'^/{id}$')
    def pythonhandler(request):
        userlist = [
            (3, 'tom', 20),
            (5, 'jerry', 23),
            (6, 'll', 25),
            (7, 'ls', 30)
        ]

        d = {'userlist': userlist, 'usercount': len(userlist)}
        return render('index.html', d)
        # if request.vars:
        #     id = request.vars.id
        #     return '<h2></h2>{}'.format(id)

    (3)template.py   (渲染模块加载文件)


    from jinja2 import Environment ,PackageLoader,select_autoescape,FileSystemLoader

    env = Environment(
        loader=PackageLoader('webarch','templates'),
        autoescape=select_autoescape(['html','xml'])
    )
    # env1 = Environment(loader=FileSystemLoader('templates'))

    # d = {'id':5,
    #      'name':'tom',
    #      'age':20,
    #      'list':[]
    # }
    # d = {
    #     'userlist':[
    #         (1,'tom',20),
    #         (3,'jeery',23),
    #         (7,'ll',28)
    #     ]
    # }

    def render(name,data:dict):

    """

    :param name:
    去模块目录搜索此模板名的文件
    :param data:   字典
    :return:  字符串
    """



        template = env.get_template('index.html')
        html = template.render(**data)
        return html

    (4)web.py  (app,routerable,attrdict类)


    from webob.dec import wsgify
    from webob.exc import HTTPNotFound
    from webob import Response,Request
    import logging
    import re



    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT,level=logging.INFO)


    class AttrDict:
        def __init__(self,d:dict):
            self.__dict__.update(d if isinstance(d,dict)else {})

        def __setattr__(self, key, value):
            raise NotImplementedError

        def __repr__(self):
            return '<AttrDict{}>'.format(self.__dict__)

        def __len__(self):
            return len(self.__dict__)

    class RouterAbles:
        pa = r'/{([^{}:]+):?([^{}:]*)}'
        _regex = re.compile(pa)

        TYPEPATTERNS = {
            'str': r'[^/]+',
            'word': r'w+',
            'int': r'[+-]?d+',
            'float': r'[+-]?d+.d+',
            'any': r'.+'
        }

        # def repl(self,matcher):
        #     print(matcher,']]]')
        #     name = matcher.group(1)
        #     t = matcher.group(2)
        #     s1 = '/(?P<{}>{})'.format(name, self.TYPEPATTERNS.get(t, self.TYPEPATTERNS['str']))
        #     print('----',s1)
        #     return  s1
        #
        # def pase(self,rule):
        #     s2= self._regex.sub(self.repl,rule)
        #     print('+-+-',rule)
        #     print('=====',s2)
        #     return s2

        def parse(self,src: str):
            start = 0
            repl = ''
            types = {}

            TYPECAST = {
                'str': str,
                'word': str,
                'int': int,
                'float': float,
                'any': str
            }
            matchers = self._regex.finditer(src)
            for i, matcher in enumerate(matchers):
                name = matcher.group(1)
                t = matcher.group(2)
                types[name] = TYPECAST.get(t, str)

                repl += src[start:matcher.start()]

                tmp = '/(?P<{}>{})'.format(name, self.TYPEPATTERNS.get(t, self.TYPEPATTERNS['str']))

                repl += tmp

                start = matcher.end()
            else:
                repl += src[start:]

            return repl, types

        def __init__(self,prefix:str=''):
            self.__prefix = prefix.rstrip('\/')
            self.__ROUTERABLES = []

        def router_register(self,rule,*method):
            def _router_register(handler):
                pattern ,types = self.parse(rule)
                self.__ROUTERABLES.append(
                    (tuple(map(lambda x:x.upper(),method)),
                     re.compile(pattern),types,handler))
                #print(1,self.__ROUTERABLES)
                return handler
            return _router_register

        def get(self,pattern):
            return self.router_register(pattern,'GET')

        def post(self,pattern):
            return self.router_register(pattern,'POST')

        def head(self,pattern):
            return self.router_register(pattern,"HEAD")


        def match(self,request:Request):
            if not request.path.startswith(self.__prefix):
                return None

            for method,pattern,trans,handler in self.__ROUTERABLES:
                if not method or request.method.upper() in method:
                    matchers = pattern.match(request.path.replace(self.__prefix,'',1))
                    #print('+++',trans)
                    if matchers:
                        newdict ={}
                        for k,v in matchers.groupdict().items():
                            newdict[k] = trans[k](v)
                            #print('+-+-',trans[k](v))
                           #print(1,matchers.groupdict().items())
                            #print(k,v)
                            #print('---',newdict)
                        # request.groups = matcher.groups()
                        # print(2,request.groups)
                        request.vars = AttrDict(newdict)
                        # print(3,request.groupdict)
                        return handler(request)


    class App:

        _ROUTER = []

        @classmethod
        def app_regsieter(cls,*routerables:RouterAbles):
            for routerable in routerables:
                cls._ROUTER.append(routerable)
                print(4,cls._ROUTER)

        @wsgify
        def __call__(self, request):
            for routerable in self._ROUTER:
                response = routerable.match(request)
                if response:
                    return response
            raise HTTPNotFound()

     

    (5)index.html文件内容,渲染模板

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    显示数据<br>
    <ul>
        {% for id,name,age in userlist %}
        <li>{{loop.index}}{{id}} ,{{name}},{{age}}</li>li>
        {% endfor %}
    </ul>
    total{{usercount}}ren
    </body>
    </html>

     

     

    5、拦截器interceptor

    拦截器是在请求处理的环节的某一处加入处理,有可能是中断后续的处理

    分类:链式、洋葱式的。

    拦截点不同:

    1)请求时候拦截的

    2)响应时候拦截

    影响面:

    1)全局拦截,在app中拦截

    2)局部拦截,在router中拦截

    拦截器可以是多个,多个拦截器是有顺序的,数据的response前执行的命名为preinterceptor,之后命名为postinterceptor。

    加入拦截器功能的方式

    (1)app和router类直接加入。

    把拦截器相关方法,属性分别添加到相关的类中。

    实现简单

    (2)Mixin

    App和router类都需要这个拦截器功能,这个两个类没有什么关系,可以使用Mixin方式,将属性、方法组合进来。App拦截器适合使用第二种,但是router的拦截器是每个实例不一样的,所以使用第一种方式实现。

    拦截器函数的设计,fn不能影响数据向下一级的传递,也就是透明的。

    Ip拦截的简单代码;

    from webob.exc import Request



    def ip(request:Request):
        if request.remote_addr.startswish('192'):
            return request
       
        else:
            return None

    6、发布的模式

    from distutils.core import setup
    import glob


    setup(name='webarch',     # 名字
          version='0.0.1',     #版本
          description='python wsgi web framework',   #描述信息
          author='wcl',                            #作者
          author_email='604603701@qq.com',      #作者邮箱
          url='www.',                          #包的主页
          packages=['webarch'],      #打包列表,
          data_files=glob.glob('webarch/templates/*')  #返回列表     配置文件,图片等文件列表
          )

    #打包
    ##python setup.py sdist

    # 安装
    #pip install webarch

    7、总结

    (1)熟悉wsgi的编程接口

    (2)强化模块、类封装的思想

    (3)增强业务分析的能力。

    框架基本具备了wsgi web框架 的基本实现。

    权限验证,SQL注入的检测功能都需要使用拦截器功能实现

    完整代码:

    (1)__init__.py文件

    from .web import RouterAbles,Request,Response,App
    from .template import render


    idx = RouterAbles('/')
    py = RouterAbles('/python')

    App.app_regsieter(idx,py)

    def ip(request:Request):
        if request.remote_addr.startswish('192'):
            return request

        else:
            return None

    py.register_preinterceprot(ip)

    # @idx.get(r'^/$')
    # @idx.router_register(r'^/(?P<id>d+)$')
    @idx.router_register(r'^/{id:int}$')
    def handler(request):
        userlist = [
            (3,'tom',20),
            (5,'jerry',23),
            (6,'ll',25),
            (7,'ls',30)
        ]

        d = {'userlist':userlist,'usercount':len(userlist)}
        # res = Response(json=d)      #json支持
        # return res
        return render('index.html',d)
        # id = ''
        # if request.vars:
        #     id = request.vars.id
        # return '<h1></h1>{}'.format(id)

    # @py.get(r'^/$')
    # @py.get('^/(w+)$')
    # @py.router_register(r'^/(?P<id>d+)$')
    @py.router_register(r'^/{id}$')
    def pythonhandler(request):
        userlist = [
            (3, 'tom', 20),
            (5, 'jerry', 23),
            (6, 'll', 25),
            (7, 'ls', 30)
        ]

        d = {'userlist': userlist, 'usercount': len(userlist)}
        return render('index.html', d)
        # res = Response(json=d)      #json支持
        # return res
        # if request.vars:
        #     id = request.vars.id
        #     return '<h2></h2>{}'.format(id)

    (2)类文件web.py


    from webob.dec import wsgify
    from webob.exc import HTTPNotFound
    from webob import Response,Request
    import logging
    import re



    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT,level=logging.INFO)


    class AttrDict:
        def __init__(self,d:dict):
            self.__dict__.update(d if isinstance(d,dict)else {})

        def __setattr__(self, key, value):
            raise NotImplementedError

        def __repr__(self):
            return '<AttrDict{}>'.format(self.__dict__)

        def __len__(self):
            return len(self.__dict__)

    class RouterAbles:
        pa = r'/{([^{}:]+):?([^{}:]*)}'
        _regex = re.compile(pa)

        TYPEPATTERNS = {
            'str': r'[^/]+',
            'word': r'w+',
            'int': r'[+-]?d+',
            'float': r'[+-]?d+.d+',
            'any': r'.+'
        }

        # def repl(self,matcher):
        #     print(matcher,']]]')
        #     name = matcher.group(1)
        #     t = matcher.group(2)
        #     s1 = '/(?P<{}>{})'.format(name, self.TYPEPATTERNS.get(t, self.TYPEPATTERNS['str']))
        #     print('----',s1)
        #     return  s1
        #
        # def pase(self,rule):
        #     s2= self._regex.sub(self.repl,rule)
        #     print('+-+-',rule)
        #     print('=====',s2)
        #     return s2

        def parse(self,src: str):
            start = 0
            repl = ''
            types = {}

            TYPECAST = {
                'str': str,
                'word': str,
                'int': int,
                'float': float,
                'any': str
            }
            matchers = self._regex.finditer(src)
            for i, matcher in enumerate(matchers):
                name = matcher.group(1)
                t = matcher.group(2)
                types[name] = TYPECAST.get(t, str)

                repl += src[start:matcher.start()]

                tmp = '/(?P<{}>{})'.format(name, self.TYPEPATTERNS.get(t, self.TYPEPATTERNS['str']))

                repl += tmp

                start = matcher.end()
            else:
                repl += src[start:]

            return repl, types

        def __init__(self,prefix:str=''):
            self.__prefix = prefix.rstrip('\/')
            self.__ROUTERABLES = []

            #拦截器
            self.pre_interceptor = []
            self.post_interceptor = []

        def register_preinterceprot(self,fn):
            self.pre_interceptor.append(fn)
            return fn

        def register_postinterport(self,fn):
            self.post_interceptor.append(fn)
            return fn


        def router_register(self,rule,*method):
            def _router_register(handler):
                pattern ,types = self.parse(rule)
                self.__ROUTERABLES.append(
                    (tuple(map(lambda x:x.upper(),method)),
                     re.compile(pattern),types,handler))
                #print(1,self.__ROUTERABLES)
                return handler
            return _router_register

        def get(self,pattern):
            return self.router_register(pattern,'GET')

        def post(self,pattern):
            return self.router_register(pattern,'POST')

        def head(self,pattern):
            return self.router_register(pattern,"HEAD")


        def match(self,request:Request):
            if not request.path.startswith(self.__prefix):
                return None

            #请求拦截,处理request
            for fn in self.pre_interceptor:
                request = fn(request)
                if not request:
                    return None

            for method,pattern,trans,handler in self.__ROUTERABLES:
                if not method or request.method.upper() in method:
                    matchers = pattern.match(request.path.replace(self.__prefix,'',1))
                    #print('+++',trans)
                    if matchers:
                        newdict ={}
                        for k,v in matchers.groupdict().items():
                            newdict[k] = trans[k](v)
                            #print('+-+-',trans[k](v))
                           #print(1,matchers.groupdict().items())
                            #print(k,v)
                            #print('---',newdict)
                        # request.groups = matcher.groups()
                        # print(2,request.groups)
                        request.vars = AttrDict(newdict)
                        # print(3,request.groupdict)
                        return handler(request)


    class App:

        _ROUTER = []
        #全局拦截
        PRE_INTERCEPTOR = []
        POST_INTERCEPTOR = []

        #全局拦截器注册函数
        @classmethod
        def register_preinterceptor(cls,fn):
            cls.PRE_INTERCEPTOR.append(fn)
            return fn

        @classmethod
        def register_postinterceptor(cls,fn):
            cls.POST_INTERCEPTOR.append(fn)

        @classmethod
        def app_regsieter(cls,*routerables:RouterAbles):
            for routerable in routerables:
                cls._ROUTER.append(routerable)
                print(4,cls._ROUTER)

        @wsgify
        def __call__(self, request):
            #全局请求拦截
            for fn in self.PRE_INTERCEPTOR:
                request = fn(request)

            #全局拦截响应
            for routerable in self._ROUTER:
                response = routerable.match(request)
                for fn in self.POST_INTERCEPTOR:
                    response = fn(request,response)

                if response:
                    return response
            raise HTTPNotFound()




    (3)template.py文件


    from jinja2 import Environment ,PackageLoader,select_autoescape,FileSystemLoader

    env = Environment(
        loader=PackageLoader('webarch','templates'),
        autoescape=select_autoescape(['html','xml'])
    )
    # env1 = Environment(loader=FileSystemLoader('templates'))

    # d = {'id':5,
    #      'name':'tom',
    #      'age':20,
    #      'list':[]
    # }
    # d = {
    #     'userlist':[
    #         (1,'tom',20),
    #         (3,'jeery',23),
    #         (7,'ll',28)
    #     ]
    # }

    def render(name,data:dict):
        """

        :param name:
    去模块目录搜索此模板名的文件
        :param data:   字典
        :return:  字符串
        """
       
    template = env.get_template('index.html')
        html = template.render(**data)
        return html

    (4)app.py 文件


    from wsgiref import simple_server
    from webarch import App

    if __name__ == '__main__':
        server = simple_server.make_server('127.0.0.1',9000,App())
        try:
            server.serve_forever()
        except KeyboardInterrupt:
            server.shutdown()
            server.server_close()

    (5)index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    显示数据<br>
    <ul>
        {% for id,name,age in userlist %}
        <li>{{loop.index}}{{id}} ,{{name}},{{age}}</li>li>
        {% endfor %}
    </ul>
    total{{usercount}}ren
    </body>
    </html>

  • 相关阅读:
    oracle 静默安装
    浅析hybrid模式下地支付宝钱包和微信
    LeetCode96_Unique Binary Search Trees(求1到n这些节点能够组成多少种不同的二叉查找树) Java题解
    hdu 5411 CRB and Puzzle 矩阵高速幂
    Hadoop作业性能指标及參数调优实例 (三)Hadoop作业性能參数调优方法
    实现Android4.4系统设置分页滑动浏览功能
    oracle 数据库中数据导出到excel
    Nginx配置upstream实现负载均衡
    公司须要内部的地图服务,准备自己去开发可是成本太高,如今有没有专门为企业提供GIS地图开发的产品呀?大概价格多少?
    图片在内存中的占用的空间大小
  • 原文地址:https://www.cnblogs.com/wangchunli-blogs/p/9949937.html
Copyright © 2011-2022 走看看