一、WEB框架介绍
互联网刚兴起的那段时间大家都热衷于CS架构,也就是Client/Server模式。每个用户的电脑上安装一个Client,就像QQ这种终端软件。随着互联网的发展,开发客户端显得越来越‘重’,BS架构(Browser/Server模式)越来越流行,也就是用户只需要一个浏览器就足够了。
1、页面显示的过程
2、HTTP协议
所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定?HTTP协议。HTTP协议就是解决这个问题,它规定了发送消息和接收消息的格式
每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。HTTP响应的Header中有一个Content-Type
表明响应的内容格式。如text/html
表示HTML网页。
(1)HTTP请求
HTTP GET请求的格式:
HTTP POST请求格式:
(2)HTTP响应
HTTP响应的格式:
3、自定义WEB框架
WEB应用本质就是一个sockect服务端,只要让我们的Web框架在给客户端回复响应的时候按照HTTP协议的规则加上响应头,这样我们就实现了一个正经的Web框架。如下简单实例:开启服务端后,在浏览器上输入网址:127.0.0.1:8090/index/后就会向浏览器返回:这是主页内容
from socket import socket server=socket() server.bind(("127.0.0.1",8090)) server.listen(5) while True: conn,addr=server.accept() data = conn.recv(1024).decode() #data为请求的HTTP格式 print(data) header=data.split(" ")[0] #获得请求header部分 temp=header.split(" ")[0] #获得header中包含请求路径的第一行 url=temp.split(" ")[1] #获得请求路径 conn.send(b'HTTP/1.1 200 OK Content-Type:text/html; charset=utf-8 ') #返回HTTP响应格式内容 #根据路径不同返回不同的内容 if url=="/index/": response="这是主页" else: response="404" conn.send(bytes(response,encoding="utf-8"))
如下为上述实例接收到HTTP请求的data内容:
GET /index/ HTTP/1.1 #header部分 Host: 127.0.0.1:8090 Connection: keep-alive Cache-Control: max-age=0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36 Upgrade-Insecure-Requests: 1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 GET /favicon.ico HTTP/1.1 #body部分 Host: 127.0.0.1:8090 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36 Accept: image/webp,image/apng,image/*,*/*;q=0.8 Referer: http://127.0.0.1:8090/index/ Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9
上述实例我们可以从请求头里面拿到请求的URL,然后做一个判断,我们的Web服务根据用户请求的URL不同而返回不同的内容。但是问题又来了,如果有很多很多页面怎么办?难道要挨个判断?当然不用,我们有更聪明的办法。
from socket import socket server=socket() server.bind(("127.0.0.1",8090)) server.listen(5) def index(): return "这是主页" def info(): return "这是个人信息页" func_list=[ ("/index/",index), ("/info/",info) ] while True: conn,addr=server.accept() data = conn.recv(1024).decode() header=data.split(" ")[0] temp=header.split(" ")[0] url=temp.split(" ")[1] conn.send(b'HTTP/1.1 200 OK Content-Type:text/html; charset=utf-8 ') func_name=None for i in func_list: if url==i[0]: func_name=i[1] break if func_name: response=func_name() else: response="404" conn.send(bytes(response,encoding="utf-8"))
完美解决了不同URL返回不同内容的问题。但是我不想仅仅返回几个字符串,我想给浏览器返回完整的HTML内容,这又该怎么办呢?没问题,不管是什么内容,最后都是转换成字节数据发送出去的。我可以打开HTML文件,读取出它内部的二进制数据,然后发送给浏览器。
from socket import socket server=socket() server.bind(("127.0.0.1",8090)) server.listen(5) def index(): with open("index.html",encoding="utf-8") as f: data_html=f.read() #读取html程序,本质是字符串 import pymysql conn = pymysql.connect(host='localhost', user='root', password='', database='day65', charset='utf8') cur = conn.cursor(cursor=pymysql.cursors.DictCursor) # 设置查询结果以字典形式显示 sql = "select id,name,balance from user" cur.execute(sql) use_list = cur.fetchall() print(use_list)#[{'id': 1, 'name': 'egon', 'balance': 10000}, {'id': 2, 'name': 'alex', 'balance': 2000}, {'id': 3, 'name': 'wsb', 'balance': 20000}] ret="" for i in use_list: ret+=""" <tr> <td>{0}</td> <td>{1}</td> <td>{2}</td> </tr> """.format(i["id"],i["name"],i["balance"]) cur.close() conn.close() data_new=data_html.replace("@@xx@@",ret) #将html中的占位内容用数据库中查询的结果组成的字符替换 return data_new #返回替换数据的html字符串 index() def info(): return "这是个人信息页" func_list=[ ("/index/",index), ("/info/",info) ] while True: conn,addr=server.accept() data = conn.recv(1024).decode() header=data.split(" ")[0] temp=header.split(" ")[0] url=temp.split(" ")[1] conn.send(b'HTTP/1.1 200 OK Content-Type:text/html; charset=utf-8 ') func_name=None for i in func_list: if url==i[0]: func_name=i[1] break if func_name: response=func_name() else: response="404" conn.send(bytes(response,encoding="utf-8"))
上述代码模拟了一个web服务的本质,而对于真实开发中的Python Web程序来说,一般分为两部分:服务器程序和应用程序。服务器程序负责对Socket
服务器进行封装,并在请求到来时,对请求的各种数据进行整理。应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架。
不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。这时候,标准化就变得尤为重要。一旦标准确定,双方根据约定的标准各自进行开发即可。WSGI(Web Server Gateway Interface)就是这样一种规范,它定义了使用Python编写的Web APP与Web Server之间接口格式,实现Web APP与Web Server间的解耦。
二、django框架
1、框架介绍
根据第一部分内容介绍,我们可以总结出一个web框架应该包含如下三部分:a.sockect服务、b.根据不同的url调用不同函数(包含逻辑)、c.返回内容(模板渲染)。常见的python WEB框架有tornado(包含如上3部分, Django(上述a部分为第三方的),flask(上述a和c为第三方的)。
2、Django安装
Django框架安装:
命令行创建django项目(首先将cd到django项目欲放置的位置):
命令行开启django服务端(首先将cd到django项目文件夹的位置):
python manage.py runserver
上述开启的服务的默认端口为:127.0.0.1:8000,如果需要指定端口,则在上述命令后空格直接跟指定的ip和端口
3、目录介绍
4、配置文件
模板文件配置:在python中创建一个django项目后,在项目文件夹下会多一个templates文件夹,主要用于存放html模板文件,若要准确找到引用的html文件,需要在setting文件中对此文件夹路径进行配置。如下:
静态文件配置:在使用html模板文件时,其通常会有使用js或css等文件,这些文件称为静态文件,为了使其正常生效,我们也需要在setting文件进行相关配置,如下:
html文件中引用静态文件的方式如下:
5、入门三件套
(1)HttpResponse:用于向服务端返回字符串,如启动服务后,在浏览器请求:127.0.0.1:8000/index/后,浏览器显示hello world字符内容
from django.conf.urls import url from django.contrib import admin from django.shortcuts import HttpResponse def index(request): return HttpResponse("hello world") urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^index/', index), ]
(2)render:用于渲染html文件,如启动服务后,在浏览器请求:127.0.0.1:8000/login/后,浏览器会显示login.html文件应被渲染出的结果
from django.conf.urls import url from django.contrib import admin from django.shortcuts import render #render用于渲染html文件 def login(request): #request就代表了请求的所有内容 return render(request,"login.html") urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^login/', login), ]
(3)redirect:用于跳转到指定的连接,实例如下:当启动服务端后,在浏览器请求:127.0.0.1:8000/login/后,会显示login.html被渲染的登录界面,提交后若判断登录成功,则会跳转到指定的网页。
login.html文件主要代码:
<body> <h1>欢迎登录</h1> <form action="/login/" method="post"> //action为提交路径,action为请求方式 <p><input type="text" name="username"></p> <p><input type="password" name="userpswd"></p> <p><input type="submit" value="提交"></p> {{ error_msg }} //模板语言,不传值不会显示 </form> </body>
项目中url逻辑代码:
from django.conf.urls import url from django.contrib import admin from django.shortcuts import HttpResponse,render,redirect def login(request): if request.method=="GET": #request.method表示请求类型 return render(request,"login.html") else:#当点击提交按钮执行 print(request.method) #结果:POST print(request.POST) #结果为登录输入的用户名及密码<QueryDict: {'uesername': ['zhangli'], 'userpswd': ['123']}> if request.POST.get("username")=="zhangli" and request.POST.get("userpswd")=="123": return redirect("http://www.baidu.com") #如果登录成功,则跳转其他页面 else: return render(request, "login.html", {"error_msg": "用户名或密码错误"}) #有html有模板语言(占位用)时render用法 urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^login/', login), ]
6、模板语言
主要用于html文件中,暂时主要介绍两种:用于实现循环和占位,占位已在上一节展示,此处不再演示,主要介绍循环模板,如下例:
index.html文件主要代码:
<body> <table border="1"> <thead> <tr> <th>#</th> <th>姓名</th> <th>工资</th> </tr> </thead> <tbody> {% for i in user_list %} <tr> <td>{{ i.id }}</td> <td>{{ i.name }}</td> <td>{{ i.balance }}</td> </tr> {% endfor %} </tbody> </table> </body>
项目中url代码:
from django.conf.urls import url from django.contrib import admin from django.shortcuts import HttpResponse,render,redirect def index(request): import pymysql conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="", db="day65", charset="utf8") cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select id, name, balance from user") user_list = cursor.fetchall() cursor.close() conn.close() print(user_list)#查询结果为:[{'id': 1, 'name': 'egon', 'balance': 10000}, {'id': 2, 'name': 'alex', 'balance': 2000}, {'id': 3, 'name': 'wsb', 'balance': 20000}] return render(request, "index.html", {"user_list": user_list}) urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^login/', login), url(r'^index/', index), ]