日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

Python Web开发:开发wsgi中间件

發(fā)布時(shí)間:2023/12/10 python 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python Web开发:开发wsgi中间件 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文參考了:

  • github.com/alanctkc/ws…
  • Youtube : Creating WSGI Middleware

上篇文章簡(jiǎn)要提到:wsgi 規(guī)范中的 app 是一個(gè)可調(diào)用對(duì)象,可以通過嵌套調(diào)用的方式實(shí)現(xiàn)中間件的功能。這篇文章就來親自動(dòng)手實(shí)現(xiàn)一下。

此文的重點(diǎn)在于 app 端,所以 wsgi 服務(wù)器將使用python 內(nèi)置module wsgiref.simple_server 中的make_server。

創(chuàng)建 app

新建文件 app.py :

def application(environ, start_response):"""The web application."""response_body = ""for key, value in environ.items():response_body += "<p>{} : {}\n</p>".format(key, value)# Set up the response status and headersstatus = '200 OK'response_headers = [('Content-Type', 'text/html; charset=utf-8'),('Content-Length', str(len(response_body))),]start_response(status, response_headers)return [response_body.encode('utf-8')]復(fù)制代碼

注意:python3中要求 response_body是 bytes,所以需要 encode()一下。在 python2中是 str,不需要 encode()。

這個(gè) app 做的事情非常簡(jiǎn)單,把傳過來的 environ 原樣返回。在開始返回body 之前,調(diào)用server傳過來的start_response函數(shù)。

簡(jiǎn)要說明一下為什么是 retuen [response_body]而不是 return response_body或者 return response_body.split("\n")或者return response_body.split("")?

  • 首先 wsgi 規(guī)范說明了app返回的是一個(gè)可迭代對(duì)象,列表是可迭代的。
  • 其次,對(duì)于大多數(shù) app 來說,response_body都不會(huì)太長(zhǎng),服務(wù)器的內(nèi)存完成足以一次性裝下,所以最高效的方法就是一次性把response_body全傳過去。

創(chuàng)建 server

新建文件server.py

from wsgiref.simple_server import make_server from app import applicationprint("Server is running at http://localhost:8888 . Press Ctrl+C to stop.") server = make_server('localhost', 8888, application) server.serve_forever()復(fù)制代碼

用瀏覽器打開 http://localhost:8888,就可以看到 environ 的詳細(xì)內(nèi)容。其中比較重要的我用紅框框圈了起來。

第一個(gè)中間件:cors

簡(jiǎn)要了解一下 cors 的機(jī)制(詳細(xì)的要比這個(gè)復(fù)雜點(diǎn)):

如果一個(gè)ajax請(qǐng)求(XMLHttpRequest)是跨域的,比如說在 http://localhost:9000頁(yè)面上向運(yùn)行在http://localhost:8888的服務(wù)器發(fā)起請(qǐng)求,瀏覽器就會(huì)往請(qǐng)求頭上面加上一個(gè)ORIGIN字段,這個(gè)字段的值就是localhost:9000。(對(duì)應(yīng)在app 的 environ 參數(shù)中,就是 HTTP_ORIGIN)

同時(shí),瀏覽器會(huì)先發(fā)出OPTIONS請(qǐng)求,服務(wù)器要實(shí)現(xiàn)這樣的功能:如果想要接收這個(gè)請(qǐng)求的話,需要在response 的 headers里面添加一個(gè)Access-Control-Allow-Origin字段,值就是請(qǐng)求傳過來的那個(gè)ORIGIN。

瀏覽器發(fā)出OPTIONS請(qǐng)求并發(fā)現(xiàn)返回?cái)?shù)據(jù)的 headers 里面有Access-Control-Allow-Origin,才會(huì)進(jìn)行下一步發(fā)出真正的請(qǐng)求:GET,POST,WAHTERVER。

所以,CORS 是瀏覽器和 Server共同協(xié)作來完成的。

看一下代碼:

class CORSMiddleware(object):def __init__(self, app, whitelist=None):"""Initialize the middleware for the specified app."""if whitelist is None:whitelist = []self.app = appself.whitelist = whitelistdef validate_origin(self, origin):"""Validate that the origin of the request is whitelisted."""return origin and origin in self.whitelistdef cors_response_factory(self, origin, start_response):"""Create a start_response method that includes a CORS header for thespecified origin."""def cors_allowed_response(status, response_headers, exc_info=None):"""This wraps the start_response behavior to add some headers."""response_headers.extend([('Access-Control-Allow-Origin', origin)])return start_response(status, response_headers, exc_info)return cors_allowed_responsedef cors_options_app(self, origin, environ, start_response):"""A small wsgi app that responds to preflight requests for thespecified origin."""response_body = 'ok'status = '200 OK'response_headers = [('Content-Type', 'text/plain'),('Content-Length', str(len(response_body))),('Access-Control-Allow-Origin', origin),('Access-Control-Allow-Headers', 'Content-Type'),]start_response(status, response_headers)return [response_body.encode('utf-8')]def cors_reject_app(self, origin, environ, start_response):response_body = 'rejected'status = '200 OK'response_headers = [('Content-Type', 'text/plain'),('Content-Length', str(len(response_body))),]start_response(status, response_headers)return [response_body.encode('utf-8')]def __call__(self, environ, start_response):"""Handle an individual request."""origin = environ.get('HTTP_ORIGIN')if origin:if self.validate_origin(origin):method = environ.get('REQUEST_METHOD')if method == 'OPTIONS':return self.cors_options_app(origin, environ, start_response)return self.app(environ, self.cors_response_factory(origin, start_response))else:return self.cors_reject_app(origin, environ, start_response)else:return self.app(environ, start_response)復(fù)制代碼

__init__方法傳入的參數(shù)有:下一層的 app(回顧一下前面說的 app 是一層一層的,所以能夠?qū)崿F(xiàn)中間件)和 client 白名單,只允許來自這個(gè)白名單內(nèi)的ajax 請(qǐng)求。

__call__方法說明這是一個(gè)可調(diào)用對(duì)象(類也可以是可調(diào)用的),一樣接收兩個(gè)參數(shù):environ和start_response。首先判斷一下 environ 中有沒有HTTP_ORIGIN,有的話就表明屬于跨域請(qǐng)求。如果是跨域,判斷一下 origin 在不咋白名單。如果在白名單里面,如果是 OPTIONS請(qǐng)求,返回cors_options_app里面的對(duì)應(yīng)內(nèi)容(加上了Access-Control-Allow-Origin header);如果不是OPTIONS請(qǐng)求,調(diào)用下一層的 app。如果不在白名單,返回的是cors_reject_app。

修改一下server.py:

app = CORSMiddleware(app=application,whitelist=['http://localhost:9000','http://localhost:9001'] ) server = make_server('localhost', 8000, app) 復(fù)制代碼

測(cè)試 cors app

這里在運(yùn)行三個(gè)客戶端,[代碼在此]。(github.com/liaochangji…)

運(yùn)行python client.py:

在瀏覽器打開http://localhost:9000、http://localhost:9001和http://localhost:9002,可以發(fā)現(xiàn)http://localhost:9000和http://localhost:9001成功發(fā)出了請(qǐng)求,而http://localhost:9002失敗了。

第二個(gè)中間件:請(qǐng)求耗時(shí)

這個(gè)比上一個(gè)要簡(jiǎn)單很多,相信現(xiàn)在你已經(jīng)完全能夠理解了:

import timeclass ResponseTimingMiddleware(object):"""A wrapper around an app to print out the response time for eachrequest."""def __init__(self, app):self.app = appdef __call__(self, environ, start_response):"""Meaure the time spent in the application."""start_time = time.time()response = self.app(environ, start_response)response_time = (time.time() - start_time) * 1000timing_text = "總共耗時(shí): {:.10f}ms \n".format(response_time)response = [timing_text.encode('utf-8') + response[0]]return response 復(fù)制代碼

再修改一下server.py:

app = ResponseTimingMiddleware(CORSMiddleware(app=application,whitelist=['http://localhost:9000','http://localhost:9001']) ) 復(fù)制代碼

再次訪問http://localhost:8000,會(huì)看到最前面打印出了此次請(qǐng)求的耗時(shí):

總結(jié)一下

我手畫了一個(gè)請(qǐng)求圖,希望對(duì)你有所幫助:

本文的所有源代碼開源在 github 上:github.com/liaochangji…

希望能點(diǎn)個(gè) star ~

如果你像我一樣真正熱愛計(jì)算機(jī)科學(xué),喜歡研究底層邏輯,歡迎關(guān)注我的微信公眾號(hào):

總結(jié)

以上是生活随笔為你收集整理的Python Web开发:开发wsgi中间件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。