Django - Web框架本質

1、Web框架本質

咱們能夠這樣理解:全部的Web應用本質上就是一個socket服務端,而用戶的瀏覽器就是一個socket客戶端。 這樣咱們就能夠本身實現Web框架了。html

 

1. 半成品自定義web框架

import socket

sk = socket.socket()
sk.bind(("127.0.0.1", 80))
sk.listen()


while True:
    conn, addr = sk.accept()
    data = conn.recv(8096)
    conn.send(b"OK")
    conn.close() 

 

能夠說Web服務本質上都是在這十幾行代碼基礎上擴展出來的。這段代碼就是它們的祖宗。python

用戶的瀏覽器一輸入網址,會給服務端發送數據,那瀏覽器會發送什麼數據?怎麼發?這個誰來定? 你這個網站是這個規定,他那個網站按照他那個規定,這互聯網還能玩麼?mysql

因此,必須有一個統一的規則,讓你們發送消息、接收消息的時候有個格式依據,不能隨便寫。web

這個規則就是HTTP協議,之後瀏覽器發送請求信息也好,服務器回覆響應信息也罷,都要按照這個規則來。sql

HTTP協議主要規定了客戶端和服務器之間的通訊格式,那HTTP協議是怎麼規定消息格式的呢?數據庫

讓咱們首先打印下咱們在服務端接收到的消息是什麼。django

 

import socket

sk = socket.socket()
sk.bind(("127.0.0.1", 80))
sk.listen()


while True:
    conn, addr = sk.accept()
    data = conn.recv(8096)
    print(data)  # 將瀏覽器發來的消息打印出來
    conn.send(b"OK")
    conn.close()

輸出:瀏覽器

b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nDNT: 1\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv\r\n\r\n'

 

 

而後咱們再看一下咱們訪問博客園官網時瀏覽器收到的響應信息是什麼。服務器

響應相關信息能夠在瀏覽器調試窗口的network標籤頁中看到。session

 

點擊view source以後顯示以下圖:

HTTP協議對收發消息的格式要求

每一個HTTP請求和響應都遵循相同的格式,一個HTTP包含Header和Body兩部分,其中Body是可選的。 HTTP響應的Header中有一個 Content-Type代表響應的內容格式。如 text/html表示HTML網頁。

 

2.HTTP GET請求的格式:

 

3.HTTP響應的格式:

 

4.處女版自定義web框架

通過上面的補充學習,咱們知道了要想讓咱們本身寫的web server端正經起來,必需要讓咱們的Web server在給客戶端回覆消息的時候按照HTTP協議的規則加上響應狀態行,這樣咱們就實現了一個正經的Web框架了。

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('127.0.0.1', 8000))
sock.listen()

while True:
    conn, addr = sock.accept()
    data = conn.recv(8096)
    # 給回覆的消息加上響應狀態行
    conn.send(b"HTTP/1.1 200 OK\r\n\r\n")
    conn.send(b"OK")
    conn.close()

 

5.根據不一樣的路徑返回不一樣的內容

"""
根據URL中不一樣的路徑返回不一樣的內容
"""

import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口
sk.listen()  # 監聽


while 1:
    # 等待鏈接
    conn, add = sk.accept()
    data = conn.recv(8096)  # 接收客戶端發來的消息
    # 從data中取到路徑
    data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0]
    url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
    # 根據不一樣的路徑返回不一樣內容
    if url == "/index/":
        response = b"index"
    elif url == "/home/":
        response = b"home"
    else:
        response = b"404 not found!"

    conn.send(response)
    conn.close()

 

6.根據不一樣的路徑返回不一樣的內容--函數版

"""
根據URL中不一樣的路徑返回不一樣的內容--函數版
"""

import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口
sk.listen()  # 監聽


# 將返回不一樣的內容部分封裝成函數
def index(url):
    s = "這是{}頁面!".format(url)
    return bytes(s, encoding="utf8")


def home(url):
    s = "這是{}頁面!".format(url)
    return bytes(s, encoding="utf8")


while 1:
    # 等待鏈接
    conn, add = sk.accept()
    data = conn.recv(8096)  # 接收客戶端發來的消息
    # 從data中取到路徑
    data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0]
    url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
    # 根據不一樣的路徑返回不一樣內容,response是具體的響應體
    if url == "/index/":
        response = index(url)
    elif url == "/home/":
        response = home(url)
    else:
        response = b"404 not found!"

    conn.send(response)
    conn.close()

  

7.根據不一樣的路徑返回不一樣的內容--函數進階版

"""
根據URL中不一樣的路徑返回不一樣的內容--函數進階版
"""

import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口
sk.listen()  # 監聽


# 將返回不一樣的內容部分封裝成函數
def index(url):
    s = "這是{}頁面!".format(url)
    return bytes(s, encoding="utf8")


def home(url):
    s = "這是{}頁面!".format(url)
    return bytes(s, encoding="utf8")


# 定義一個url和實際要執行的函數的對應關係
list1 = [
    ("/index/", index),
    ("/home/", home),
]

while 1:
    # 等待鏈接
    conn, add = sk.accept()
    data = conn.recv(8096)  # 接收客戶端發來的消息
    # 從data中取到路徑
    data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0]
    url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
    # 根據不一樣的路徑返回不一樣內容
    func = None  # 定義一個保存將要執行的函數名的變量
    for i in list1:
        if i[0] == url:
            func = i[1]
            break
    if func:
        response = func(url)
    else:
        response = b"404 not found!"

    # 返回具體的響應消息
    conn.send(response)
    conn.close()

  

8.返回具體的HTML文件

"""
根據URL中不一樣的路徑返回不一樣的內容--函數進階版
返回獨立的HTML頁面
"""

import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口
sk.listen()  # 監聽


# 將返回不一樣的內容部分封裝成函數
def index(url):
    # 讀取index.html頁面的內容
    with open("index.html", "r", encoding="utf8") as f:
        s = f.read()
    # 返回字節數據
    return bytes(s, encoding="utf8")


def home(url):
    with open("home.html", "r", encoding="utf8") as f:
        s = f.read()
    return bytes(s, encoding="utf8")


# 定義一個url和實際要執行的函數的對應關係
list1 = [
    ("/index/", index),
    ("/home/", home),
]

while 1:
    # 等待鏈接
    conn, add = sk.accept()
    data = conn.recv(8096)  # 接收客戶端發來的消息
    # 從data中取到路徑
    data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0]
    url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
    # 根據不一樣的路徑返回不一樣內容
    func = None  # 定義一個保存將要執行的函數名的變量
    for i in list1:
        if i[0] == url:
            func = i[1]
            break
    if func:
        response = func(url)
    else:
        response = b"404 not found!"

    # 返回具體的響應消息
    conn.send(response)
    conn.close()

  

9.讓網頁動態起來

"""
根據URL中不一樣的路徑返回不一樣的內容--函數進階版
返回HTML頁面
讓網頁動態起來
"""

import socket
import time

sk = socket.socket()
sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口
sk.listen()  # 監聽


# 將返回不一樣的內容部分封裝成函數
def index(url):
    with open("index.html", "r", encoding="utf8") as f:
        s = f.read()
        now = str(time.time())
        s = s.replace("@@oo@@", now)  # 在網頁中定義好特殊符號,用動態的數據去替換提早定義好的特殊符號
    return bytes(s, encoding="utf8")


def home(url):
    with open("home.html", "r", encoding="utf8") as f:
        s = f.read()
    return bytes(s, encoding="utf8")


# 定義一個url和實際要執行的函數的對應關係
list1 = [
    ("/index/", index),
    ("/home/", home),
]

while 1:
    # 等待鏈接
    conn, add = sk.accept()
    data = conn.recv(8096)  # 接收客戶端發來的消息
    # 從data中取到路徑
    data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0]
    url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
    # 根據不一樣的路徑返回不一樣內容
    func = None  # 定義一個保存將要執行的函數名的變量
    for i in list1:
        if i[0] == url:
            func = i[1]
            break
    if func:
        response = func(url)
    else:
        response = b"404 not found!"

    # 返回具體的響應消息
    conn.send(response)
    conn.close()

  

2、服務器程序和應用程序

WSGI(Web Server Gateway Interface)就是一種規範,它定義了使用Python編寫的web應用程序與web服務器程序之間的接口格式,實現web應用程序與web服務器程序間的解耦。

經常使用的WSGI服務器有uwsgi、Gunicorn。而Python標準庫提供的獨立WSGI服務器叫wsgiref,Django開發環境用的就是這個模塊來作服務器。

 

1.Wsgiref

"""
根據URL中不一樣的路徑返回不一樣的內容--函數進階版
返回HTML頁面
讓網頁動態起來
wsgiref模塊版
"""

import time
from wsgiref.simple_server import make_server


# 將返回不一樣的內容部分封裝成函數
def index(url):
    with open("index.html", "r", encoding="utf8") as f:
        s = f.read()
        now = str(time.time())
        s = s.replace("@@oo@@", now)
    return bytes(s, encoding="utf8")


def home(url):
    with open("home.html", "r", encoding="utf8") as f:
        s = f.read()
    return bytes(s, encoding="utf8")


# 定義一個url和實際要執行的函數的對應關係
list1 = [
    ("/index/", index),
    ("/home/", home),
]


def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 設置HTTP響應的狀態碼和頭信息
    url = environ['PATH_INFO']  # 取到用戶輸入的url
    func = None
    for i in list1:
        if i[0] == url:
            func = i[1]
            break
    if func:
        response = func(url)
    else:
        response = b"404 not found!"
    return [response, ]


if __name__ == '__main__':
    httpd = make_server('127.0.0.1', 8090, run_server)
    print("我在8090等你哦...")
    httpd.serve_forever()

  

2.jinjia2

上面的代碼實現了一個簡單的動態,我徹底能夠從數據庫中查詢數據,而後去替換我html中的對應內容,而後再發送給瀏覽器完成渲染。 這個過程就至關於HTML模板渲染數據。 本質上就是HTML內容中利用一些特殊的符號來替換要展現的數據。 我這裏用的特殊符號是我定義的,其實模板渲染有個現成的工具:jinja2

下載jinja2:

pip install jinja2

 

 1 <!DOCTYPE html>
 2 <html lang="zh-CN">
 3 <head>
 4   <meta charset="UTF-8">
 5   <meta http-equiv="x-ua-compatible" content="IE=edge">
 6   <meta name="viewport" content="width=device-width, initial-scale=1">
 7   <title>Title</title>
 8 </head>
 9 <body>
10     <h1>姓名:{{name}}</h1>
11     <h1>愛好:</h1>
12     <ul>
13         {% for hobby in hobby_list %}
14         <li>{{hobby}}</li>
15         {% endfor %}
16     </ul>
17 </body>
18 </html>
index2.html
 1 from wsgiref.simple_server import make_server
 2 from jinja2 import Template
 3 
 4 
 5 def index():
 6     with open("index2.html", "r") as f:
 7         data = f.read()
 8     template = Template(data)  # 生成模板文件
 9     ret = template.render({"name": "Alex", "hobby_list": ["燙頭", "泡吧"]})  # 把數據填充到模板裏面
10     return [bytes(ret, encoding="utf8"), ]
11 
12 
13 def home():
14     with open("home.html", "rb") as f:
15         data = f.read()
16     return [data, ]
17 
18 
19 # 定義一個url和函數的對應關係
20 URL_LIST = [
21     ("/index/", index),
22     ("/home/", home),
23 ]
24 
25 
26 def run_server(environ, start_response):
27     start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 設置HTTP響應的狀態碼和頭信息
28     url = environ['PATH_INFO']  # 取到用戶輸入的url
29     func = None  # 將要執行的函數
30     for i in URL_LIST:
31         if i[0] == url:
32             func = i[1]  # 去以前定義好的url列表裏找url應該執行的函數
33             break
34     if func:  # 若是能找到要執行的函數
35         return func()  # 返回函數的執行結果
36     else:
37         return [bytes("404沒有該頁面", encoding="utf8"), ]
38 
39 
40 if __name__ == '__main__':
41     httpd = make_server('', 8000, run_server)
42     print("Serving HTTP on port 8000...")
43     httpd.serve_forever()
使用jinja2渲染index2.html
1 conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8")
2 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
3 cursor.execute("select name, age, department_id from userinfo")
4 user_list = cursor.fetchall()
5 cursor.close()
6 conn.close()
使用pymysql鏈接數據庫

 

 

3、Django

Django官網下載頁面:https://www.djangoproject.com/download/

 

1.安裝(安裝最新LTS版)

pip3 install django==1.11.9

 

2.建立一個django項目:

下面的命令建立了一個名爲"mysite"的Django 項目:

django-admin startproject mysite

 

3.目錄介紹:

mysite/
├── manage.py  # 管理文件
└── mysite  # 項目目錄
    ├── __init__.py
    ├── settings.py  # 配置
    ├── urls.py  # 路由 --> URL和函數的對應關係
    └── wsgi.py  # runserver命令就使用wsgiref模塊作簡單的web server

 

4.運行Django項目

python manage.py runserver 127.0.0.1:8000

  

5.模板文件配置

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, "template")],  # template文件夾位置
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

  

6.靜態文件配置

STATIC_URL = '/static/'  # HTML中使用的靜態文件夾前綴
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),  # 靜態文件存放位置
]

  

剛開始學習時可在配置文件中暫時禁用csrf中間件,方便表單提交測試。

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

 

4、Django基礎必備三件套

from django.shortcuts import HttpResponse, render, redirect

 

1.HttpResponse

內部傳入一個字符串參數,返回給瀏覽器。

例如:

def index(request):
    # 業務邏輯代碼
    return HttpResponse("OK")

  

2.Render

除request參數外還接受一個待渲染的模板文件和一個保存具體數據的字典參數。

將數據填充進模板文件,最後把結果返回給瀏覽器。(相似於咱們上面用到的jinja2)

例如:

def index(request):
    # 業務邏輯代碼
    return render(request, "index.html", {"name": "alex", "hobby": ["燙頭", "泡吧"]})

  

3.Redirect

接受一個URL參數,表示跳轉到指定的URL。

例如:

def index(request):
    # 業務邏輯代碼
    return redirect("/home/")

 

5、重定向是怎麼回事?

 

6、啓動Django報錯

Django 啓動時報錯 UnicodeEncodeError ...

報這個錯誤一般是由於計算機名爲中文,改爲英文的計算機名重啓下電腦就能夠了。

 

7、Django請求流程

相關文章
相關標籤/搜索