Tcp udpcss
Cs即客戶端、服務器端編程,客戶端和服務器端之間須要使用socket,約定協議、版本(協議使用的是tcp或者udp)。Tcp協議和udp協議,指定地址和端口,就能夠通訊了。html
客戶端、服務器端傳輸數據,數據能夠有必定的格式,雙方必須先約定好。python
Bs:在tcp協議之上的http協議開發的,瀏覽器和服務器,是基於cs開發之上的。web
Browser、server開發正則表達式
Browser瀏覽器,一種特殊的客戶端,支持HTTP(s)協議,可以經過URL向服務器端發起請求,等待服務端返回HTML,並在瀏覽器內可視化展現的程序。編程
server,支持HTTP(s)協議,可以介紹衆多客戶端發起的HTTP協議請求,通過處理,將HTML等數據返回給瀏覽器。json
是特殊的cs,即客戶端必須是一種支持HTTP協議且能解析並渲染HTML的軟件,服務器端必須是可以接受多客戶端http訪問的軟件服務器。flask
http協議底層是基於tcp協議實現的。api
客戶器端開發:HTML,css,Javascript瀏覽器
服務器端開發:Python有wsgi,Django,flask,tornado。
安裝httpd
#yum install httpd
HTTP協議是無狀態協議。
同一個客戶端的兩次請求之間沒有任何關係,從服務器端角度來講,不知道兩個請求是來自同一個客戶端。
鍵值對信息,瀏覽器發起每一請求,都會把cookie信息發給服務器端,是一種客戶端、服務器端傳遞數據的技術。
服務器端能夠經過判斷這些信息,來肯定此次請求是否和以前的請求有關聯,
通常來講cookie信息是在服務器端生成的,返回給客戶端。
客戶端能夠本身設置cookie信息。
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形式,用&符號分割。
消息分爲request和response
Request:瀏覽器向服務器端發起請求。
Response:服務器對客戶端的請求響應。
請求和響應的消息都由請求行,header消息報頭,body消息正文組成。
(1)請求消息行:請求方法method、請求路徑、協議版本、CRLF(回車換行)。Get / http /1.1
(2)請求方法method
Get:請求獲取對應URL對應的資源。把請求的內容到放到了header的裏面。
Post:提交數據至服務器端,請求報文頭部。
Head:和get類似,不過不返回消息正文。
(3)常見傳遞信息的方式
使用get方法使用querystring
經過查詢字符串在URL中傳遞參數。
Post方法提交數據。
URL自己就包含着信息
響應消息行:協議版本,狀態碼,消息描述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上游服務器錯誤
無狀態:服務器不知道兩次鏈接請求的之間的關係,同一個瀏覽器兩次訪問,服務器端不知道兩次之間是有聯繫的。後來能夠根據cookie,session判斷。
有鏈接:基於tcp協議,是面向鏈接的,須要3次握手,4次揮手。
短鏈接:http1.1以前,都是一個請求一個鏈接,而tcp的鏈接建立銷燬成本過高,對服務器影響很大。
從http1.1開始,支持keep-live。默認開啓,一個鏈接打開後,會保持一段時間(能夠設置),瀏覽器再次訪問該服務器就會使用這個tcp鏈接,減輕了服務器的壓力,提升了效率。
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()
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響應報文返回瀏覽器端。
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()
是包含HTTP請求信息的dict對象
名稱 |
含義 |
REQUEST_METHOD |
請求方法,get、post等 |
PATH_INFO |
URL中的路徑部分 |
QUERY_STRING |
查詢字符串 |
SERVER_NAME,SERVER_POST |
服務器名、端口 |
HTTP_HOST |
地址和端口 |
SERVER_PROTOCOL |
協議 |
HTTP_USER_AGENT |
Useragent信息 |
是一個可調用對象,有3個參數,
Status:狀態碼和描述語。
Response_headers:可迭代對象,是一個元素爲二元組的列表。[('Content-Type', 'text/html;charset=utf-8')] 響應頭部
Exc_info=None:在錯誤處理的時候使用.
start_reaponse 應該在返回可迭代對象以前調用,由於其返回的是response header,返回的可迭代對象是response body。
服務器程序須要調用符合上述定義的可調用對象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傳輸數據
本質上就是一個tcp協議,監聽在特定的端口上
支持HTTP協議,可以將HTTP請求的報文進行解析,可以把響應數據進行HTTP協議的報文封裝並返回瀏覽器。
實現wsgi協議,協議約定了和應用程序之間接口。
HTTP協議是應用層。
聽從wagi協議
自己是一個可調用對象
調用start_response,返回響應頭部。
返回包含正文的可迭代對象。
響應的是調用app的調用,response的是status,headers。
app的兩個參數environ和start_response
Wsgi服務器會幫忙處理http請求報文,可是提供的environ是一個用起來很不方便的字典。
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'}
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能夠有多個值。
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']}
環境數據有不少,都是存在字典中,字典的存取方式沒有對象的屬性訪問方便。
利用第三方庫webob庫,能夠把環境數據的解析、封裝成爲類。
安裝pip install webob
官方文檔:https://docs.pylonsproject.org/projects/webob/en/stable/#webob
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
是容許一個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
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()
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()
Restfull:request.post
路由:路怎麼走,按照不一樣的路徑分發數據。
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()
路由功能,使用路由類實現。
路由類實現的功能主要就是完成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()
Webob.exc提供了異常模塊
使用webob.exc.HTTPNoteFound表示路由表找不到對象的處理函數。
使用字符串匹配的模式,路徑匹配很死板,使用正則表達式,能夠更好的匹配路徑,導入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()
請求方法,即便是同一個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。
路由分組,就是按照前綴分別映射。
常見的一級目錄:
/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()
屬性變成字典的方式來進行訪問。
讓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()
類型 |
含義 |
對應的正則 |
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函數。
方法:一、路由註冊的時候,將用戶指定的rule(@py.router_register(r'^/{id}$'))添加入路由表三元組中。添加的時候調用parse函數。
二、parse函數中的返回值爲:模式匹配sub匹配用戶rule中的name和類型,sub(兩個參數,調用另一個函數depl,rule)。
三、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匹配了。提升了效率。
客戶端發來http請求,被wsgi服務器處理後傳遞給app的__call__方法。
App中遍歷已經註冊的router ,router的match來判斷本身能不能處理,前綴匹配,看到註冊的規則。(規則是裝飾器已經轉換成了命名分組的正則表達式了)
若是由某一個註冊的正則表達式匹配,就把回去的參數放到request中,調用註冊時候的映射handler傳入request。
Handler處理之後,返回response。App中拿到response的數據,返回給wsgi。
Handler返回的僅僅是數據,將數據填入HTML中,將新生成的HTML字符串返回給客戶端,就是網頁技術。模板技術。
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>
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
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)
將全部的代碼組織成包和模塊。
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>
攔截器是在請求處理的環節的某一處加入處理,有多是中斷後續的處理
分類:鏈式、洋蔥式的。
攔截點不一樣:
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
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
(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>