Django 系列 | 第一讲 Web 应用程序的本质

1.Web应用的本质是什么?

我们知道,在互联网时代,当前软件产品的主流形态是Web应用。思考我们平时通过浏览器上网的过程,如下图:
在这里插入图片描述

  • Web应用程序是一种基于B/S(或C/S)架构的网络应用程序,既然是网络应用程序,那么其背后一定是基于socket网络编程来实现的;
  • Web应用程序作为一种C/S架构的网络应用程序,其客户端是浏览器(Chrome/IE/Firefox等),服务器端是Web服务器;
  • 浏览器与Web服务器之间通信使用的交互语言是HTTP协议(一种应用层协议);
  • 综上,我们可以得出:Web应用本质上就是一个基于B/S架构的网络应用程序(使用了socket网络编程),这个程序包含一个socket服务端,一个socket客户端(即我们用户使用的浏览器)。

2. 我们开发一个Web应用程序,开发的到底是什么?

在知道Web应用程序本质上包含一个socket客户端程序和一个socket服务器端程序,那么当我们说要开发一个Web应用程序的时候,无非也就是去开发一个socket客户端程序,和一个socket服务器端程序。

  • 开发socket 客户端程序(这些浏览器已经全部实现)
  1. 如何建立TCP连接(Socket编程)
  2. 如何封装HTTP请求包?(HTTP 协议相关)
  3. 如何处理解析服务器返回的HTTP响应包?(HTTP 协议相关)
  4. 解析HTTP响应包体数据,渲染成用户所看到的界面(能够解析HTML、CSS、JS等)
  • 开发socket服务器程序?
  1. 如何建立TCP连接(Socket编程)
  2. 如何封装HTTP响应包(HTTP协议相关)
  3. 如何处理不同的请求,返回不同的数据(业务逻辑)
    在这里插入图片描述
  • 区分Web应用程序和Web服务器程序

在这里插入图片描述

3. 什么是Web框架?

通过上述分析,我们得出一个Socket服务器端程序包含两部分:Web服务器程序和Web应用程序。当把socket服务端程序分看做两个部分的时候,一个问题出现了,就是两者之间如何协调工作?自然而然,我们想到的解决方案是指定一个统一的接口标准,大家遵循这个规范即可。这个标准就是WSGI(Web Server Gateway Interface)。

  • Web 服务器程序负责对socket 进行封装,实现传输层TCP服务,应用层HTTP协议数据包的封装和解析。
  • Web应用程序负责具体的业务逻辑处理。

为了提高开发效率和规范,将许多通用的操作封装起来,就出现了许多半成品的Web应用程序(我们称之为Web框架)。

4.自己动手写一个Web框架(使用socket)?

#coding:utf-8
import socket

def do_request(connection):
    buffer = connection.recv(1024)
    connection.send(b"HTTP /1.1 200 OK\r\n\r\n")
    connection.send(b"Hello World")

def server():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 8080))
    sock.listen(5)

    while True:
        connection, address = sock.accept()
        do_request(connection)
        
if __name__ == '__main__':
    server()

5.增加功能:如何实现不同的请求URL,响应不同的数据?

5.1 版本一(初级版本)

#coding:utf-8
import socket

def do_request(connection):
    buffer = connection.recv(1024)

    data = str(buffer, encoding="utf8")  # 把收到的字节类型的数据转换成字符串

    datas = data.split("\r\n")  # 按\r\n分割
    print(datas[0]) # 打印请求行
    url = datas[0].split()[1] # 拿到url地址

    connection.send(b"HTTP /1.1 200 OK\r\n\r\n")

    if url == "/login/":
        response = b"login"
    elif url == "/index/":
        response = b"index"
    else:
        response = b"404 not found"

    connection.send(response)
    connection.close()

def server():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 8080))
    sock.listen(5)

    while True:
        connection, address = sock.accept()
        do_request(connection)

if __name__ == '__main__':
    server()

5.2 版本二(函数初级版本)

  • 不同的url,需要不同的业务处理,因此可以将对具体url的处理封装成函数。
#coding:utf-8
import socket

def do_request(connection):
    buffer = connection.recv(1024)

    data = str(buffer, encoding="utf8")  # 把收到的字节类型的数据转换成字符串

    datas = data.split("\r\n")  # 按\r\n分割
    print(datas[0]) # 打印请求行
    url = datas[0].split()[1] # 拿到url地址

    connection.send(b"HTTP /1.1 200 OK\r\n\r\n")

    if url == "/login/":
        response = b"login"
    elif url == "/index/":
        response = b"index"
    else: 
        response = b"404 not found"

    connection.send(response)
    connection.close()

def server():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 8080))
    sock.listen(5)

    while True:
        connection, address = sock.accept()
        do_request(connection)

if __name__ == '__main__':
    server()

5.3 版本三(函数进阶版本)

  • 若果有很多不同的url, 那么就需要写很多的if判断,这是一个很低效的方法。
  • 我们可以使用一个列表来保存url与处理url函数的映关系。
#coding:utf-8
import socket


def login(url):
    response_data = "hello {}".format(url)
    return bytes(response_data, encoding="utf8")


def index(url):
    response_data = "welcome {}".format(url)
    return bytes(response_data, encoding="utf8")



urlpatterns = [
    ("/login/", login),
    ("/index/", index)
]


def do_request(connection):
    buffer = connection.recv(1024)

    data = str(buffer, encoding="utf8")  # 把收到的字节类型的数据转换成字符串

    datas = data.split("\r\n")  # 按\r\n分割
    print(datas[0]) # 打印请求行
    url = datas[0].split()[1] # 拿到url地址

    connection.send(b"HTTP /1.1 200 OK\r\n\r\n")

    foo = None
    for pattern in urlpatterns:
        if pattern[0] == url:
            foo = pattern[1]
            break

    if foo:
        response = foo(url)
    else:
        response = b"404 not found"

    connection.send(response)
    connection.close()

def server():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 8080))
    sock.listen(5)

    while True:
        connection, address = sock.accept()
        do_request(connection)

if __name__ == '__main__':
    server()

7.增加功能:如何实现不同的请求URL,响应不同的HTML文件?

#coding:utf-8
import socket

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


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



urlpatterns = [
    ("/login/", login),
    ("/index/", index)
]


def do_request(connection):
    buffer = connection.recv(1024)

    data = str(buffer, encoding="utf8")  # 把收到的字节类型的数据转换成字符串

    datas = data.split("\r\n")  # 按\r\n分割
    print(datas[0]) # 打印请求行
    url = datas[0].split()[1] # 拿到url地址

    connection.send(b"HTTP /1.1 200 OK\r\n\r\n")

    foo = None
    for pattern in urlpatterns:
        if pattern[0] == url:
            foo = pattern[1]
            break

    if foo:
        response = foo(url)
    else:
        response = b"404 not found"

    connection.send(response)
    connection.close()

def server():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 8080))
    sock.listen(5)

    while True:
        connection, address = sock.accept()
        do_request(connection)

if __name__ == '__main__':
    server()

8.增加功能:从静态网页到动态网页?

  • 在网页中添加自定义的特殊字符,然后用数据替换
#coding:utf-8
import socket
import time


def login(url):
    with open("login.html", "r", encoding="utf8") as f:
        response_data = f.read()
        current_time = str(time.asctime(time.localtime()))
        response_data = response_data.replace("%%time%%", current_time)
        return bytes(response_data, encoding="utf8")


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



urlpatterns = [
    ("/login/", login),
    ("/index/", index)
]


def do_request(connection):
    buffer = connection.recv(8096)

    data = str(buffer, encoding="utf8")  # 把收到的字节类型的数据转换成字符串

    datas = data.split("\r\n")  # 按\r\n分割
    print(datas[0]) # 打印请求行
    url = datas[0].split()[1] # 拿到url地址

    connection.send(b"HTTP /1.1 200 OK\r\n\r\n")

    foo = None
    for pattern in urlpatterns:
        if pattern[0] == url:
            foo = pattern[1]
            break

    if foo:
        response = foo(url)
    else:
        response = b"404 not found"

    connection.send(response)
    connection.close()

def server():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 8080))
    sock.listen(5)

    while True:
        connection, address = sock.accept()
        do_request(connection)

if __name__ == '__main__':
    server()

9.自己动手写一个Web框架(使用WSGI)?

  • WSGI(Web Server Gateway Interface)是一种规范,它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦。
  • python标准库提供的独立WSGI服务器称为wsgiref。
  • 我们使用wsgiref模块开发一个自己的服务器端,代码如下:
#coding:utf-8

from wsgiref.simple_server import make_server
import time

# 将返回不同的内容部分封装成函数
def login(url):
    with open("login.html", "r", encoding="utf8") as f:
        response_data = f.read()
        current_time = str(time.asctime(time.localtime()))
        response_data = response_data.replace("%%time%%", current_time)
    return bytes(response_data, encoding="utf8")


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


# 定义一个url和实际要执行的函数的对应关系
urlpatterns = [
    ("/index/", index),
    ("/login/", login),
]

def server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 设置HTTP响应的状态码和头信息
    url = environ['PATH_INFO']  # 取到客户端输入的url
    foo = None
    for pattern in urlpatterns:
        if pattern[0] == url:
            foo = pattern[1]
            break
    if foo:
        response = foo(url)
    else:
        response = b"404 not found!"
    return [response, ]


if __name__ == '__main__':
    httpd = make_server('127.0.0.1', 8080, server)
    httpd.serve_forever()

10.自己动手写一个Web框架(使用WSGI + jinja2)?

10.1 数据从一个字典变量中获取

  • 使用开源 jinja2 模块,jinja2 是一个优秀的模板渲染引擎。通过使用 jinja2 特定的语法规则,其内部会对指定的语法进行相应的替换,我们可以实现动态网页内容的返回.
  • 模板渲染的本质原理就是字符串替换(填表格)
  • 安装jinja2 pip install jinja2
#coding:utf-8

from wsgiref.simple_server import make_server
from jinja2 import Template

def login(url):
    with open("login.html", "r", encoding="utf8") as f:
        response_data = f.read()
        template = Template(response_data)           # 生成模板文件
        person_data = {"name": "小明", "age":25, "gender": "男","favoriates": ["阅读", "旅行"]}
        response_data = template.render(person_data)  # 把数据填充到模板里面
        return bytes(response_data, encoding="utf8")


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

urlpatterns = [
    ("/index/", index),
    ("/login/", login),
]


def server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) 
    url = environ['PATH_INFO'] 
    foo = None
    for pattern in urlpatterns:
        if pattern[0] == url:
            foo = pattern[1]
            break
    if foo:
        response = foo(url)
    else:
        response = b"404 not found!"
    return [response, ]


if __name__ == '__main__':
    httpd = make_server('127.0.0.1', 8080, server)
    httpd.serve_forever()
  • login.html 文件代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h2>姓名:{{name}}</h2>
    <h2>年龄:{{age}}</h2>
    <h2>性别:{{gender}}</h2>
    <h2>兴趣:</h2>
    <ol>
        {% for item in favoriates %}
        <li>{{item}}</li>
        {% endfor %}
    </ol>

</body>
</html>

10.2 数据从数据库中获取

  • 创建一个数据库 testdb
CREATE DATABASE testdb;
  • 使用数据库 testdb
USE testdb;
  • 创建一个表 user
CREATE TABLE user(
  id int auto_increment PRIMARY KEY,
  name CHAR(10) NOT NULL,
  age INTEGER NOT NULL,
  gender CHAR(20) NOT NULL,
  favoriates CHAR(20) NOT NULL
)engine=innodb DEFAULT charset=UTF8;
  • 插入数据
INSERT INTO user(name, age, gender, favoriates) VALUES("张三", 25, "男", "阅读 旅行");
INSERT INTO user(name, age, gender, favoriates) VALUES("李四", 23, "女", "体育 旅行");
  • 使用 pymysql 操作数据库
#coding:utf-8

from wsgiref.simple_server import make_server
from jinja2 import Template
import pymysql



def get_data_from_db():
    conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="123", db="testdb", charset="utf8")
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute("select name, age, gender, favoriates from user")
    user_data = cursor.fetchall()
    for idx, record in enumerate(user_data):
        for key,value in record.items():
            if key == "favoriates":
                tmp_list = value.split()
                print(tmp_list)
                user_data[idx]["favorites"] = tmp_list
                break
    cursor.close()
    conn.close()
    return user_data

def login(url):
    with open("login.html", "r", encoding="utf8") as f:
        response_data = f.read()
        template = Template(response_data)           
        user_data = get_data_from_db()
        response_data = template.render(user_data[0])  
        return bytes(response_data, encoding="utf8")

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


# 定义一个url和实际要执行的函数的对应关系
urlpatterns = [
    ("/index/", index),
    ("/login/", login),
]


def handler(environ, start_response):
    # 设置HTTP响应行
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) 
    # 获取客户端的URL 
    url = environ['PATH_INFO']  
    foo = None
    for pattern in urlpatterns:
        if pattern[0] == url:
            foo = pattern[1]
            break
    if foo:
        response = foo(url)
    else:
        response = b"404 not found!"
    return [response, ]


if __name__ == '__main__':
    httpd = make_server('127.0.0.1', 8080, handler)
    httpd.serve_forever()
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页