Django基础一之web框架的认识

一 自己制作web框架

我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端,基于请求做出响应,客户都先请求,服务端做出对应的响应,按照http协议的请求协议发送请求,服务端按照http协议的响应协议来响应请求,这样的网络通信,我们就可以自己实现Web框架了。

  通过对socket的学习,我们知道网络通信,我们完全可以自己写了,因为socket就是做网络通信用的,下面我们就基于socket来自己实现一个web框架,写一个web服务端,让浏览器来请求,并通过自己的服务端把页面返回给浏览器,浏览器渲染出我们想要的效果,废话少说,直接上代码:

import time
import socket
from threading import Thread
server = socket.socket()
server.bind(('0.0.0.0',8001))#绑定本机端口
server.listen()#监听


def html(conn):
    conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n')#按照http协议,每次发消息之前都先发送一条响应头
    # conn.send(b'who are you?')
    with open('01 index2.html', 'rb') as f:#打开我们想渲染的html页面
        data = f.read()#此处是一次性读出来的,也可一行行读一行行发
    conn.send(data)
    conn.close()#一次发送完毕就把这个连接关掉,因为http是无状态无连接的
def css(conn):
    conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n')#按照http协议,每次发消息之前都先发送一条响应头
    # conn.send(b'who are you?')
    with open('index.css', 'rb') as f:#此处是因为我们发的html页面中有浏览器渲染时需要的文件,只能向我们请求,我们才能给
        data = f.read()
    conn.send(data)
    conn.close()

def js(conn):
    conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n')#按照http协议,每次发消息之前都先发送一条响应头
    # conn.send(b'who are you?')
    with open('index.js', 'rb') as f:#此处是因为我们发的html页面中有浏览器渲染时需要的文件,只能向我们请求,我们才能给
        data = f.read()
        data = f.read()
    conn.send(data)
    conn.close()

def ico(conn):
    conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n')#按照http协议,每次发消息之前都先发送一条响应头
    # conn.send(b'who are you?')
    with open('favicon.ico', 'rb') as f:#此处是因为我们发的html页面中有浏览器渲染时需要的文件,只能向我们请求,我们才能给
        data = f.read()
        data = f.read()
    conn.send(data)
    conn.close()
def jpg(conn):
    conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n')#按照http协议,每次发消息之前都先发送一条响应头
    # conn.send(b'who are you?')

    with open('1.jpg', 'rb') as f:#此处是因为我们发的html页面中有浏览器渲染时需要的文件,只能向我们请求,我们才能给
        data = f.read()
        data = f.read()
    conn.send(data)
    conn.close()

def home(conn):
    conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n')#按照http协议,每次发消息之前都先发送一条响应头
    current_time = str(time.time())
    # conn.send(b'who are you?')
    with open('home.html', 'r',encoding='utf-8') as f:#此处是因为我们发的html页面中有浏览器渲染时需要的文件,只能向我们请求,我们才能给
        data = f.read()
        data = f.read()
        data = data.replace('xxoo',current_time)#此处是把另一个html的字符串特定内容(这是我们自己定的协议)替换成我们想在浏览器显示的内容---动态页面的雏形

    conn.send(data.encode('utf-8'))
    conn.close()


urlpatterns = [      #利用列表中加成对的元祖,造成映射关系,遍历列表按照索引取值加以判断,再关联其相应函数的操作
    ('/',html),
    ('/index.css',css),
    ('/index.js',js),
    ('/favicon.ico',ico),
    ('/1.jpg',jpg),
    ('/home.html',home),
]


def communication(conn):#此为逻辑关系总控制,通过对请求文件的操作分析,筛选需要的信息
    client_msg = conn.recv(1024).decode('utf-8')
    # print(client_msg)
    path = client_msg.split('\r\n')[0].split(' ')[1]#对请求文件操作

    #针对不同的请求路径,返回不同的文件
    for urlpattern in urlpatterns:
        print(path)
        if urlpattern[0] == path:
            # urlpattern[1](conn)
            # 多线程执行函数
            t = Thread(target=urlpattern[1],args=(conn,))
            t.start()

while 1:
    conn, add = server.accept()#一旦有来连接的客户端即接受
    t = Thread(target=communication,args=(conn,))
    t.start()
web框架本质

我们发现,自己做的建议框架,很多东西都在重复,同时存在很多漏洞与bug,需要再完善优化,总体而言对他的大方向是把握住了,下面看一个wsgiref模块版web框架。

二wsgiref模块版web框架

         最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。

         wsgiref模块其实就是将整个请求信息给封装了起来,就不需要你自己处理了,假如它将所有请求信息封装成了一个叫做request的对象,那么你直接request.path就能获取到用户这次请求的路径,request.method就能获取到本次用户请求的请求方式(get还是post)等,那这个模块用起来,我们再写web框架是不是就简单了好多啊。

    对于真实开发中的python web程序来说,一般会分为两部分:服务器程序应用程序

    服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。

    应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。

    正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口协议来实现这样的服务器软件,让我们专心用Python编写Web业务。

    这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。

    WSGI(Web Server Gateway Interface)就是一种规范,它定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦。

    常用的WSGI服务器有uwsgi、Gunicorn。Python标准库提供的独立WSGI服务器叫wsgirefDjango开发环境用的就是这个模块来做服务器。

    好,接下来我们就看一下(能理解就行,了解就可以了):先看看wsfiref怎么使用

from wsgiref.simple_server import make_server
# wsgiref本身就是个web框架,提供了一些固定的功能(请求和响应信息的封装,不需要我们自己写原生的socket了也不需要咱们自己来完成请求信息的提取了,提取起来很方便)
#函数名字随便起
def application(environ, start_response):
    '''
    :param environ: 是全部加工好的请求信息,加工成了一个字典,通过字典取值的方式就能拿到很多你想要拿到的信息
    :param start_response: 帮你封装响应信息的(响应行和响应头),注意下面的参数
    :return:
    '''
    start_response('200 OK', [('Content-Type', 'text/html'),('k1','v1')])
    print(environ)
    print(environ['PATH_INFO'])  #输入地址127.0.0.1:8000,这个打印的是'/',输入的是127.0.0.1:8000/index,打印结果是'/index'
    return [b'<h1>Hello, web!</h1>']

#和咱们学的socketserver那个模块很像啊
httpd = make_server('127.0.0.1', 8080, application)

print('Serving HTTP on port 8080...')
# 开始监听HTTP请求:
httpd.serve_forever()
wsgiref使用

来一个完整的web项目,用户登录认证的项目,我们需要连接数据库了,所以先到mysql数据库里面准备一些表和数据

mysql> create database db1;
Query OK, 1 row affected (0.00 sec)

mysql> use db1;
Database changed
mysql> create table userinfo(id int primary key auto_increment,username char(20) not null unique,password char(20) not null);
Query OK, 0 rows affected (0.23 sec)

mysql> insert into userinfo(username,password) values('chao','666'),('sb1','222');
Query OK, 2 rows affected (0.03 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from userinfo;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | chao     | 666      |
|  2 | sb1      | 222      |
+----+----------+----------+
2 rows in set (0.00 sec)
View Code

然后再创建这么几个文件:

    python文件名称webmodel.py,内容如下:

#创建表,插入数据
def createtable():
    import pymysql
    conn = pymysql.connect(
        host='127.0.0.1',
        port=3306,
        user='root',
        password='666',
        database='db1',
        charset='utf8'
    )
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    sql = '''
        -- 创建表
        create table userinfo(id int primary key auto_increment,username char(20) not null unique,password char(20) not null);
        -- 插入数据
        insert into userinfo(username,password) values('chao','666'),('sb1','222');
    '''
    cursor.execute(sql)
    conn.commit()
    cursor.close()
    conn.close()

python的名为webauth文件,内容如下:

#对用户名和密码进行验证
def auth(username,password):
    import pymysql
    conn = pymysql.connect(
        host='127.0.0.1',
        port=3306,
        user='root',
        password='123',
        database='db1',
        charset='utf8'
    )
    print('userinfo',username,password)
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    sql = 'select * from userinfo where username=%s and password=%s;'
    res = cursor.execute(sql, [username, password])
    if res:
        return True
    else:
        return False
View Code

用户输入用户名和密码的文件,名为web.html,内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--如果form表单里面的action什么值也没给,默认是往当前页面的url上提交你的数据,所以我们可以自己指定数据的提交路径-->
<!--<form action="http://127.0.0.1:8080/auth/" method="post">-->
<form action="http://127.0.0.1:8080/auth/" method="get">
    用户名<input type="text" name="username">
    密码 <input type="password" name="password">
    <input type="submit">
</form>

<script>

</script>
</body>
</html>
View Code

用户验证成功后跳转的页面,显示成功,名为websuccess.html,内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        h1{
            color:red;
        }
    </style>
</head>
<body>
<h1>宝贝儿,恭喜你登陆成功啦</h1>


</body>
</html>
View Code

python服务端代码(主逻辑代码),名为web_python.py:

from urllib.parse import parse_qs
from wsgiref.simple_server import make_server
import webauth
def application(environ, start_response):

    # start_response('200 OK', [('Content-Type', 'text/html'),('k1','v1')])
    # start_response('200 OK', [('Content-Type', 'text/html'),('charset','utf-8')])
    start_response('200 OK', [('Content-Type', 'text/html')])
    print(environ)
    print(environ['PATH_INFO'])
    path = environ['PATH_INFO']
    #用户获取login页面的请求路径
    if path == '/login':
        with open('web.html','rb') as f:
            data = f.read()
    #针对form表单提交的auth路径,进行对应的逻辑处理
    elif path == '/auth/':
        #登陆认证
        #1.获取用户输入的用户名和密码

        #2.去数据库做数据的校验,查看用户提交的是否合法
        # user_information = environ['']
        if environ.get("REQUEST_METHOD") == "POST":
            #获取请求体数据的长度,因为提交过来的数据需要用它来提取,注意POST请求和GET请求的获取数据的方式不同
            try:
                request_body_size = int(environ.get('CONTENT_LENGTH', 0))
            except (ValueError):
                request_body_size = 0
            #POST请求获取数据的方式
            request_data = environ['wsgi.input'].read(request_body_size)
            print('>>>>>',request_data) # >>>>> b'username=chao&password=123',是个bytes类型数据
            print('?????',environ['QUERY_STRING']) #????? 空的,因为post请求只能按照上面这种方式取数据
            #parse_qs可以帮我们解析数据
            re_data = parse_qs(request_data)
            print('拆解后的数据',re_data) #拆解后的数据 {b'password': [b'123'], b'username': [b'chao']}
            #post请求的返回数据我就不写啦
        pass

        if environ.get("REQUEST_METHOD") == "GET":
            #GET请求获取数据的方式,只能按照这种方式取
            print('?????',environ['QUERY_STRING']) #????? username=chao&password=123,是个字符串类型数据
            request_data = environ['QUERY_STRING']

            # parse_qs可以帮我们解析数据
            re_data = parse_qs(request_data)
            print('拆解后的数据', re_data) #拆解后的数据 {'password': ['123'], 'username': ['chao']}
            username = re_data['username'][0]
            password = re_data['password'][0]
            print(username,password)
            #进行验证:
            status = webauth.auth(username,password)
            if status:
            # 3.将相应内容返回
                with open('websuccess.html','rb') as f:
                    data = f.read()
            else:
                data = b'auth error'
        # 但是不管是post还是get请求都不能直接拿到数据,拿到的数据还需要我们来进行分解提取,所以我们引入urllib模块来帮我们分解



        #注意昂,我们如果直接返回中文,没有给浏览器指定编码格式,默认是gbk,所以我们需要gbk来编码一下,浏览器才能识别
        # data='登陆成功!'.encode('gbk')
    else:
        data = b'sorry 404!,not found the page'
    return [data]



#和咱们学的socketserver那个模块很像啊
httpd = make_server('127.0.0.1', 8080, application)

print('Serving HTTP on port 8080...')
# 开始监听HTTP请求:
httpd.serve_forever()
View Code

把代码拷走,创建文件,放到同一个目录下,运行一下we_python.py文件的代码就能看到效果,注意先输入的网址是127.0.0.1:8080/login ,还要注意你的mysql数据库没有问题。

猜你喜欢

转载自www.cnblogs.com/liangsha0/p/10878201.html
今日推荐