Web應用

Web應用

一 、BS架構引入

undefined

接下來咱們學習的目的是爲了開發一個Web應用程序,而Web應用程序是基於B/S架構的,其中B指的是瀏覽器,負責向S端發送請求信息,而S端會根據接收到的請求信息返回相應的數據給瀏覽器,須要強調的一點是:S端由server和application兩大部分構成,如圖所示:html

上圖:Web應用組成python

undefined

二 、Web應用小練

咱們無需開發瀏覽器(本質即套接字客戶端),只須要開發S端便可,S端的本質就是用套接字實現的,接下來將藉助Python語言手擼一個socket編程web

手擼S端1.0

server.py數據庫

import socket

def make_server(ip, port, app):  # 表明server
    sock = socket.socket()
    sock.bind((ip, port))
    sock.listen(5)
    print('Starting development server at http://%s:%s/' %(ip,port))
    while True:
        conn, addr = sock.accept()

        # 一、接收瀏覽器發來的請求信息
        recv_data = conn.recv(1024)
        # print(recv_data.decode('utf-8'))

        # 二、將請求信息直接轉交給application
        res = app(recv_data)

        # 三、向瀏覽器返回消息(此處並無按照http協議返回)
        conn.send(res)
        
        conn.close()

def app(environ):  # 表明application
    # 處理業務邏輯
    return b'hello world'

if __name__ == '__main__':
    make_server('127.0.0.1', 8008, app)  # 在客戶端瀏覽器輸入:http://127.0.0.1:8008 若是報錯(請注意:須要使用谷歌瀏覽器)

目前Server端已經能夠正常接收瀏覽器發來的請求消息了,可是瀏覽器在接收到S端回覆的響應消息b'hello world'時卻沒法正常解析 ,由於瀏覽器與S端之間收發消息默認使用的應用層協議是HTTP,瀏覽器默認會按照HTTP協議規定的格式發消息,而S端也必須按照HTTP協議的格式回消息才行,因此接下來咱們詳細介紹HTTP協議django

HTTP協議詳解連接地址:HTTP協議詳解連接地址編程

undefined

手擼S端2.0

server.py瀏覽器

import socket

def make_server(ip, port, app): # 表明server
    sock = socket.socket()
    sock.bind((ip, port))
    sock.listen(5)
    print('Starting development server at http://%s:%s/' %(ip,port))
    while True:
        conn, addr = sock.accept()

        # 一、接收並處理瀏覽器發來的請求信息
        # 1.1 接收瀏覽器發來的http協議的消息
        recv_data = conn.recv(1024)

        # 1.2 對http協議的消息加以處理,簡單示範以下
        ll=recv_data.decode('utf-8').split('\r\n')
        head_ll=ll[0].split(' ')
        environ={}
        environ['PATH_INFO']=head_ll[1]
        environ['method']=head_ll[0]

        # 2:將請求信息處理後的結果environ交給application,這樣application便無需再關注請求信息的處理,能夠更加專一於業務邏輯的處理
        res = app(environ)

        # 3:按照http協議向瀏覽器返回消息
        # 3.1 返回響應首行
        conn.send(b'HTTP/1.1 200 OK\r\n')
        # 3.2 返回響應頭(能夠省略)
        conn.send(b'Content-Type: text/html\r\n\r\n')
        # 3.3 返回響應體
        conn.send(res)

        conn.close()

def app(environ): # 表明application
    # 處理業務邏輯
    return b'hello world'

if __name__ == '__main__':
    make_server('127.0.0.1', 8008, app)

此時,重啓S端後,再在客戶端瀏覽器輸入:http://127.0.0.1:8008 即可以看到正常結果hello world了。架構

undefined

咱們不只能夠回覆hello world這樣的普通字符,還能夠夾雜HTML標籤,瀏覽器在接收到消息後會對解析出的HTML標籤加以渲染app

手擼S端3.0

server.py框架

# server.py
import socket

def make_server(ip, port, app): 
    sock = socket.socket()
    sock.bind((ip, port))
    sock.listen(5)
    print('Starting development server at http://%s:%s/' %(ip,port))
    while True:
        conn, addr = sock.accept()
        
        recv_data = conn.recv(1024)
        ll=recv_data.decode('utf-8').split('\r\n')
        head_ll=ll[0].split(' ')
        environ={}
        environ['PATH_INFO']=head_ll[1]
        environ['method']=head_ll[0]

        res = app(environ)

        conn.send(b'HTTP/1.1 200 OK\r\n')
        conn.send(b'Content-Type: text/html\r\n\r\n')
        conn.send(res)

        conn.close()

def app(environ):
    # 返回html標籤
    return b'<h1>hello web</h1><img src="https://www.baidu.com/img/bd_logo1.png"></img>'

if __name__ == '__main__':
    make_server('127.0.0.1', 8008, app)

手擼S端4.0

更進一步咱們還能夠返回一個文件,例如time.html,內容以下

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>{{ time }}</h2>
</body>
</html>

server.py

# S端
import socket

def make_server(ip, port, app): # 表明server
    sock = socket.socket()
    sock.bind((ip, port))
    sock.listen(5)
    print('Starting development server at http://%s:%s/' %(ip,port))
    while True:
        conn, addr = sock.accept()

        recv_data = conn.recv(1024)
        ll=recv_data.decode('utf-8').split('\r\n')
        head_ll=ll[0].split(' ')
        environ={}
        environ['PATH_INFO']=head_ll[1]
        environ['method']=head_ll[0]

        res = app(environ)

        conn.send(b'HTTP/1.1 200 OK\r\n')
        conn.send(b'Content-Type: text/html\r\n\r\n')
        conn.send(res)

        conn.close()

def app(environ):
    # 處理業務邏輯:打開文件,讀取文件內容並返回
    with open('timer.html', 'r', encoding='utf-8') as f:
        data = f.read()
    return data.encode('utf-8')

if __name__ == '__main__':
    make_server('127.0.0.1', 8008, app)

手擼S端終極5.0

上述S端爲瀏覽器返回的都是靜態頁面(內容都固定的),咱們還能夠返回動態頁面(內容是變化的)

server.py

# S端
import socket

def make_server(ip, port, app): # 表明server
    sock = socket.socket()
    sock.bind((ip, port))
    sock.listen(5)
    print('Starting development server at http://%s:%s/' %(ip,port))
    while True:
        conn, addr = sock.accept()

        recv_data = conn.recv(1024)
        ll=recv_data.decode('utf-8').split('\r\n')
        head_ll=ll[0].split(' ')
        environ={}
        environ['PATH_INFO']=head_ll[1]
        environ['method']=head_ll[0]

        res = app(environ)

        conn.send(b'HTTP/1.1 200 OK\r\n')
        conn.send(b'Content-Type: text/html\r\n\r\n')
        conn.send(res)

        conn.close()

def app(environ):
    # 處理業務邏輯
    with open('timer.html', 'r', encoding='utf-8') as f:
        data = f.read()

    import time
    now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    data = data.replace('{{ time }}', now)  # 字符串替換
    return data.encode('utf-8')

if __name__ == '__main__':
    make_server('127.0.0.1', 8008, app) # 在瀏覽器輸入http://127.0.0.1:8008,每次刷新都會看到不一樣的時間

undefined

三 、Web框架的由來

引入框架

綜上案例咱們能夠發現一個規律,在開發S端時,server的功能是複雜且固定的(處理socket消息的收發和http協議的處理),而app中的業務邏輯卻各不相同(不一樣的軟件就應該有不一樣的業務邏輯),重複開發複雜且固定的server是毫無心義的,有一個wsgiref模塊幫咱們寫好了server的功能,這樣咱們便只須要專一於app功能的編寫便可

undefined

繼續擼wsgi

# wsgiref實現了server,即make_server
from wsgiref.simple_server import make_server 

def app(environ, start_response): # 表明application 
    # 一、返回http協議的響應首行和響應頭信息
    start_response('200 OK', [('Content-Type', 'text/html')])
    
    # 二、處理業務邏輯:根據請求url的不一樣返回不一樣的頁面內容
    if environ.get('PATH_INFO') == '/index':
        with open('index.html','r', encoding='utf-8') as f:
            data=f.read()
    elif environ.get('PATH_INFO') == '/timer':
        with open('timer.html', 'r', encoding='utf-8') as f:
            data = f.read()
        import time
        now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        data = data.replace('{{ time }}', now)  # 字符串替換
    else:
        data='<h1>Hello, web!</h1>'
    
    # 三、返回http響應體信息,必須是bytes類型,必須放在列表中
    return [data.encode('utf-8')]

if __name__ == '__main__':
    # 當接收到請求時,wsgiref模塊會對該請求加以處理,而後後調用app函數,自動傳入兩個參數:
    # 1 environ是一個字典,存放了http的請求信息
    # 2 start_response是一個功能,用於返回http協議的響應首行和響應頭信息
    s = make_server('', 8011, app) # 表明server
    print('監聽8011')
    s.serve_forever() # 在瀏覽器輸入http://127.0.0.1:8011/index和http://127.0.0.1:8011/timer會看到不一樣的頁面內容

time.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>主頁</h1>
</body>
</html>

上述案例中app在處理業務邏輯時須要根據不一樣的url地址返回不一樣的頁面內容,當url地址愈來愈多,須要寫一堆if判斷,代碼不夠清晰,耦合程度高,因此咱們作出如下優化

# 處理業務邏輯的函數
def index(environ):
    with open('index.html', 'r', encoding='utf-8') as f:
        data = f.read()
    return data.encode('utf-8')


def timer(environ):
    import datetime
    now = datetime.datetime.now().strftime('%y-%m-%d %X')
    with open('timer.html', 'r', encoding='utf-8') as f:
        data = f.read()
    data = data.replace('{{ time }}', now)
    return data.encode('utf-8')


# 路徑跟函數的映射關係
url_patterns = [
    ('/index', index),
    ('/timer', timer),
]

from wsgiref.simple_server import make_server


def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])

    # 拿到請求的url並根據映射關係url_patters執行相應的函數
    reuqest_url = environ.get('PATH_INFO')
    for url in url_patterns:
        if url[0] == reuqest_url:
            data = url[1](environ)
            break
    else:
        data = b'404'

    return [data]


if __name__ == '__main__':
    s = make_server('', 8011, app)
    print('監聽8011')
    s.serve_forever()

Django目錄小窺

隨着業務邏輯複雜度的增長,處理業務邏輯的函數以及`url_patterns中的映射關係都會不斷地增多,此時仍然把全部代碼都放到一個文件中,程序的可讀性和可擴展性都會變得很是差,因此咱們應該將現有的代碼拆分到不一樣文件中

插圖 :

undefined

view.py內容

# 處理業務邏輯的函數
def index(environ):
    with open('templates/index.html', 'r',encoding='utf-8') as f: # 注意文件路徑
        data = f.read()
    return data.encode('utf-8')

def timer(environ):
    import datetime
    now = datetime.datetime.now().strftime('%y-%m-%d %X')
    with open('templates/timer.html', 'r',encoding='utf-8') as f: # 注意文件路徑
        data = f.read()
    data=data.replace('{{ time }}',now)
    return data.encode('utf-8')

urls.py內容

# 路徑跟函數的映射關係
from app01.views import * # 須要導入views中的函數

url_patterns = [
    ('/index', index),
    ('/timer', timer),
]

main.py內容以下

from wsgiref.simple_server import make_server
from mysite.urls import url_patterns  # 須要導入urls中的url_patterns


def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])

    # 拿到請求的url並根據映射關係url_patters執行相應的函數
    reuqest_url = environ.get('PATH_INFO')
    for url in url_patterns:
        if url[0] == reuqest_url:
            data = url[1](environ)
            break
    else:
        data = b'404'

    return [data]


if __name__ == '__main__':
    s = make_server('', 8011, app)
    print('監聽8011')
    s.serve_forever()

至此,咱們就針對application的開發自定義了一個框架,因此說框架的本質就是一系列功能的集合體、不一樣的功能放到不一樣的文件中。有了該框架,可讓咱們專一於業務邏輯的編寫,極大的提升了開發web應用的效率(開發web應用的框架能夠簡稱爲web框架),好比咱們新增一個業務邏輯,要求爲:瀏覽器輸入http://127.0.0.1:8011/home 就能訪問到home.html頁面,在框架的基礎上具體開發步驟以下:

步驟一:在templates文件夾下新增home.html

步驟二:在urls.pyurl_patterns中新增一條映射關係

url_patterns = [
    ('/index', index),
    ('/timer', timer),
    ('/home', home), # 新增的映射關係
]

步驟三:在views.py中新增一個名爲home的函數

def home(environ):
    with open('templates/home.html', 'r',encoding='utf-8') as f: 
        data = f.read()
    return data.encode('utf-8')

咱們自定義的框架功能有限,在Python中咱們可使用別人開發的、功能更強大的Django框架

undefined

四 、Django框架的安裝與使用

在使用Django框架開發web應用程序時,開發階段一樣依賴wsgiref模塊來實現Server的功能,咱們使用Django框架是爲了快速地開發application

undefined

安裝

目前在企業開發中Django框架使用的主流版本爲1.11.x版本,最新版本爲2.x,咱們主要講解1.11版本,同時會涉及2.x的新特性,2019年11月中旬Django3.0 beta已經到來

pip3 install django==1.11.18 # 在命令行執行該命令

使用

建立項目並啓動

若是使用的是咱們自定義的框架來開發web應用,須要事先生成框架包含的一系列基礎文件,而後在此基礎上進行開發。

若是使用的是Django框架來開發web應用,一樣須要事先生成Django框架包含的一系列基礎文件,而後在此基礎上進行開發。

Django框架更爲方便的地方在於它已經爲咱們提供了一系列命令來幫咱們快速地生成這一系列基礎文件

# 在命令行執行如下指令,會在當前目錄生成一個名爲mysite的文件夾,該文件夾中包含Django框架的一系列基礎文件
django-admin startproject mysite

建立功能模塊

cd mysite # 切換到mysite目錄下,執行如下命令
python manage.py startapp app01 # 建立功能模塊app01,此處的startapp表明建立application下的一個功能模塊。例如咱們要開發application是京東商城,京東商城這個大項目下有一個訂單管理模塊,咱們能夠將其命名爲app01

運行

python manage.py runserver 8001 # 在瀏覽器輸入:http://127.0.0.1:8001 會看到Django的歡迎頁面。

項目目錄與結構

undefined

截目錄樹的圖(按照下述目錄截圖)

mysite # 文件夾
    ├── app01 # 文件夾
    │   └── migrations # 文件夾
    │   └── admin.py
    │   └── apps.py
    │   └── models.py
    │   └── tests.py
    │   └── views.py
    ├── mysite # 文件夾
    │   └── settings.py
    │   └── urls.py
    │   └── wsgi.py
    └── templates # 文件夾
    ├── manage.py

關鍵文件介紹

-manage.py---項目入口,執行一些命令
-項目名
    -settings.py  全局配置信息
    -urls.py      總路由,請求地址跟視圖函數的映射關係
-app名字
    -migrations   數據庫遷移的記錄
    -models.py    數據庫表模型
    -views.py     處理業務邏輯的函數,簡稱視圖函數

基於`Pycharm建立Django項目

undefined

基於Django實現的一個簡單示例

url.py路由
from django.contrib import admin
from django.conf.urls import url
#導入views模塊
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    
    # r'^index/$' 會正則匹配url地址的路徑部分
    url(r'^index/$',views.index), # 新增地址http://127.0.0.1:8001/index/與index函數的映射關係
]
視圖
from django.shortcuts import render

# 必須定義一個request形參,request至關於咱們自定義框架時的environ參數
def index(request):
    import datetime
    now=datetime.datetime.now()
    ctime=now.strftime("%Y-%m-%d %X")

    return render(request,"index.html",{"ctime":ctime}) # render會讀取templates目錄下的index.html文件的內容而且用字典中的ctime的值替換模版中的{{ ctime }}
模版

在templates目錄下新建文件index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h4>當前時間:{{ ctime }}</h4>

</body>
</html>

測試

python manage.py runserver 8001 # 在瀏覽器輸入:http://127.0.0.1:8001/index/ 會看到當前時間。

Django框架的分層與請求生命週期

綜上,咱們使用Django框架就是爲了開發application,而application的工做過程本質就是根據不一樣的請求返回不一樣的數據,Django框架將這個工做過程細分爲以下四層去實現

  1. 路由層(根據不一樣的地址執行不一樣的視圖函數,詳見urls.py
  2. 視圖層(定義處理業務邏輯的視圖函數,詳見views.py
  3. 模型層 (跟數據庫打交道的,詳解models.py
  4. 模板層(待返回給瀏覽器的html文件,詳見templates

django請求生命週期

undefined

這體現了一種解耦合的思想,下面咱們開始深刻每一層

undefined

相關文章
相關標籤/搜索