對於所有的Web應用,本質上其實就是一個socket服務端,用戶的瀏覽器其實就是一個socket客戶端。 import socket def f1(request): """ 處理用戶請求,並返回相應的內容 :param request: 用戶請求的所有信息 :return: """ f = ope ...
對於所有的Web應用,本質上其實就是一個socket服務端,用戶的瀏覽器其實就是一個socket客戶端。
import socket def f1(request): """ 處理用戶請求,並返回相應的內容 :param request: 用戶請求的所有信息 :return: """ f = open('index.fsw','rb') data = f.read() f.close() return data """ <body> <h1>用戶登錄</h1> <form> <p><input type="text" placeholder="用戶名" /></p> <p><input type="password" placeholder="密碼" /></p> </form> </body> """ def f2(request): f = open('aricle.tpl','rb') data = f.read() f.close() print(data) return data """ <body> <table border="1"> <thead> <tr> <th>ID</th> <th>用戶名</th> <th>郵箱</th> </tr> </thead> <tbody> <tr> <th>1</th> <th>root</th> <th>[email protected]</th> </tr> </tbody> </table> </body> """ routers = [ ('/xxx', f1), ('/ooo', f2), ]#手動寫的網址 def run(): sock = socket.socket() sock.bind(('127.0.0.1',8080)) sock.listen(5) while True: conn,addr = sock.accept() # hang住 #print(conn)#獲得的兩個套接字,我去charm自己會發送請求一個/favicon.ico頁面的報文 # print(addr) # 有人來連接了 # 獲取用戶發送的數據 data = conn.recv(8096) data = str(data,encoding='utf-8') #print(data)#get報文 headers,bodys = data.split('\r\n\r\n') #print("head:",headers) #print("body:",bodys) body是空的 temp_list = headers.split('\r\n') # print(temp_list) method,url,protocal = temp_list[0].split(' ') # print(method) GET # print(url) /ooo /favicon.ico # print(protocal) /HTTP/1.1 conn.send(b"HTTP/1.1 200 OK\r\n\r\n") func_name = None for item in routers: if item[0] == url: func_name = item[1] break if func_name: response = func_name(data) print(data) else: response = b"404" conn.send(response) conn.close() if __name__ == '__main__': run()靜態網頁
這種靜態頁面不能與資料庫連接交互,所以也是非常的low。
import socket def f1(request): """ 處理用戶請求,並返回相應的內容 :param request: 用戶請求的所有信息 :return: """ f = open('index.fsw','rb') data = f.read() f.close() return data """ <body> <h1>用戶登錄</h1> <form> <p><input type="text" placeholder="用戶名" /></p> <p><input type="password" placeholder="密碼" /></p> </form> </body> """ def f2(request): f = open('aricle.tpl','r',encoding='utf-8') data = f.read() f.close() import time ctime = time.time() data = data.replace('@@sw@@',str(ctime)) return bytes(data,encoding='utf-8') """ <body> <table border="1"> <thead> <tr> <th>ID</th> <th>用戶名</th> <th>郵箱</th> </tr> </thead> <tbody> <tr> <th>1</th> <th>@@sw@@</th> <th>[email protected]</th> </tr> </tbody> </table> </body> """ def f3(request): import pymysql # 創建連接 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db1') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select id,username,password from userinfo") user_list = cursor.fetchall() #print(user_list) cursor.close() conn.close() content_list = [] for row in user_list: tp = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" %(row['id'],row['username'],row['password']) content_list.append(tp) content = "".join(content_list) f = open('userlist.html','r',encoding='utf-8') template = f.read() f.close() # 模板渲染(模板+數據) data = template.replace('@@sdfsdffd@@',content) return bytes(data,encoding='utf-8') """ mysql> select * from userinfo; +----+----------+----------+ | id | username | password | +----+----------+----------+ | 1 | alex | 123 | +----+----------+----------+ <body> <table border="1"> <thead> <tr> <th>ID</th> <th>用戶名</th> <th>郵箱</th> </tr> </thead> <tbody> @@sdfsdffd@@ </tbody> </table> </body> """ def f4(request): import pymysql # 創建連接 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db1') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select id,username,password from userinfo") user_list = cursor.fetchall() cursor.close() conn.close() f = open('hostlist.html','r',encoding='utf-8') data = f.read() f.close() # 基於第三方工具實現的模板渲染 from jinja2 import Template template = Template(data) data = template.render(xxxxx=user_list,user='sdfsdfsdf') return data.encode('utf-8') """ {% for row in xxxxx %} <tr> <td>{{row.id}}</td> <td>{{row.username}}</td> <td>{{row.password}}</td> </tr> {% endfor %} </tbody> </table> {{user}} """ routers = [ ('/xxx', f1), ('/ooo', f2), ('/userlist.html', f3), ('/host.html', f4), ] def run(): sock = socket.socket() sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('127.0.0.1',8080)) sock.listen(5) while True: conn,addr = sock.accept() # hang住 # 有人來連接了 # 獲取用戶發送的數據 data = conn.recv(8096) data = str(data,encoding='utf-8') headers,bodys = data.split('\r\n\r\n') temp_list = headers.split('\r\n') method,url,protocal = temp_list[0].split(' ') conn.send(b"HTTP/1.1 200 OK\r\n\r\n") func_name = None for item in routers: if item[0] == url: func_name = item[1] break if func_name: response = func_name(data) else: response = b"404" conn.send(response) conn.close() if __name__ == '__main__': run()動態頁面
這裡要說兩點,首先這裡使用了jinjia2模塊,所以要簡單的介紹一下這個模塊。
渲染模板(使用render_template方法)
@app.route('/about/') def about(): # return render_template('about.html',user='username') return render_template('about.html',**{'user':'username'})
渲染模版時有兩種傳遞參數的方式:用 var='value' 傳遞一個參數;使用字典組織多個參數,並且加兩個*
號轉換成關鍵字參數傳入。
在jinja2模板中:
{{ ... }}
:裝載一個變數,模板渲染的時候,會使用傳進來的同名參數這個變數代表的值替換掉。
{% ... %}
:裝載一個控制語句。
{# ... #}
:裝載一個註釋,模板渲染的時候會忽視這中間的值。
變數:
設置全局變數:{% set name='xx' %},之後就可以使用此變數了。
設置局部變數:
{% with foo = 42 %}
{{ foo }}
{% endwith %}
這裡的foo變數只能在with標簽中使用。
{% if kenny.sick %} Kenny is sick. {% elif kenny.dead %} You killed Kenny! You bastard!!! {% else %} Kenny looks okay --- so far {% endif %}if語句
#一般迴圈 <ul> {% for user in users %} <li>{{ user.username|e }}</li> {% endfor %} </ul> #遍歷字典 {% for key, value in my_dict.iteritems() %} <dt>{{ key|e }}</dt> <dd>{{ value|e }}</dd> {% endfor %}for迴圈
jinja2模塊最重要的部分是巨集,巨集相當於一個搭建好的頁面一部分,可以被引入,可以往巨集傳遞參數。可以將一些經常用到的代碼片段放到巨集中,然後把一些不固定的值抽取出來當成一個變數,在使用巨集時傳遞參數,從而將巨集渲染成為頁面的一部分。
更多關於此模塊的操作,可以查看博客https://www.cnblogs.com/ygj0930/p/7170621.html。
要說的第二點就是這種方法還是太low了。
import socket def handle_request(client): buf = client.recv(1024) client.send("HTTP/1.1 200 OK\r\n\r\n".encode("utf8")) client.send("<h1 style='color:red'>Hello, yuan</h1>".encode("utf8")) def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('localhost',8001)) sock.listen(5) while True: connection, address = sock.accept() handle_request(connection) connection.close() if __name__ == '__main__': main()最low的socket服務端
最簡單的Web應用就是先把HTML用文件保存好,用一個現成的HTTP伺服器軟體,接收用戶請求,從文件中讀取HTML,返回。
如果要動態生成HTML,就需要把上述步驟自己來實現。不過,接受HTTP請求、解析HTTP請求、發送HTTP響應都是苦力活,如果我們自己來寫這些底層代碼,還沒開始寫動態HTML呢,就得花個把月去讀HTTP規範。
正確的做法是底層代碼由專門的伺服器軟體實現,我們用Python專註於生成HTML文檔。因為我們不希望接觸到TCP連接、HTTP原始請求和響應格式,所以,需要一個統一的介面,讓我們專心用Python編寫Web業務。這個介面就是WSGI:Web Server Gateway Interface。
# from wsgiref.simple_server import make_server # # # def application(environ, start_response): # start_response('200 OK', [('Content-Type', 'text/html')]) # return [b'<h1>Hello, web!</h1><h2>Hello, py!</h2>'] # # # httpd = make_server('127.0.0.2', 8080, application)#(ip,pork,func) # # print('Serving HTTP on port 8080...') # # 開始監聽HTTP請求: # httpd.serve_forever()wsgiref模塊
django入門
django是一個基於python的高級web開發框架,因為他的高度集成,將會在今後的web開發里給予我們很大的幫助。
首先創建一個django工程(加不加.py都可以):
django-admin.py startproject project_name #django-admin.py startproject myblog
工程下麵有幾個核心測文件:
manage.py Django項目裡面的管理工具,通過它可以調用django shell和資料庫等。
settings.py 包含了項目的預設設置,包括資料庫信息,調試標誌以及其他一些工作的變數。
urls.py 負責把URL模式映射到應用程式,路由(就是url與函數的對應關係)。
wsgi.py 調用python內置的wsgiref模塊,web服務網關介面。他定義django用什麼socket實現,預設就是wsgiref模塊。
註:除了命令的方式pycharm也可以進行django工程的搭建。
HttpResponse模塊
from django.conf.urls import url from django.shortcuts import HttpResponse def index(request):#request用戶請求相關的所有信息 return HttpResponse('whatever') urlpatterns = [ url(r'^index/', index), ]
啟動django自帶的伺服器,
python manage.py runserver 8080
在瀏覽器訪問127.0.0.1:8080/index即可查看到django渲染後的網頁。
render模塊
from django.conf.urls import url from django.shortcuts import render def index(request):#request用戶請求相關的所有信息 return render(request,"a.html")#預設要加request參數 urlpatterns = [ url(r'^index/', index), ]
接下來在瀏覽器訪問127.0.0.1:8080/index即可查看到django渲染後的網頁(伺服器在改變了代碼的情況下會自動重啟)。還有,訪問的前提是在templates目錄下有一個a.html的文件。那麼django是如何找到這個路徑的呢,因為在settings.py下有一個TEMPLATES列表,其中'DIRS': [os.path.join(BASE_DIR, 'templates')]指明瞭render需要從這個目錄下拿到。
靜態文件的配置
在工程文件夾下創建一個static文件夾裡面存放靜態文件,並將路徑寫入settings.py下。
STATIC_URL = '/static/' STATICFILES_DIRS=(os.path.join(BASE_DIR,'static'),)
然後在導入文件時一律使用/static引入。
request相關
request.method獲得當前請求的方法。request.GET與request.POST可以取到用戶提交的數據。
from django.conf.urls import url from django.shortcuts import render,redirect def index(request):#request用戶請求相關的所有信息 if request.method =='GET':#瀏覽器預設傳get,區分返回來的信息 return render(request,"a.html") else: u=request.POST.get('user')#取出post方式傳回來的字典的值 p=request.POST.get('pwd')#get取不到會轉化為none if u=='jeff' and p=='123': return redirect('http://www.baidu.com')#當然也可以重定向到自己的目錄 else: return render(request, "a.html") urlpatterns = [ url(r'^index/', index), ]
a.html中的修改:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="/static/as.css"> <title>Title</title> </head> <body> <h1>用戶登錄</h1> <form method="post" action="/index/">{# 發送以post方式發到index下 #} <input type="text" name="user"/> <input type="password" name="pwd"/> <input type="submit" value="login"/> </form> </body> </html>
這樣用戶訪問127.0.0.1:8080/index會使用get方法返回a.html頁面,輸入用戶名和密碼提交會用post方法返回給index頁面經判斷是重定向還是重新輸入。
django的渲染模板
django基本的html的模板與jinja2很相似,我們可以在form表單裡加入一個{{ msg }}的模板,然後在render里添加一個msg:value用於自動傳入。
django的模板取序列的值也是簡單粗暴,比如取列表就是{{ list.index }}例如{{ s.0 }}{{ s.1 }},字典就是{{ dict.key }}例如{{row.id}}{{ row.name }}。
from django.conf.urls import url
from django.contrib import admin
from django.shortcuts import HttpResponse,render,redirect
def index(request):#request用戶請求相關的所有信息
if request.method =='GET':
return render(request,"a.html")
else:
u=request.POST.get('user')
p=request.POST.get('pwd')
print(u)
print(p)
if u=='jeff' and p=='123':
return render(request, "b.html",{'user':[{'id':1,'name':'jeff','age':0},
{'id': 2, 'name': 'frank', 'age': 1},
{'id': 3, 'name': 'xixi', 'age': 2}]})
else:
return render(request, "a.html")
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', index),
]
urls.py配置
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="/static/as.css"> <title>Title</title> </head> <body> <h1>用戶登錄</h1> <form method="post" action="/index/">{# 發送以post方式發到index下 #} <input type="text" name="user"/> <input type="password" name="pwd"/> <input type="submit" value="login"/> </form> </body> </html>a.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <table border="1"> {% for item in user %} <tr> <td>{{ item.id }}</td> <td>{{ item.name }}</td> <td>{{ item.age }}</td> <td><a href="/del/?nid=={{ item.id }}"></a></td>{# 跳轉到專門的del頁面 #} </tr> {% endfor %}{#迴圈結束 #} </table> </body> </html>b.html
這裡render傳入的字典可以使用pymsql導入,這樣就與資料庫緊密的連接到一起了。