Django(一)

Django

web框架的本质及自定义web框架

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

  基于socket来自己实现一个web框架,写一个web服务端,让浏览器来请求,并通过自己的服务端把页面返回给浏览器,浏览器渲染出我们想要的效果。  

一、简单的web框架

import socket

sk = socket.socket()
sk.bind(('127.0.0.1',8001))
sk.listen()

conn,addr = sk.accept()
msg = conn.recv(1024)
str_msg = msg.decode('utf-8')
print(str_msg)

conn.send(b'HTTP/1.1 200 ok 

')
conn.send(b'hello world')

conn.close()
sk.close()

 在浏览器访问服务端:

重启我们的代码,然后在网址中输入这个:

 浏览器发过来一堆的消息,我们给浏览器回复(响应)信息的时候,也要按照一个消息格式来写,这些都是http协议规定的,那么我们就来学习一下http协议,然后继续完善我们的web框架:

   HTTP协议:https://www.cnblogs.com/clschao/articles/9230431.html

 二、返回Html文件的web框架:

首先写一个html文件,内容如下,名称为index.html:

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

    <style>
        h1{
            background-color: green;
            color: white;
        }
    </style>
</head>
<body>

<h1>世界,你好!</h1>
<img src="https://img3.chouti.com/img_1557880036293.jpg?imageView2/1/q" alt="">

<script>
    alert('这是我们第一个网页')
</script>

</body>
</html>

  准备我们的python代码,服务端程序,文件内容如下,文件名称为   返回Html文件的web框架.py :

import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8001))
sk.listen()

conn,addr = sk.accept()
msg = conn.recv(1024)
str_msg = msg.decode('utf-8')
print('浏览器请求信息:',str_msg)
conn.send(b'HTTP/1.1 200 ok 

')

with open('index.html','rb') as f:
    data = f.read()
conn.send(data)

conn.close()
sk.close()
import socket
from threading import Thread

server = socket.socket()
server.bind(('0.0.0.0',8001))
server.listen()

def communication(conn):
    #接受请求数据
    msg = conn.recv(1024).decode('utf-8')
    print(msg)
    #组合响应协议的消息格式,然后发送响应信息
    conn.send(b'HTTP/1.1 200 ok 

')

    #打开index.html文件,返回给前端
    with open('index.html', 'rb') as f:  #没有引入文件形式的html页面
        data = f.read()
    conn.send(data)
    conn.close()

while 1:
    #接受连接
    conn, add = server.accept()
    #开启线性并发
    t = Thread(target=communication,args=(conn,))
    t.start()
返回html文件(多线程,函数)web框架.py

页面上输入网址看效果:css样式和js效果都有。

 

将index.html 文件中css样式与js语句写成文件,图片网络地址改为本地地址(01.jpg):

index.css:

h1{
    background-color: green;
    color: white;
}

index.js

alert('这是我们第一个网页') 

index.html改为:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="index.css">
    <!--<style>-->
        <!--h1{-->
            <!--background-color: green;-->
            <!--color: white;-->
        <!--}-->
    <!--</style>-->
</head>
<body>

<h1>世界,你好!</h1>
<img src="01.jpg" alt="">

<!--<script>-->
    <!--alert('这是我们第一个网页')-->
<!--</script>-->

<script src="index.js"></script>
</body>
</html>

 同样使用我们之前的python程序,来看效果:

  需要将html页面需要的css、js、图片等文件也发送给浏览器就,并且这些静态文件都是浏览器单独过来请求的,其实和标签的属性有有关系,css文件是link标签的href属性:<link rel="stylesheet" href="test.css">,js文件是script标签的src属性:<script src="test.js"></script>,图片文件是img标签的src属性:<img src="meinv.png" alt="" width="100" height="100"> ,那个.ico文件是link标签的属性:<link rel="icon" href="wechat.ico">,其实这些属性都会在页面加载的时候,单独到自己对应的属性值里面取请求对应的文件数据,而且我们如果在值里面写的都是自己本地的路径,那么都会来自己的本地路径来找,如果我们写的是相对路径,就会到我们自己的网址+文件名称,这个路径来找它需要的文件,所以我们只需要将这些请求做一些响应,将对应的文件数据相应给浏览器就可以了!

 三、返回静态文件的高级web框架

   服务端程序:

import socket

sk = socket.socket()
sk.bind(('127.0.0.1',8001))
sk.listen()

while 1:
    conn,addr = sk.accept()
    msg = conn.recv(1024)
    str_msg = msg.decode('utf-8')
    path = str_msg.split('
')[0].split(' ')[1]
    conn.send(b'HTTP/1.1 200 ok 

')

    if path == '/':
        print(msg.decode('utf-8'))
        with open('index.html','rb') as f:
            data = f.read()
        conn.send(data)
        conn.close()
    elif path == '/01.jpg':
        with open('01.jpg','rb') as f:
            pic_data = f.read()
        conn.send(pic_data)
        conn.close()
    elif path == '/index.css':
        with open('index.css','rb') as f:
            css_data = f.read()
        conn.send(css_data)
        conn.close()

    elif path == '/wechat.ico':
        with open('wechat.ico','rb') as f:
            ico_data = f.read()
        conn.send(ico_data)
        conn.close()

    elif path == '/index.js':
        with open('index.js','rb') as f:
            js_data = f.read()
        conn.send(js_data)
        conn.close()

  index.html:添加了<link rel="icon" href="wechat.ico">属性

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="index.css">
    <link rel="icon" href="wechat.ico">
    <!--<style>-->
        <!--h1{-->
            <!--background-color: green;-->
            <!--color: white;-->
        <!--}-->
    <!--</style>-->
</head>
<body>

<h1>世界,你好!</h1>
<img src="01.jpg" alt="">

<!--<script>-->
    <!--alert('这是我们第一个网页')-->
<!--</script>-->

<script src="index.js"></script>
</body>
</html>

import socket
from threading import Thread

server = socket.socket()
server.bind(('0.0.0.0',8001))
server.listen()

def communication(conn):
    msg = conn.recv(1024).decode('utf-8')
    print(msg)
    path = msg.split('
')[0].split(' ')[1]
    print(path)
    #针对不同的请求路径,返回不同的文件
    if path =='/':
        conn.send(b'HTTP/1.1 200 ok 
k1:v1

')
        with open('index.html', 'rb') as f:
            data = f.read()
        conn.send(data)
        conn.close()
    elif path =='/index.css':
        conn.send(b'HTTP/1.1 200 ok 
k1:v1

')
        with open('index.css', 'rb') as f:
            data = f.read()
        conn.send(data)
        conn.close()
    elif path =='/index.js':
        conn.send(b'HTTP/1.1 200 ok 
k1:v1

')
        with open('index.js', 'rb') as f:
            data = f.read()
        conn.send(data)
        conn.close()
    elif path =='/wechat.ico':
        conn.send(b'HTTP/1.1 200 ok 
k1:v1

')
        with open('wechat.ico', 'rb') as f:
            data = f.read()
        conn.send(data)
        conn.close()

    elif path =='/01.jpg':
        conn.send(b'HTTP/1.1 200 ok 
k1:v1

')
        with open('01.jpg', 'rb') as f:
            data = f.read()
        conn.send(data)
        conn.close()

while 1:
    conn, add = server.accept()
    t = Thread(target=communication,args=(conn,))
    t.start()
进阶版返回静态文件的高级web框架.py

四:函数多线程版返回静态文件的web框架

静态文件使用:index.html , index.css , index.js , 01.jpg , wechat.ico

python代码:

import socket
from threading import Thread

server = socket.socket()
server.bind(('127.0.0.1',8001))
server.listen()

def html(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    with open('index.html', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()
def css(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    with open('index.css','rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def js(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    with open('index.js', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def ico(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    with open('wechat.ico','rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()
def jpg(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    with open('01.jpg', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

urlpatterns = [
    ('/',html),
    ('/index.css',css),
    ('/index.js',js),
    ('/wechat.ico',ico),
    ('/01.jpg',jpg),
]


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

    # if path =='/':
    #     html(conn)
    # elif path =='/index.css':
    #     css(conn)
    # elif path =='/index.js':
    #     js(conn)
    # elif path =='/wechat.ico':
    #     ico(conn)
    # elif path =='/01.jpg':
    #     jpg(conn)

while 1:
    conn, add = server.accept()
    t = Thread(target=communication,args=(conn,))
    t.start()

五、返回不同html页面的web框架

静态文件使用:index.html , index.css , index.js , 01.jpg , wechat.ico

添加返回的页面 home.html

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

<h1>首页</h1>
<h1>time</h1>

</body>
</html>

python代码:

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 

')
    with open('index.html', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()
def css(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    with open('index.css', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def js(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    with open('index.js', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def ico(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    with open('wechat.ico', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()
def jpg(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    with open('01.jpg', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def home(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    with open('home.html', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

urlpatterns = [
    ('/',html),
    ('/index.css',css),
    ('/index.js',js),
    ('/wechat.ico',ico),
    ('/01.jpg',jpg),
    ('/home.html',home),
]


def communication(conn):
    msg = conn.recv(1024).decode('utf-8')
    # print(msg)
    path = msg.split('
')[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()

六、返回动态l页面的web框架

静态文件使用:index.html , index.css , index.js , 01.jpg , wechat.ico ,home.html

python 代码:

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 

')
    with open('index.html','rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def css(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    with open('index.css','rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def js(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    with open('index.js','rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def ico(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    with open('wechat.ico','rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def jpg(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    with open('01.jpg', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def home(conn):
    conn.send(b'HTTP/1.1 200 ok 

')
    current_time = str(time.time())
    with open('home.html','r',encoding='utf-8') as f:
        data = f.read()
        data = data.replace('time',current_time)
    conn.send(data.encode('utf-8'))
    conn.close()

urlpatterns = [
    ('/',html),
    ('/index.css',css),
    ('/index.js',js),
    ('/favicon.ico',ico),
    ('/01.jpg',jpg),
    ('/home.html',home),
]

def communication(conn):
    msg = conn.recv(1024).decode('utf-8')
    # print(msg)
    path = msg.split('
')[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()

 

七、wsgiref模块版web框架

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

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

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

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

   这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。

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

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

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

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

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>']

httpd = make_server('127.0.0.1', 8080, application)

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

 简单wsgiref模块版web框架:

 静态文件使用:index.html , home.html

import time
from wsgiref.simple_server import make_server

def app(environ,start_response):
    start_response('200 OK', [('Content-Type', 'text/html'), ('k1', 'v1')])
    print(environ)
    path = environ['PATH_INFO']

    for urlpattern in urlpatterns:
        print(path)
        if urlpattern[0] == path:
            ret = urlpattern[1]()
            return ret

def html():
    with open('index.html', 'rb') as f:
        data = f.read()
    return [data]

def home():
    current_time = str(time.time())
    with open('home.html', 'r',encoding='utf-8') as f:
        data = f.read()
        data = data.replace('time',current_time).encode('utf-8')
    return [data]

urlpatterns = [
    ('/',html),
    ('/home.html',home),
]

httpd = make_server('127.0.0.1', 8001, app)
httpd.serve_forever()

一个完整的web项目,用户登录认证的项目,需要连接数据库,在mysql数据库里面准备一些表和数据:

mysql> create database day53;
Query OK, 1 row affected (0.12 sec)

mysql> use day53;
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.36 sec)

mysql> insert into userinfo(username,password) values('xu','123'),('wu','456');
Query OK, 2 rows affected (0.17 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from userinfo;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | xu       | 123      |
|  2 | wu       | 456      |
+----+----------+----------+
2 rows in set (0.00 sec)

python服务端代码(主逻辑代码):

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')])
    # print(environ)
    # print(environ['PATH_INFO'])
    path = environ['PATH_INFO']
    # 用户获取login页面的请求路径
    if path == '/login':
        with open('login.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.decode('utf-8')) #将bytes类型数据转码
            print('拆解后的数据',re_data)  #拆解后的数据 {'username': ['wu'], 'password': ['456']}
            username = re_data['username'][0]
            password = re_data['password'][0]
            print(username,password)
            # 进行验证
            status = webauth.auth(username,password)
            if status:
                # 3.将相应内容返回
                with open('success.html','rb') as f:
                    data = f.read()
            else:
                data = b'auth error'

        if environ.get("REQUEST_METHOD") == "GET":
            # GET请求获取数据的方式,只能按照这种方式取
            print('?????',environ['QUERY_STRING']) #????? username=xu&password=123,是个字符串类型数据
            request_data = environ['QUERY_STRING']
            print('>>>>request_data', request_data)
            # parse_qs可以帮我们解析数据
            re_data = parse_qs(request_data)
            print('拆解后的数据', re_data)
            username = re_data['username'][0]
            password = re_data['password'][0]
            print(username,password)
            # 进行验证
            status = webauth.auth(username,password)
            if status:
                # 3.将相应内容返回
                with open('success.html','rb') as f:
                    data = f.read()
            else:
                data = b'auth error'
            # 不管是post还是get请求都不能直接拿到数据,拿到的数据还需要我们来进行分解提取,所以我们引入urllib模块来帮我们分解
     
    else:
        data = b'sorry 404!,not found the page'
    return [data]

httpd = make_server('127.0.0.1', 8001, application)
print('Serving HTTP on port 8001...')
# 开始监听HTTP请求:
httpd.serve_forever()

登陆页面 login.html文件:

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

<!--form标签中 method属性可以等于get-->
<form action="http://127.0.0.1:8001/auth/" method="post">
    用户名<input type="text" name="username">
    密码 <input type="password" name="password">
    <input type="submit">
</form>

</body>
</html>

用户验证 webauth.py文件:

def auth(username,password):
    import pymysql
    conn = pymysql.connect(
        host='127.0.0.1',
        port=3306,
        user='root',
        password='123',
        database='day53',
        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

用户登陆成功跳转 success.html文件:

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

</head>
<body>

<h1>恭喜登陆成功!</h1>

</body>
</html>

一个比较low的文件配置版web框架:

 1.pycharm创建userinfo表格:

#创建表,插入数据
def createtable():
    import pymysql
    conn = pymysql.connect(
        host='127.0.0.1',
        port=3306,
        user='root',
        password='123',
        database='day53',
        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()

2.框架主体:

from urls import urlpatterns
from wsgiref.simple_server import make_server

#这个文件里面是框架的主体内容
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    path = environ['PATH_INFO']
    for url_tuple in urlpatterns:
        if url_tuple[0] == path:
            data = url_tuple[1](environ) #environ要传进去,因为处理逻辑里面可能要用
            break
        else:
            data = b'sorry 404!,not found the page'
    return [data]
        #注意昂,我们如果直接返回中文,没有给浏览器指定编码格式,默认是gbk,所以我们需要gbk来编码一下,浏览器才能识别
        # data='登陆成功!'.encode('gbk')

httpd = make_server('127.0.0.1', 8001, application)

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

#整个框架写好了,那么我们将来想添加一些新的功能,比如说有人想在网址里面输入http://127.0.0.1:8001/timer 来查看当前时间,你只需要两步,写一个url映射关系,写一个应对的处理函数就搞定了,有了框架就不需要你在重新写一遍这所有的逻辑了,简单两步搞定新功能

3.urls文件:

from views import login,auth,favicon,index,timer

#url与视图函数的对应关系
urlpatterns=[
    ('/login',login),
    ('/auth/',auth),
    ('/favicon.ico',favicon),
    ('/',index),
    ('/timer',timer),

]

4.views文件:

import datetime
import webauth
from urllib.parse import parse_qs

def login(environ):
    with open('templates/login.html', 'rb') as f:
        data = f.read()
    return data

def auth(environ):
    # 登陆认证
    # 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.decode('utf-8'))  # 将bytes类型数据转码
        print('拆解后的数据', re_data)  # 拆解后的数据 {'username': ['wu'], 'password': ['456']}
        username = re_data['username'][0]
        password = re_data['password'][0]
        print(username, password)
        # 进行验证
        status = webauth.auth(username, password)
        if status:
            # 3.将相应内容返回
            with open('success.html', 'rb') as f:
                data = f.read()
        else:
            data = b'auth error'
    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('templates/websuccess.html', 'rb') as f:
                data = f.read()
        else:
            data = b'auth error'
    return data

def favicon(environ):
    with open('wechat.ico','rb') as f:
        data = f.read()
    return data

def index(environ):
    with open('templates/index.html','rb') as f:
        data = f.read()
    return data

#查看当前时间的
def timer(environ):
    data = str(datetime.datetime.now()).encode('utf-8')
    return data

5.用户名密码验证 webauth文件:

#对用户名和密码进行验证
def auth(username,password):
    import pymysql
    conn = pymysql.connect(
        host='127.0.0.1',
        port=3306,
        user='root',
        password='123',
        database='day53',
        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

6.templates 文件夹下html文件:

index.html:

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

<a href="http://127.0.0.1:8001/login">请登录</a>

</body>
</html>

login.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:8001/auth/" method="post">-->
<form action="http://127.0.0.1:8001/auth/" method="get">
    用户名<input type="text" name="username">
    密码 <input type="password" name="password">
    <input type="submit">
</form>

</body>
</html>

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>

模板渲染JinJa2

  上面的代码实现了一个简单的动态页面(字符串替换),我完全可以从数据库中查询数据,然后去替换我html中的对应内容(专业名词叫做模板渲染,你先渲染一下,再给浏览器进行渲染),然后再发送给浏览器完成渲染。 这个过程就相当于HTML模板渲染数据。 本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据。 我这里用的特殊符号是我定义的,其实模板渲染有个现成的工具: jinja2

 下载:在cmd命令提示符中输入或者pycharm中terminal

pip install jinja2

来一个html文件,index,html,内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>姓名:{{name}}</h1>
    <h1>爱好:</h1>
    <ul>
        {% for hobby in hobby_list %}
        <li>{{hobby}}</li>
        {% endfor %}
    </ul>
</body>
</html>

使用jinja2渲染index2.html文件,创建一个python文件:

from wsgiref.simple_server import make_server
from jinja2 import Template

def index():
with open("index.html", "r",encoding='utf-8') as f:
data = f.read()
template = Template(data) # 生成模板文件
ret = template.render({"name": "于谦", "hobby_list": ["抽烟", "喝酒","烫头"]}) # 把数据填充到模板里面
return [bytes(ret, encoding="utf8"), ]

# 定义一个url和函数的对应关系
URL_LIST = [
("/index", index),
]

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 URL_LIST:
if i[0] == url:
func = i[1] # 去之前定义好的url列表里找url应该执行的函数
break
if func: # 如果能找到要执行的函数
return func() # 返回函数的执行结果
else:
return [bytes("404没有该页面", encoding="utf8"), ]


if __name__ == '__main__':
httpd = make_server('', 8001, run_server)
print("Serving HTTP on port 8001...")
httpd.serve_forever()

 

 从数据库中查询数据,来填充页面

使用pymysql连接数据库:

conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8")
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute("select name, age, department_id from userinfo")
user_list = cursor.fetchall()
cursor.close()
conn.close()

 创建一个测试的user表:

CREATE TABLE user(
  id int auto_increment PRIMARY KEY,
  name CHAR(10) NOT NULL,
  hobby CHAR(20) NOT NULL
)engine=innodb DEFAULT charset=UTF8;

模板的原理就是字符串替换,我们只要在HTML页面中遵循jinja2的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。

原文地址:https://www.cnblogs.com/Xiao_Xu/p/10868840.html