python项目Django(web服务)
一、 简单的web框架服务器
创建一个python文件,内容如下,名称为test.py:
import socket sk = socket.socket() sk.bind(('127.0.0.1', 8001)) sk.listen() conn, addr = sk.accept() from_b_msg = conn.recv(1024) str_msg = from_b_msg.decode('utf-8') print("浏览器请求信息:", str_msg) # socket是应用层和传输层之间的抽象层,每次都有协议,协议就是消息格式,那么传输层的消息格式我们不用管,因为socket帮我们搞定了,但是应用层的协议还是需要咱们自己遵守的,所以再给浏览器发送消息的时候,如果没有按照应用层的消息格式来写,那么你返回给浏览器的信息,浏览器是没法识别的。而应用层的协议就是我们的HTTP协议,所以我们按照HTTP协议规定的消息格式来给浏览器返回消息就没有问题了,关于HTTP我们会细说,首先看一下直接写conn.send(b'hello')的效果,然后运行代码,通过浏览器来访问一下,然后再看这一句conn.send(b'HTTP/1.1 200 ok \r\n\r\nhello')的效果 # 下面这句就是按照http协议来写的 # conn.send(b'HTTP/1.1 200 ok \r\n\r\nhello') # 上面这句还可以分成下面两句来写 conn.send(b'HTTP/1.1 200 ok \r\n\r\n') conn.send(b'hello')
二、 返回HTML文件的web框架(动态加载方式)
img标签中的资源在网络的其他服务器上,可以直接访问, js,css文件写在当前加载的html文件中可以统一加载,属于动态加载html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title><link rel="stylesheet" href="test.css"> <!--直接写在html页面里面的css样式是直接可以在浏览器上显示的--> <style> h1{ background-color: green; color: white; } </style> </head> <body> <h1>hello world!</h1> <!--直接写在html页面里面的img标签的src属性值如果是别人网站的地址(网络地址)是直接可以在浏览器上显示的--> <img src="http://pic214.nipic.com/file/20190429/12398452_133616540089_2.jpg" alt=""> <!--如果都是网络地址,那么只要你的电脑有网,就可以看到,不需要自己在后端写对应的读取文件,返回图片文件信息的代码,因为别人的网站就做了这个事情了--> <!--直接写在html页面里面的js操作是直接可以在浏览器上显示的--> <script> alert('这是我们第一个网页') </script> </body> </html>
三、 简单区分区别get与post请求(form表达验证)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--get请求:GET提交的数据会放在URL之后,也就是请求行里面,以?分割URL和传输数据,参数之间以&相连--> <form action="http://127.0.0.1:8001" method="get"> 用户名1 <input type="text" name="username"> <button>提交</button> </form> <!--get请求:是把提交的数据放在HTTP包的请求体中--> <form action="http://127.0.0.1:8001" method="post"> 用户名2 <input type="text" name="username"> <button>提交</button> </form> </body> </html>
四、 html文件favicon图标加载
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>这里favicon测试</title> <link rel="icon" href="https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1557910698&di=afab29beb3c80b8fb692788b4b1753c7&src=http://b-ssl.duitang.com/uploads/item/201505/27/20150527235406_8GSLT.thumb.700_0.jpeg"> </head> <body> <script> alert('favicon测试:请查看title标签图标!') </script> </body> </html>
五、 返回HTML文件的web框架(静态加载)
当我们直接在浏览器上保存某个页面的时候,随便一个页面,我们到页面上点击右键另存为,然后存到本地的一个目录下,你会发现这个页面的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">
其实这些属性都会在页面加载的时候,单独到自己对应的属性值里面取请求对应的文件数据,而且我们如果在值里面写的都是自己本地的路径,那么都会来自己的本地路径来找
如果我们写的是相对路径,就会到我们自己的网址+文件名称,这个路径来找它需要的文件,所以我们只需要将这些请求做一些响应,将对应的文件数据相应给浏览器就可以了!
示例:
• timg.jpg,favicon.ico文件
• py文件的三种实现方式:
import socket from threading import Thread server = socket.socket() server.bind(('127.0.0.1', 8001)) server.listen() def communication(conn): client_msg = conn.recv(1024).decode('utf-8') # print(client_msg) path = client_msg.split('\r\n')[0].split(' ')[1] print(path) # 针对不同的请求路径,返回不同的文件 if path == '/': conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n') with open('test1.html', 'rb') as f: data = f.read() conn.send(data) conn.close() elif path == '/index.css': conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n') 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 \r\nk1:v1\r\n\r\n') with open('index.js', 'rb') as f: data = f.read() conn.send(data) conn.close() elif path == '/favicon2.ico': conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n') with open('favicon2.ico', 'rb') as f: data = f.read() conn.send(data) conn.close() elif path == '/timg.jpg': conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n') with open('timg.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()
import socket from threading import Thread server = socket.socket() server.bind(('127.0.0.1', 8001)) server.listen() # 文件发送 def file_send(path, conn): conn.send(b"HTTP/1.1 200 OK \r\n\r\n") with open(path, "rb") as p: data = p.read() conn.send(data) conn.close() # 服务端等待连接 def communication(conn): client_msg = conn.recv(1024).decode('utf-8') # print(client_msg) path = client_msg.split('\r\n')[0].split(' ')[1] print(path) # 针对不同的请求路径,返回不同的文件 if path == "/": filepath = r"test1.html" file_send(filepath, conn) elif path == "/index.css": filepath = r"index.css" file_send(filepath, conn) elif path == "/index.js": filepath = "index.js" file_send(filepath, conn) elif path == "/timg.jpg": filepath = r"timg.jpg" file_send(filepath, conn) elif path == "/favicon2.ico": filepath = r"favicon2.ico" file_send(filepath, conn) while 1: conn, add = server.accept() t = Thread(target=communication, args=(conn,)) t.start()
import socket from threading import Thread sk = socket.socket() sk.bind(('127.0.0.1', 8001)) sk.listen() # 加载html网页文件 def func1(conn): conn.send(b'HTTP/1.1 200 ok\r\n\r\n') with open(r"test1.html", "rb") as p: data = p.read() conn.send(data) conn.close() # 加载img文件 def func2(conn): conn.send(b'HTTP/1.1 200 ok\r\n\r\n') with open(r"timg.jpg", "rb") as p: data = p.read() conn.send(data) conn.close() # 加载css文件 def func3(conn): conn.send(b'HTTP/1.1 200 ok\r\n\r\n') with open(r"index.css", "rb") as p: data = p.read() conn.send(data) conn.close() # 加载ico图标文件 def func4(conn): conn.send(b'HTTP/1.1 200 ok\r\n\r\n') with open(r"favicon2.ico", "rb") as p: data = p.read() conn.send(data) conn.close() # 加载js文件 def func5(conn): conn.send(b'HTTP/1.1 200 ok\r\n\r\n') with open(r"index.js", "rb") as p: data = p.read() conn.send(data) conn.close() # 循环开启线程,加载静态文件列表 def func(path, conn): for i in lst: if i[0] == path: t = Thread(target=i[1], args=(conn,)) t.start() # 文件加载列表 lst = [ ('/', func1), ('/timg.jpg', func2), ('/index.css', func3), ('/favicon2.ico', func4), ('/index.js', func5), ] # 等待连接 while True: conn, _ = sk.accept() msg = conn.recv(1024).decode("utf-8") # print(msg) path = msg.split("\n")[0].split(" ")[1] print(path) # 开启多线程,可接受多个连接请求 Thread(target=func, args=(path, conn)).start()
• html文件:test1.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="index.css"> <link rel="icon" href="favicon2.ico"> </head> <body> <h1>在苍茫的大海上,狂风卷集着乌云。在乌云和大海之间,海燕像黑色的闪电,在高傲地飞翔。</h1> <img src="timg.jpg" alt=""> <script src="index.js"></script> </body> </html>
• ccs文件:index.css h1{ background-color: red; color: #ffffff; } • js文件:index.js alert('简单的web服务测试代码!');
六、 不同路径返回不同页面的web框架
import socket sk = socket.socket() sk.bind(("127.0.0.1", 9001)) # 绑定IP和端口 sk.listen() # 监听 # 将返回不同的内容部分封装成函数 def index(url): # 读取index.html页面的内容 with open("index.html", "r", encoding="utf8") as f: s = f.read() # 返回字节数据 return bytes(s, encoding="utf8") def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") # 定义一个url和实际要执行的函数的对应关系 list1 = [ ("/index/", index), ("/home/", home), ] while 1: # 等待连接 conn, add = sk.accept() data = conn.recv(8096) # 接收客户端发来的消息 # 从data中取到路径 data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串 # 按\r\n分割 data1 = data.split("\r\n")[0] url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行 # 根据不同的路径返回不同内容 func = None # 定义一个保存将要执行的函数名的变量 for i in list1: if i[0] == url: func = i[1] break if func: response = func(url) else: response = b"404 not found!" # 返回具体的响应消息 conn.send(response) conn.close()
七、 返回动态页面的web框架
静态页面:页面的内容都不会变化的
动态页面:里面有动态变化的数据,而不是页面里面有动态效果
""" 根据URL中不同的路径返回不同的内容 返回HTML页面 让网页动态起来 """ import socket import time sk = socket.socket() sk.bind(("127.0.0.1", 9001)) # 绑定IP和端口 sk.listen() # 监听 # 将返回不同的内容部分封装成函数 def index(url): with open("index.html", "r", encoding="utf-8") as p: # 此处转换成字符串,实现字符串替换功能 data = p.read() now = str(time.time()) data = data.replace("@@@@", now) # 在网页中定义好特殊符号,用动态的数据去替换提前定义好的特殊符号 return bytes(data, encoding="utf-8") # 转换成bytes类型 def home(url): with open("home.html", "rb") as p: # 此处直接返回二进制 data = p.read() return data # 定义一个url和实际要执行的函数的对应关系 urlpatterns = [ ("/index", index), ("/home", home) ] while True: # 等待连接 conn, add = sk.accept() data = conn.recv(1024) # 接收客户端发来的消息 # 从data中取到路径 data = str(data, encoding="utf-8") # 把收到的字节类型的数据转换成字符串 # 按\r\n分割,再空格分割,获取访问路径 path = data.split("\r\n")[0].split(" ")[1] print(path) conn.send(B"HTTP/1.1 200 OK\r\n\r\n") # 因为要遵循HTTP协议,所以回复的消息也要加状态行 # 根据不同的路径返回不同内容 func = None # 定义一个保存将要执行的函数名的变量 for i in urlpatterns: if i[0] == path: func = i[1] break if func: response = func(path) else: response = b"404 not found!" # 返回具体的响应消息 conn.send(response) conn.close()
八、 wsgiref模块版web框架
对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。
1、服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。
2、应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。
WSGI(Web Server Gateway Interface)就是一种规范,它定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦。
常用的WSGI服务器有uwsgi、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。
wsgiref模块其实就是将整个请求信息给封装了起来,就不需要你自己处理了,假如它将所有请求信息封装成了一个叫做request的对象
• request.path就能获取到用户这次请求的路径
• request.method就能获取到本次用户请求的请求方式(get还是post)等
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:9001,这个打印的是'/',输入的是127.0.0.1:8000/index,打印结果是'/index' return [b'<h1>Hello, web!</h1>'] # 以数组形式返回一个bytes内容 httpd = make_server("127.0.0.1", 9001, application) print('Serving HTTP on port 8080...') # 开始监听HTTP请求 httpd.serve_forever()
九、 wsgiref模块版web框架(MVC框架)
数据库方式创建表和记录:
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)
通过pymysql方式创建数据库的表和记录的model.py:
#创建表,插入数据 def createtable(): import pymysql conn = pymysql.connect( host='127.0.0.1', port=3306, user='root', password='1234', 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); ''' sql1 = """ -- 插入数据 insert into userinfo(username,password) values('chao','666'),('sb1','222'); """ cursor.execute(sql) cursor.execute(sql1) conn.commit() cursor.close() conn.close()
服务器的框架的主体结构的manage.py:
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') #和咱们学的socketserver那个模块很像啊 httpd = make_server('127.0.0.1', 8080, application) print('Serving HTTP on port 8080...') # 开始监听HTTP请求: httpd.serve_forever() #整个框架写好了,那么我们将来想添加一些新的功能,比如说有人想在网址里面输入http://127.0.0.1:8080/timer 来查看当前时间,你只需要两步,写一个url映射关系,写一个应对的处理函数就搞定了,有了框架就不需要你在重新写一遍这所有的逻辑了,简单两步搞定新功能
调用函数的引用urls.py文件:
from views import login, auth, favicon, index, timer # url与视图函数的对应关系 urlpatterns = [ ('/login', login), ('/auth/', auth), ('/favicon.ico', favicon), ('/', index), ('/timer', timer), ]
应用代码程序的逻辑部分views.py:
import datetime import webauth from urllib.parse import parse_qs def login(environ): with open('./templates/web(1).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) 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('./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
用户名的登录验证webauth.py:
# 对用户名和密码进行验证 def auth(username, password): import pymysql conn = pymysql.connect( host='127.0.0.1', port=3306, user='root', password='1234', 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
静态文件(templates)的index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--看一下百度的登陆--> <a href="http://127.0.0.1:8080/login">请登录</a> </body> </html>
静态文件(templates)的web(1).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>
静态文件(templates)的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>
其他文件:图片(wechat.ico)