WEB開發和WSGI--Django播客系統(一)

DJango項目-1.WEB開發和WSGI

wsgi_001

  • CS即客戶端、服務器編程。
    1. 客戶端、服務端之間需要使用Socket,約定協議、版本(往往使用的協議是TCP或者UDP),指定地址和端口,就可以通信了。
    2. 客戶端、服務端傳輸數據,數據可以有一定的格式,雙方必須先約定好。

001

  • BS編程,即Browser、Server開發
    1. Brower瀏覽器,一種特殊的客戶端,支持HTTP(s)協議,能夠通過URL向服務端發起請求,等待服務端返回HTML等數據,並在瀏覽器內可視化展示的程序。
    2. Server,支持HTTP(s)協議,能夠接受衆多客戶端發起的HTTP協議請求,經過處理,將HTML等數據返回給瀏覽器。
  • 本質上來說,BS是一種特殊的CS,即客戶端必須是一種支持HTTP協議且能解析並渲染HTML的軟件,服務端必須能夠接收多客戶端HTTP訪問的服務器軟件。
  • HTTP協議底層基於TCP協議實現。
  1. BS開發分爲端開發
    • 客戶端開發:或稱前端開發。HTML、CSS、JavaScript等。
    • 服務端開發:Python有WSGI、Django、Flask、Tornado等。

HTTP協議

安裝httpd

  • 可以安裝httpd或nginx等服務端服務程序,通過瀏覽器訪問,觀察http協議

無狀態,有鏈接和短連接

  • 無狀態:指的是服務器無法知道2次請求之間的聯繫,即使是前後2次同一個瀏覽器也沒有任何數據判斷出時同一個瀏覽器的請求。後來可以通過cookie、session來判斷。
  • 有鏈接:是因爲它基於TCP協議,是面向鏈接的,需要3次握手、4次揮手。
  • 短連接:Http 1.1之前,都是一個請求一個連接,而TCP的鏈接創建銷燬成本高,對服務器有很大影響。所以,自Http 1.1開始,支持keep-alive,默認也開啓,一個連接打開後,會保持一段時間(可設置),瀏覽器再訪問該服務器就使用這個TCP鏈接,減輕了服務器壓力,提高了效率。

協議

Http協議是無狀態協議。
同一個客戶端的兩次請求之間沒有任何關係,從服務器端角度來說,它不知道這兩個請求來自同一個客戶端。

URL組成

  • URL可以說就是地址,uniform resource locator統一資源定位符,每一個連接指向一個資源供客戶端訪問。schema://host[:port#]/path/.../[;url-params][?query-string][#anchor]

  • 例如,通過http://www.xdd.com/pathon/index.html?id=5&name=python訪問靜態資源時,通過上面這個URL訪問的是網站的某路徑下的index.html文件,而這個文件對應磁盤上的真實的文件。就會從磁盤上讀取這個文件,並把文件的內容發回瀏覽器端。

  1. scheme模式、協議
    • http、ftp、https、mailto等等。
  2. host:port
    • www.xdd.com:80,80端口是默認端口可以不寫,域名會使用DNS解析,域名會解析成IP才能使用。實際上會對解析返回的IP的TCP的80端口發起訪問。
  3. /path/to/resource
    • path,指向資源的路徑。
  4. ?key1=value1&key2=value2
    • query string,查詢字符串,問號用來和路徑分開,後面key=value形式,且使用&符號分割。

HTTP消息

  • 消息分爲Request、Response

    1. Request:瀏覽器向服務器發起的請求
    2. Response:服務器對客戶端請求的響應
      • 請求報文由Header消息報頭、Body消息正文組成(可選)
        • 請求報文第一行稱爲請求行
      • 響應報文由Header消息報頭、Body消息正文組成(可選)
        • 響應報頭第一行稱爲狀態行
      • 每一次使用回車和換行符作爲結尾
      • 如果有Body部分,Header、Body之間留一行空行
  • 請求報文

    1. 請求消息行:請求放Method請求路徑協議版本CRLF
      wsgi_003
    2. 請求方法Method
    方法 說明
    GET 請求獲取URL對應的資源
    POST 請求數據至服務端
    HEAD 和GET類似,不過不返回響應報文的正文
  • 常見的消息傳遞方式

    1. GET方法使用Query String。例如:http://www.xdd.com/pathon/index.html?id=5&name=python&name=linux通過查詢字符串在URL中傳遞參數,而URL在請求報文的頭部第一行

    2. POST方法提交數據

      請求消息如下
      POST /xxx/yyy?id=5&name=magedu HTTP/1.1
      HOST: 127.0.0.1:9999
      content-length: 26
      content-type: application/x-www-form-urlencoded
      
      age=5&weight=80&height=170
      

      請求時提交的數據是在請求報文的正文Body部分。

    3. URL中本身就包含信息
      http://www.xdd.com/python/student/001

  • 響應報文

    1. 響應消息行:協議版本 狀態碼 消息描述CRLF
      004
  • status code狀態碼

    1. 狀態碼在響應頭第一行
      • 1xx 提示信息,表示請求已被成功接收,繼續處理
      • 2xx 表示正常響應
        • 200 表示返回了網頁內容,即請求成功
      • 3xx 重定向
        • 301 頁面永久性移走,永久重定向。返回新的URL,瀏覽器會根據返回的url發起新的request請求
        • 302 臨時重定向
        • 304 資源未修改,瀏覽器使用本地緩存
      • 4xx 客戶端請求錯誤
        • 404 Not Found,問也找不到,客戶端請求的資源有錯
        • 400 請求語法錯誤
        • 401 請求要求身份驗證
          8 403 服務器拒絕請求
      • 5xx 服務器端錯誤
        • 500 服務器內部錯誤
        • 502 上游服務器錯誤,例如nginx反向代理的時候

Cookie技術

  • 鍵值對信息
  • 是一種客戶端、服務器端傳遞數據的技術
  • 一般來說cookie信息時在服務器端生成,返回給瀏覽器端的
  • 瀏覽器端可以保持這些值,瀏覽器對同一域發起每一請求時,都會吧Cookie信息發給服務器端
  • 服務端收到瀏覽器端發過來的Cookie,處理這些信息,可以用來判斷這次請求是否和之前的請求有關聯

曾經Cookie唯一在瀏覽器端存儲數據的手段,目前瀏覽器端存儲數據的方案很多,Cookie正在被淘汰。
當服務器收到HTTP請求時,服務器可以在響應頭裏面添加一個Set-Cookie鍵值對。瀏覽器收到響應後通常會保存這些Cookie,之後對該服務器每一次請求中都通過Cookie請求頭部將Cookie信息發送給服務器。
另外,Cookie的過期時間、域、路徑、有效期、適用站點都可以根據需要來指定。
可以使用Set-Cookie: NAME=VALUE;Expires=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE
例如:

Set-Cookie:aliyungf_tc=AQAAAJDwJ3Bu8gkAHbrHb4zlNZGw4Y; Path=/; HttpOnly
set-cookie:test_cookie=CheckForPermission; expires=Tue, 19-Mar-2018 15:53:02 GMT; path=/;
domain=.doubleclick.net

Set-Cookie: BD_HOME=1; path=/
key value說明
Cookie過期 Cookie可以設定過期終止時間,過期後將被瀏覽器清除。
如果缺省,Cookie不會持久化,瀏覽器關閉Cookie消失,稱爲會話級Cookie
Cookie域 域確定有哪些預可研存取這個Cookie.
缺省設置屬性值爲當前主機,例如www.xdd.com
如果設置爲xdd.com表示包含子域
Path 確定哪些目錄及子目錄訪問可以使用該Cookie
Secure 表示Cookie隨着HTTPS加密過的請求發送給服務端
有些瀏覽器已經不允許http://協議使用Secure
這個Secure不能保證Cookie是安全的,Cookie中不要傳輸敏感信息
HttpOnly 將Cookie設置此標記,就不能被JavaScript訪問,只能發給服務器端

set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
告訴瀏覽器端設置這個Cookie的鍵值對,有過期時間,使用HTTPS加密傳輸到服務端,且不能被瀏覽器中JS腳本訪問該Cookie

  • Cookie的作用域:Domain和Path定義Cookie的作用域
Domain
domain=www.xdd.com 表示只有該域的URL才能使用
domain=xdd.com 表示可以包含子域,例如www.xdd.com、python.xdd.com等

Path
path=/ 所有/的子路徑可以使用
domain=www.xdd.com; path=/webapp 表示只有www.xdd.com/webapp下的URL匹配,例如http://www.xdd.com/webapp/a.html就可以
  • 缺點
    1. Cookie一般明文傳輸(Secure是加密傳輸),安全性極差,不要傳輸敏感數據
    2. 有4kB大小限制
    3. 每次請求中都會發生Cookie,增加流量。

Session技術

WEB服務器端,尤其是動態網頁服務端Server,有時需要知道瀏覽器方式誰?但是HTTP是無狀態的。爲了解決。可以如下:

  • 服務器端會爲每一次瀏覽器端第一次訪問生成一個SessionID,用來唯一標識該瀏覽器,通過Set-Cookie發送到瀏覽器端。
  • 瀏覽器端收到之後並不永久保持這個Cookie,只是會話級的。瀏覽器訪問服務端時,會使用Cookie,也會帶上這個SessionID的Cookie值。

Set-Cookie:JSESSIONID=741248A52EEB83DF182009912A4ABD86.Tomcat1; Path=/; HttpOnly

  • 服務端會位置這個SessionIDy一段時間,如果超時,會清理這些超時沒有人訪問的SessionID。如果瀏覽器端發來的SessionID無法在服務端找到,就會自動再分配新的SessionID,並通過Set-Cookie發送到瀏覽器端以覆蓋原有的存在瀏覽器中 的會話級的SessionID。
  • 推薦圖書《HTTP權威指南》

WSGI

005

  • WSGI(Web Server Gateway Interface)主要規定了服務器端和應用程序間的接口。
# 啓動一個WSGI服務器
wsgiref.simple_server.make_server(host,port,app,server_class=WSGIServer,handler_class=WSGIRequestHandler)

# 一個兩參數函數,小巧完整的WSGI的應用程序的實現
wsgiref.simple_server.demo_app(eviron,start_response)

# 返回文本例子
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() # server.handle_request()執行一次

006

  • WSGI服務器作用
    1. 監聽HTTP服務端口(TCPServer,默認端口80)
    2. 接收瀏覽器端的HTTP請求並解析封裝成environ環境數據
    3. 負責調用應用程序,將environ數據和start_response
    4. 將應用程序響應的正文封裝成HTTP響應報文返回瀏覽器端

WSGI APP應用程序端

  1. 應用程序應該是一個可調用對象,在Python中應該是函數、類、實現了__call__方法的類的實例

  2. 這個可調用對象應該接收兩個參數

    # 1. 函數實現
    def application(environ,start_response):
        pass
    
    # 2.類實現
    class Application:
        def __init__(self,environ,start_response):
            pass
    
    # 3. 類的實例實現
    class Application:
        def __call__(self,environ,start_response):
            pass
    
  3. 以上的可調用對象實現,都必須返回一個可迭代對象

from wsgiref.simple_server import make_server,demo_app

ip = '127.0.0.1'
port = 9999
res_str = b'www.xdd.com abc\n'

# 1.函數實現
def application(environ,start_response):
    start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')])
    return [res_str]

# 2. 類實現
class Application:
    def __init__(self,environ,start_response):
        self.env = environ
        self.start_response = start_response

    def __iter__(self):
        self.start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')])
        yield res_str

# 3. 類的對象,可調用對象
class Application2:
    def __call__(self, environ,start_response):
        start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')])
        return [res_str]

# server = make_server(ip,port,application #函數實現
# server = make_server(ip,port,Application) # 類實現
server = make_server(ip,port,Application2()) # demo_app應用程序,可調用對象
server.serve_forever()
# server.handle_request() # 執行一次
  • environ和start_response這兩個參數名可以是任何合法名,但是一般默認都是這2個名字。
  • 注意:第2、第3種實現調用時的不同
  1. environ參數

    • environ包含HTTP請求信息的dict字典對象
    名稱 含義
    REQUEST_METHOD 請求方法,GET、POST等
    PATH_INFO URL中的路徑部分
    QUERY_STRING 查詢字符串
    SERVER_NAME,SERVER_PRORT 服務器名,端口
    HTTP_HOST 地址和端口
    SERVER_PROTOCOL 協議
    HTTP_USER_AGENT UserAgent信息
    CONTENT_TYPE = 'text/plain'
    HTTP_HOST = '127.0.0.1:9999'
    HTTP_USER_AGENT = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN) AppleWebKit/537.36 (KHTML, like Gecko) Version/5.0.1 Safari/537.36'
    PATH_INFO = '/'
    QUERY_STRING = ''
    REMOTE_ADDR = '127.0.0.1'
    REMOTE_HOST = ''
    REQUEST_METHOD = 'GET'
    SERVER_NAME = 'DESKTOP-D34H5HF'
    SERVER_PORT = '9999'
    SERVER_PROTOCOL = 'HTTP/1.1'
    SERVER_SOFTWARE = 'WSGIServer/0.2'
    
  2. start_response參數

    • 它是一個可調用對象。有3個參數,定義如下:start_response(status,response_headers,exc_info=None)
    參數名稱 說明
    status 狀態碼和狀態描述,例如200 ok
    response_headers 一個元素爲二元組的列表,例如[('Content-Type','text/plain;charset=utf-8')]
    exc_info 在錯誤處理的時候使用
    • start_response應該在返回可迭代對象之前調用,因爲它返回的是Response Header。返回的可迭代對象是Response Body。

服務器端

  • 服務器程序需要調用複合上述定義的可調用對象APP,傳入environ、start_response,APP處理後,返回響應頭和可迭代對象的正文,由服務器封裝返回瀏覽器端。
import wsgiref.simple_server

def application(environ,start_response):
    status = '200 OK'
    headers = [('Content-Type','text/html;charset=utf-8')]
    start_response(status,headers)
    # 返回可迭代對象
    html = '<hq>xdd歡迎你</h1>'.encode('utf-8')
    return [html]

ip = '127.0.0.1'
port = 9999
server = wsgiref.simple_server.make_server(ip,port,application) # demo_app應用程序,可調用對象
server.serve_forever()
# server.handle_request() # 執行一次
  • simple_server 只是參考用,不能用於生產環境。
  1. 測試用命令
$ curl -I http://127.0.0.1:9999/xxx?id=5
$ curl -X POST http://127.0.0.1:9999/yyy -d '{"x":2}'
  • -I使用HEAD方法
  • -X指定方法,-d傳輸數據
  1. WSGI WEB服務器
    • 本質上就是一個TCP服務器,監聽在特定端口上
    • 支持HTTP協議,能夠將HTTP請求報文進行解析,能夠把響應數據進行HTTP協議的報文封裝並返回瀏覽器端。
    • 實現了WSGI協議,該協議約定了和應用程序之間接口(參看PEP333,https://www.python.org/dev/peps/pep-0333/)
  2. WSGI APP應用程序
    • 遵從WSGI協議
    • 本身是一個可調用對象
    • 調用start_response,返回響應頭部
    • 返回包含正文的可迭代對象
  3. WSGI框架庫往往可以看做增強的更加複雜的Application。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章