Python全棧開發之1七、tornado和web基礎知識

1、web基礎知識

  學習web框架以前,先來看一下web基礎知識,首先要明白其本質就是socket,用戶對應一個socket客戶端,可是若是從socket開始開發web應用程序那麼效率太了,正確的作法是底層socket處理代碼由專門的服務器軟件實現,而對於真實開發中的python web程序來講也是通常會分爲兩部分:服務器程序和應用程序。服務器程序負責對socket服務器進行封裝,並在請求到來時,先通過web服務器,對請求的各類數據進行整理封裝。以後web服務器將封裝好的數據傳遞給應用程序,應用程序並不涉及底層的socket,由應用程序負責具體的邏輯處理,可是上面就有一個問題,那就是如何來定義一個統一的web服務器和web應用程序之間接口格式,這個接口就是WSGI,WSGI(Web Server Gateway Interface)是一種規範,它定義了使用python編寫的web app與web server之間接口格式。WSGI applications 能夠是棧式的,這個棧的中間部分能夠叫作中間件,兩端是必需要實現的application和server。python標準庫提供的獨立WSGI服務器稱爲wsgiref。下面咱們來看一下如何簡單的自定義一個web框架。javascript

2、自定義web框架

  HTTP請求的全部輸入信息均可以經過environ得到,HTTP響應的輸出均可以經過start_response()加上函數返回值做爲Body。下面來看一下用最簡單的wsgiref來實現一個web框架。css

from wsgiref.simple_server import make_server

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    body = '<h1>hello {}</h1>'.format(environ['PATH_INFO'][1:] or 'web')
    return [body.encode('utf-8')]
    # return [b'<h1>Hello, web!</h1>']   # 這裏須要注意格式

httpd = make_server('', 8000, application)
httpd.serve_forever()

上面的東西寫得太簡單了,一個簡單的框架還應該包括路由系統和模板引擎等基本的東西,下面咱們來看一下如何本身來簡單的實現一下增強版的web框架html

from wsgiref.simple_server import make_server
import time
def new():
    f = open('s1.html', 'r')
    data = f.read()
    f.close()

    # 模擬模板引擎處理數據,將html裏文件的item替換成其餘數據
    # 這裏僅僅是用字符串的替換來最簡單的說明下原理
    new_data = data.replace("{{item}}", str(time.time()))
    return new_data
    
def index():
    f = open('index.html', 'r')      //打開文件讀取內容,將內容看成字符串返回給用戶
    data = f.read()
    f.close()
    return data

def home():
    return 'home'

URLS = {                            # 定義一個字典,簡單模擬路由系統
    "/new": new,                    # 一個url對應一個函數處理
    "/index": index,
    "/home": home,
}


def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    url = environ['PATH_INFO']
    if url in URLS.keys():              # 不一樣的url執行不一樣的函數
        func_name = URLS[url]
        ret = func_name()
    else:
        ret = "404"
    return [ret.encode('utf-8')]                        # 最後將結果返回


httpd = make_server('', 8000, application)
httpd.serve_forever()

雖然上面的代碼能夠用簡單的字符串進行替換,來動態的返回內容,可是效率極其的低下,最好的作法是使用jinja2模板引擎。至於如何使用,這裏先不講,先講下tronadao,而後咱們看一個真正的web框架是如何使用路由系統,和模板引擎。java

3、tronado

  雖然咱們本身寫框架,可是功能效率不行,這裏看一下比較常見的web框架tronado,Tornado爲python的一個非阻塞異步web frame,不一樣於那些最多隻能達到10,000個併發鏈接的傳統網絡服務器,Tornado在設計之初就考慮到了性能因素,旨在解決C10K問題,這樣的設計使得其成爲一個擁有很是高性能的框架。此外,它還擁有處理安全性、用戶驗證、社交網絡以及與外部服務(如數據庫和網站API)進行異步交互的工具。python

下面來看一下最簡單的hello,world, jquery

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

application = tornado.web.Application([
    (r"/index", MainHandler),
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

上述代碼執行的具體過程以下:  web

  第一步:執行腳本,監聽 8888 端口數據庫

  第二步:瀏覽器客戶端訪問 /index  -->  http://127.0.0.1:8888/indexnpm

  第三步:服務器接受請求,並交由對應的類處理該請求瀏覽器

  第四步:類接受到請求以後,根據請求方式(post / get / delete ...)的不一樣調用並執行相應的方法

  第五步:方法返回值的字符串內容發送瀏覽器

1、路由系統  

  web框架其中一個關鍵的地方就是路由系統,路由系統說簡單的就是,一個url對應一個類(其餘框架裏面可能對應的是函數),而後相應的類裏面定義get,post等方法來處理不一樣的請求。並且tronado支持2級路由。下面來看一下代碼實現

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

class StoryHandler(tornado.web.RequestHandler):
    def get(self, story_id):
        self.write("You requested the story " + story_id)

class BuyHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("it.wxtrkbc.com/index")

application = tornado.web.Application([
    (r"/index", MainHandler),
    (r"/story/([0-9]+)", StoryHandler),     # 後面的分頁功能就是基於此實現的
])

application.add_handlers('it.wxtrkbc.com$', [  # 這裏添加2級域名,實驗的話須要改本地host
    (r'/index', BuyHandler),                 # it.wxtrkbc.com:8888/index
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

2、模板引擎  

  模板引擎說簡單點就是將本來的html的某些內容用一些特殊的字符串代替,而後在處理用戶的不一樣的請求時,將html的字符串替換掉,返回給用戶新的一個字符串,這樣就達到了動態的html的效果。Tornado的模板支持「控制語句」和「表達語句」,控制語句是使用 {% 和 %} 包起來的 例如 {% if len(items) > 2 %}。表達語句是使用 {{ 和 }} 包起來的,例如 {{ items[0] }}。控制語句和對應的 Python 語句的格式基本徹底相同。咱們支持 ifforwhile 和 try,這些語句邏輯結束的位置須要用 {% end %} 作標記。還經過 extends 和 block 語句實現了模板繼承,此外還能夠自定義UIMethod以UIModule。下面咱們來簡單的看一下用法

首先看一下html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="static/s1.css" rel="stylesheet"/>
    <script src="static/s1.js"></script>
</head>
<body>
    <div>
        <h2>提交內容</h2>
        <form  method="post" action="/index">
            <input type="text" name="xxx">
            <input type="submit" value="提交">
        </form>
        <h2>顯示內容</h2>
        <ul>
            {% for item in inp %}        //for循環
            <li>{{item}}</li>
            {% end %}
        </ul>

        <h3>{{npm}}</h3>
        <h3>{{func(npm)}}</h3>         // 自定義UIMethod
        <h3>{% module custom() %}</h3>  // 自定義UIModule
    </div>
</body>
</html>

下面來看一下py文件

import tornado.web

import tornado_s1.uimodule as md
from tornado_s1 import uimethod as mt

INPUT_LIST = [11, 22
class MainHandler(tornado.web.RequestHandler):
    def get(self):   
        self.render("s1.html", npm='NPM', inp=INPUT_LIST)

    def post(self):
        username = self.get_argument('xxx', None)
        INPUT_LIST.append(username)
        self.render("s1.html", npm='NPM', inp=INPUT_LIST)
     
settings = {
    'template_path': 'template',
    'static_path': 'static',
    # 'static_url_prefix': '/ss/',
    'ui_methods': mt,
    'ui_modules': md,
}

application = tornado.web.Application([(r"/index", MainHandler), ], **settings)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

最後來看一下自定義的module和method,這裏只是簡單的演示,因此代碼比較簡單

uimethod.py

def func(self, arg):
    return arg.lower()


uimodule.py

from tornado.web import UIModule
from tornado import escape

class custom(UIModule):
    def render(self, *args, **kwargs):
        return '123'

3、模板繼承和靜態緩存  

將一些公用的html,css等寫到通用的文件,而後經過繼承,就能夠獲取母板的內容,而繼承的html裏面只須要寫特有的東西,模板繼承的功能很是實用,而靜態緩存則能夠減小相應的請求資源,下面來看一下具體怎麼用這裏我就只簡單看一下html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link href="{{static_url('css/chouti.css')}}" type="text/css" rel="stylesheet">  // 經過static_url引用靜態文件
    {% block css %} {% end %}
</head>
<body>
    <div class="header">
        <div class="header-content">      
            {% if user_info['is_login'] %}
                <div class="account">
                    <a href="#">{{user_info['username']}}</a>
                    <a href="/logout">退出</a>
                </div>
            {% else %}
                <div class="account">
                    <a href="http://127.0.0.1:8888/register">註冊</a>
                    <a href="http://127.0.0.1:8888/login">登錄</a>
                </div>
            {% end %}
        </div>
    </div>
    <div class="content">
        {% block body %}
        {% end %}       
    </div>
    <a class="back-to-head" href="javascript:scroll(0,0)"></a>
    {% block js %} {% end %}
    <script>

    </script>
</body>
</html>

下面來看一會兒模板的html文件

{% extends '../base/layout.html' %}
{% block css %}
<link href="{{static_url('css/css/common.css')}}" rel="stylesheet">
<link href="{{static_url('css/css/login.css')}}" rel="stylesheet">
{% end %}


{% block body %}

{% end %}

{% block js %}
    <script src="{{static_url('js/jquery-1.12.4.js')}}"></script>
    <script src="{{static_url('js/login.js')}}"></script>
{% end %}

下面來簡單的說如下tronado模板引擎的編譯原理,具體的實現原理等後面有時間再來補充,

相關文章
相關標籤/搜索