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\n" % (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\n" % (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>

 

posted @ 2018-11-12 23:37  Python爱好者666  阅读(1096)  评论(0编辑  收藏  举报