tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

Easter79
• 阅读 995

web框架的本质其实就是socket服务端再加上业务逻辑处理, 比如像是Tornado这样的框架. 有一些框架则只包含业务逻辑处理, 例如Django, bottle, flask这些框架, 它们的使用需要依赖包含socket的第三方模块(即 wsgiref)来运行
在python中常见的web框架构建模式有以下两种:

MVC框架:Models      数据相关操作Views       模板html文件Controllers 业务逻辑mvc类似于抽象工厂设计模式,确认了架构以后才考虑应用设计模式,  三层架构大于MVC模式

三层架构将整个项目划分为:表现层(UI)、业务逻辑层(BLL)、数据访问层(DAL)

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

MTV框架:Models      数据相关操作Templates   模板html文件Views       业务逻辑相当于文件夹的归类, 只是命名不同, 所遵循的的思想也只是大同小异flask, django,  bottle  框架中使用模板引擎Jinja2:一个模板系统为Flask提供模板支持,其灵活,快速和安全等优点被广泛使用。web框架本质是socket, 通过socket发送的就是字符串,第一块:协议和方式第二块:请求头第三块:发送内容

第一块:协议和状态第二块:响应头第三块:响应内容

python web 框架分类:自己的socket   ====> Tornado第三方:wsgi +框架 ===>Django, flash,  bottl都是基于wsgiTornado:1.a.导入tornado模块b.写类xxxxxHandler, 必须继承tornado的模块c.路由系统,就是url和类的关系d.程序运行e.模板路劲配置和静态文件配置2.写表单<input type="text" name="use" / >提交后台.self.get_argument('use)后台返回请求:self.render("index.html") 找到文件, 渲染, 得到字符串self.write("字符串")       字符串返回给用户self.redirect("/manager")  url跳转3.模板语言:a.{%%}if else 等代码块b.{{}}c.自定义 UIMethod, UIModule模板语言本质:字符串形式的函数 "def execute(): xxx"    compile  execcookie:    Tornado:        self.set_cookie()        self.get_cookie()Ajax:  本质就是浏览器在用户未感知的情况下发请求  XMLHttpRequest ==>发请求  jquery内部本质还是调用这个发送XMLHttpRequest 

XSS是通过网页使用JavaScript跨站脚本的攻击是代码注入的一种。

  • 通常是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。

  • 攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。

    import os import time from jinja2 import Template 模板语言配合jinja2使用 css, js, 静态文件需要引用

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

from wsgiref.simple_server import make_server  #首次得下载wsgiref模块,
#建立动态响应函数
def application(environ,start_response):#要实现的是做一个web应用
    # 响应首行和响应头,响应头可以是空,响应首行必须有内容
    start_response('200 OK', [('Content-Type', 'text/html')])
    # environ, start_response 这两个参数是两个形参,所以叫什么名字都无所谓,
    # 关键是实参谁来调用它
    # application的调用取决于make_server在调用serve_forever的时候要跑application
    # 相当于实参是什么取决于模块给他放什么参数
    # 这个wsgiref模块里面有个make_server类
    # application(a,b)里面有两个参数,a就是打包好的数据也就是上面的environ
    # 换句话来说environ里面放的就是所有的数据,,按着http协议解析数据:environ
    # start_response:确定响应头的信息,,,按者http协议组装数据:start_response
    print("environ",environ)
    path=environ.get("PATH_INFO")# 当前的请求路径
    if path=="/login":
        with open("template/login.html","rb")as f:
            data=f.read()
        return [data]
    elif path=="/auth":

        return [b"<h1>hello</h1>"]

#类似socket创建的socket对象,绑定IP端口,开启监听3步
s=make_server("127.0.0.1",8084,application)
print("servering")
s.serve_forever()#类似socket建立连接,等待接收用户请求


import os
def new():
    f = open(os.path.join("文件夹","文件夹下级文件","r"))
    data = f.read()
    f.close()
    return data

wsgiref

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
     //静态文件引入写法
    <link rel="stylesheet" href="{{start_url('commons.css')}}" />
</head>
<body>
    <h1>提交内容:</h1>
    <form method="post" action="/index">
        <input type="text" name="xxx" />
        <input type="submit" value="提交" />
    </form>
    <h1>展示内容:</h1>
    //模板语言:1.支持普通方式 2.支持代码块方式,  3.支持自定义方式(uimethod,uimodule)
    <ul>
        //模板语言固定写法(普通方式), 使用for循环展示数据
        {% for item in xxoo %}
        <li>{{item}}</li>
        {% end %}
    </ul>
//转换后的新字符串格式
    <ul>
        <li>[11]</li>
        <li>[22]</li>
    </ul>
    //静态文件引入写法
    <script src='{{static_url("oldboy.js")}}'></script>
</body>
</html>

模板语言

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.ioloop
import tornado.web
INPUTS_LIST = []
# 创建MainHandler类继承tornado.web.RequestHandler
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("hello world")
        #1.打开s1.html文件,读取内容(包含特殊语法)
        #2.xxoo = [11,22] && 读取内容(包含特殊语法),结合起来
        #3.得到新的字符串
        #4.返回给用户
        self.render("s1.html", xxoo = INPUTS_LIST)#默认去当前目录下找文件,渲染成html文件
    def post(self, *args, **kwargs):
        name = self.get_argument('xxx')
        #get_argument获取前台提交的数据,get&post都可以获取,get在url中可以传输,post只能以提交方式传输,两者区别还有长度安全性
        INPUTS_LIST.append(name)
        print("post")
        # self.write("hello world")
        self.render("s1.html", xxoo = INPUTS_LIST)
settings = {
    "tempalte_path": "tpl",#模板路径配置, 相当于配了全局的变量,以后所有使用html的页面都可以使用tempalte找到
    "static_path": "static",#静态文件配置
    "static_url_prefix": "/sss/",
    "ui_methods" : mt,
    "ui_modules": md,


}

# 匹配用户URL,和类,   叫:路由映射,路由系统
application = tornado.web.Application([
    (r"/index",MainHandler)
], **settings) #配置文件settings生效


if __name__ == "__main":
    #socket运行起来
    application.listen("127.0.0.1",8888)
    tornado.ioloop.IOLoop.instance().start()

tornado

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.ioloop
import tornado.web
 
 
 
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.write("Hello, world")
        self.render('js2.html')
 
settings = {
    'template_path':'template',#路径配置,就是自动取到该路径下寻找文件
    'static_path':'static',#静态文件配置,需要特殊处理
}
 
 
 
#路由映射,根据不同url对应到不同的类里面
application = tornado.web.Application([
    (r"/index", MainHandler),
],**settings)
#第二个接收配置文件
 
if __name__ == "__main__":
    application.listen(8888)#监听端口
    tornado.ioloop.IOLoop.instance().start()

s1代码

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.ioloop
import tornado.web
 
input_list=[]#用来接收用户的数据
 
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.write("Hello, world")
        self.render('js2.html',xxxooo = input_list)
    def post(self, *args, **kwargs):
 
 
        name = self.get_argument('jjj')#根据value提取用户输入的名字
        input_list.append(name)
        print(name)
        #1、打开js2.html文件,读取内容(包含特殊语法)
        #2、xxxooo = [11,22,333,44] 读取内容(包含特殊语法)
        #3、得到新的字符串
        #4、将得到新的字符串返回给用户
        self.render('js2.html',xxxooo =  input_list)
settings = {
    'template_path':'template',#路径配置,就是自动取到该路径下寻找文件
    'static_path':'static',#静态文件配置,需要特殊处理
    'static_url_prefix':'/sss/',#标记文件开始的名字
}
 
 
#路由映射,根据不同url对应到不同的类里面
application = tornado.web.Application([
    (r"/index", MainHandler),
],**settings)
#第二个接收配置文件
 
if __name__ == "__main__":
    application.listen(8888)#监听端口
    tornado.ioloop.IOLoop.instance().start()

利用tornado框架实现数据提交,,主要是修改上面s1的代码

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>你好</title>
    <link rel="stylesheet" href="/sss/commons.css">
</head>
<body>
 
<P style="font-size: 50px"> js3</P>
<h1>提交内容</h1>
<form method="post" action="/index">
    <input type="text" name="jjj">
    <input type="submit" value="提交">
</form>
 
 
<h1>显示内容</h1>
<ul>
    {% for item in xxxooo %}
    <li>{{item}}</li>
    {% end %}
</ul>
<!--<script src="sss/jayzhou.js"></script>-->
</body>
</html>

js2的html文件,写了一个表单

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

对于模板语言,主要有三类

模板语言分为三类:
{{}}表达式
{% if %}{% end %}
例如:


    {% for item in xxxooo %}
  • {{item}}

  • {% end %}
#模板语言里通过for循环展示数据
自定义:
uimethod/uimodle

 

然后在上面的s1里面配置好设置文件

settings = {
    'template_path':'template',#路径配置,就是自动取到该路径下寻找文件
    'static_path':'static',#静态文件配置,需要特殊处理
    'static_url_prefix':'/sss/',#标记文件开始的名字
    'ui_methods':uimethod#这个是我们导入的文件
}

在js2.html文件里面的模板语言

<ul>
    {% for item in xxxooo %}
    <li>{{item}}</li>
    <h2>{{func(item)}}</h2>
    {% end %}
<h2>{{func(amp)}}</h2>

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

js2.html文件里面要这样写

<ul>
    {% for item in xxxooo %}
    <li>{{item}}</li>
 
    {% end %}
<h2>{{func(amp)}}</h2>
<h3>{% module custom() %}</h3>
    <!--调用自定义的custom模块-->
 
</ul>

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

# wsgiref在py2中运行正常, 在py3中会报错

# 当我们将执行的index()和news()功能函数放进Controllers业务逻辑处理模块, \
# 将返回结果ret改为文件读写后的内容, 并将该文件放置到Views或者Template模块中, \
# 就形成了最基础版本的MVC和MTV框架

from wsgiref.simple_server import make_server

def index():
    return "This is index "

def news():
    return "welcome to news "

URLS = {
    '/index': index,
    '/news': news,
}

def RunServer(rq, rp):#RunServer(rq, rp) 该方法中rq封装了请求信息, rp封装了响应信息
    rp('200 OK', [('Content-Type', 'text/html')])
    url = rq['PATH_INFO']#获取请求的url连接地址
    if url in URLS.keys():
        ret = URLS[url]()#根据请求的url执行对应的函数
    else:
        ret = '404'
    return ret


if __name__ == '__main__':
    http = make_server('', 8000, RunServer)#这里创建socket服务端, 并传入业务逻辑功能函数RunServer(rq, rp)
    http.serve_forever()#启动服务端, 阻塞进程等待客户端访问, 一旦有访问则执行RunServer(rq, rp)方法

使用wsgiref再加上自己写的业务逻辑自定义一个web框架

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

.body {
    margin: 0;
    background-color: cornflowerblue;
}

commons.css文件内容

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>S1</title>
    <link rel="stylesheet" href="../static/commons.css">
</head>
<body>
<form method="post">
    <input type="text" name="name">
    <input type="submit" value="提交">
</form>
<h1>内容展示</h1>
<ul>
    {% for item in contents %}
        <li>{{item}}</li>
    {% end %}
</ul>
</body>
</html>

index.html文件内容

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.web, tornado.ioloop

# 客户端第一次访问调用的是`MyHandle`类中的`get(self, *args, **kwargs)`方法, 服务端向客户端返回`index.html`文件
# - 客户端浏览器接受到`index.html`文件之后, 在输入框中输入内容并提交之后会调用`post(self, *args, **kwargs)`, 并将输入的内容追加到
# - `self.get_argument('name')` 获取指定参数的内容
# `CONTENTS_LIST`中, 服务端返回`index.html`, 返回过程中`Toranado`
# 会将`CONTENTS_LIST` 的内容渲染到`index.html`之后才会发给客户端浏览器

class MyHandle(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("index.html", contents=CONTENTS_LIST)

    def post(self, *args, **kwargs):
        CONTENTS_LIST.append(self.get_argument('name'))
        self.render('index.html', contents=CONTENTS_LIST)


if __name__ == '__main__':
    CONTENTS_LIST = []#为存放的是输入框输入的内容
    settings = {    #字典表示的是配置文件
        'template_path': 'template',#模板文件的存放位置
        'static_path': 'static', #静态文件的存放位置, 静态文件必须声明, 否则浏览器无法找到静态文件
        'static_url_prefix': 'static/', #静态文件前缀, 减少每个文件引入都要加前缀的麻烦
    }

    application = tornado.web.Application([
        (r"/index", MyHandle)
    ], **settings)
    application.listen(800)#设置服务端的监听端口
    tornado.ioloop.IOLoop.instance().start()#阻塞服务端进程, 等待客户端的访问

index.py文件内容

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

模板引擎的使用:

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

def test_uimethod(self):
    return "uimethod"

uimethod

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

from tornado.web import UIModule

class MyClass(UIModule):
    def render(self, *args, **kwargs):
        return "uimodule"

uimodule.py文件如下

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.web, tornado.ioloop
import uimethod as ut
import uimodule as ud


class MyHandle(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("index.html", ag="this is ag", contents=CONTENTS_LIST)

    def post(self, *args, **kwargs):
        CONTENTS_LIST.append(self.get_argument('name'))
        self.render('index.html', contents=CONTENTS_LIST)


if __name__ == '__main__':
    CONTENTS_LIST = []
    settings = {
        'template_path': 'template',
        'static_path': 'static',
        'static_url_prefix': 'static/',
        'ui_methods': ut,
        'ui_modules': ud
    }

    application = tornado.web.Application([
        (r"/index", MyHandle)
    ], **settings)
    application.listen(80)
    tornado.ioloop.IOLoop.instance().start()

index.py文件如下

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>S1</title>
    <link rel="stylesheet" href='{{static_url("commons.css")}}'>
</head>
<body>
<h1>{{ag}}</h1>
<h1>{{test_uimethod()}}</h1>
<h1>{%module MyClass()%}</h1>
<form method="post">
    <input type="text" name="name">
    <input type="submit" value="提交">
</form>
<h1>内容展示</h1>
<ul>
    {% for item in contents %}
    <li>{{item}}</li>
    {% end %}
</ul>
<hr>
</body>
</html>

index.html文件如下

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

  • 模板引擎中的{{key}}表示取key对应的值, 当key为函数时候执行该函数并取该函数结果. 例如index.html文件中的<h1>{{ag}}</h1> 实际上取得是index.pyself.render("index.html", ag="this is ag", contents=CONTENTS_LIST)中的参数ag的值
  • <h1>{{test_uimethod()}}</h1> 这里执行的是自定义函数, 我们将这个自定义函数写在uimethod.py文件中, 并且在index.py文件中导入, 然后将index.py文件中的settings配置增加一行'ui_methods': ut, 该行内容表示模板引擎可执行自定义函数
  • 模板引擎中的{%%}可用于循环语句和条件语言以及自定义类的执行, {% for item in contents %}此处正是用于循环遍历contents中的内容
  • <h1>{%module MyClass()%}</h1>此处表示模板引擎执行自定义类, 该类的文件对应的是uimodule.py文件, 我们需要在index.pysettings中增加一行'ui_modules': ud, 改行表示模板引擎可使用自定义类
  • 注意, 我们将index.html文件引入css的方式改为了<link rel="stylesheet" href='{{static_url("commons.css")}}'>, static_url()是模板引擎内置的自定义函数, 用该函数引入css文件时候, 仅当css文件内容发生变化时候, 浏览器才会重新缓存该css文件

 以下tornado_cookie:

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.ioloop
import tornado.web
import time
class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("index.html")

class ManagerHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        co = self.get_cookie("auth")
        if co == "1":
            self.render("manager.html")
        else:
            self.redirect("/login")


class LoginHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("login.html",status_text="")

    def post(self, *args, **kwargs):
        username = self.get_argument("username",None)
        pwd = self.get_argument("password",None)
        check = self.get_argument("auth",None)
        if username == "alex" and pwd == "sb":
            if check:
                # self.get_secure_cookie()#原生cookie
                self.set_cookie("username",username,expires_days=7)
                self.set_cookie("auth","1",expires_days=7)
            else:
                r = time.time() + 10#当前时间加10秒
                self.set_cookie("auth","1",expires=r)
                self.set_cookie("username",username,expires=r)
            self.redirect("/manager")
        else:
            self.render("login.html",status_text="登录失败")

class LogoutHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.set_cookie("auth","1",expires=time.time())
        self.redirect("/login")

settings = {
    "template_path": "views",
}

application = tornado.web.Application([
    (r"/index", IndexHandler),
    (r"/login",LoginHandler),
    (r"/manager",ManagerHandler),
    (r"/logout",LogoutHandler),
],**settings)

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

tornodo_cookie

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/login" method="post">
        <input type="text" name="username" />
        <input type="password" name="password" />
        <input type="checkbox" name="auth" value="1">7天免登录
        <input type="submit" value="登录">
        <span style="color:red;">{{status_text}}</span>
    </form>
</body>
</html>

login

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="/logout">退出</a>
    <h1>你的当前银行卡余额:-1000</h1>
</body>
</html>

manager

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

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

index

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

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

        <input type="text" name="username" />
        <input type="password" name="password" />
        <input type="checkbox" name="auth" value="1">7天免登录
        <input type="button" value="登录">
<script src="{{static-url('jquery-3.3.1')}}"></script>
<script>
    function SubmitForm(){
        $.post('/login',{'username':$('#user').val(), 'password':$('#pwd').val()},function(callback){
            console.log(callback);
        })
    }
</script>

jquery发送请求

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

start.py文件:

from controllers import home
import tornado.web
import tornado.ioloop
settings = {
    'template_path': 'views',#模板路劲配置
    'static_path':'statics',#静态文件
}

application = tornado.web.Application([
    # (r'/index',home.IndexHandler),
    (r'/index/(?P<num>\d*)/(?P<nid>\d*)',home.IndexHandler),
],**settings)


if __name__ == '__main__':
    application.listen(8880)
    tornado.ioloop.IOLoop.instance().start()



home.html文件:

import tornado.web
class IndexHandler(tornado.web.RequestHandler):
    def get(self, nid,num):
        print(nid,num)
        self.write("ok")

正则路由匹配 

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

路由系统:

Tornado中支持两种路由系统, 正则路由系统以及二级域名路由系统

# 默认路由系统, 根据url的不容调用不同的类
application = tornado.web.Application([
    (r"/index/(?P<page>\d*)", home.IndexHandle),
], **settings)

#二级路由匹配
application.add_handlers("test.ming.com",[
    (r"/index/(?P<page>\d*)", home.IndexHandle)
])
  • (r"/index/(?P<page>\d*)", home.IndexHandle) 这里我们访问时候需要以类似http://127.0.0.1/index/2的方式访问, 在get或者post接受处理请求的函数应有page参数来接受访问地址最后的整数(我们稍后将根据这个做一个分页的demo)
  • 当我们加入二级域名时候, 默认访问网站执行第一个默认路由系统, 仅当我们访问设置的二级域名才会调用对应的处理器(当前代码指的是test.ming.com的域名)

接下来我们使用基于正则的路由系统来实现网页分页功能.

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

#利用全局变量模拟数据库所有内容
USER_LIST= [
    {'username': 'test', 'email': 'test@163.com'}
]

#利用循环生成多条数据来模拟大量数据依次便于实现分页效果
for i in range(300):
    tmp = {'username': "test - " + str(i), 'email': str(i) + "@vip.com"}
    USER_LIST.append(tmp)

all.py文件如下

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

# 单独用于实现分页功能的类
class Page:
    # current_page表示当前页数, all_item表示总的数据条目
    # 初始化时候将当前页数current_page与总页数all_page加入到对象中
    def __init__(self, current_page, all_item):
        # all_page表示总页数, 每页显示5条数据, more表示余数, 如果大于0则表示应多加一页才能显示完所有的数据
        all_page, more = divmod(all_item, 5)
        if more > 0:
            all_page += 1
        self.all_page = all_page

        # 捕捉异常, 防止传入非法字符冒充页数, 一旦发生异常则直接将当前页current_page设置为1, 表示默认显示第一页
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        # 如果前传入的页数小于1, 则直接默认为第一页
        if current_page < 1:
            current_page = 1
        self.current_page = current_page

    # 根据当前页在每个页面显示11个页码, 此处是开始页码
    @property
    def start_page(self):
        return (self.current_page - 1) * 5

    # 结束页码
    @property
    def end_page(self):
        return self.current_page * 5

    # 显示的页码对应的html字符串, base_url表示可定制的url跳转路径
    def page_str(self, base_url):
        # 定义list_page列表用来暂时存储所有的页码字符串
        list_page = []

        # 如果总页数小于11页, 则直接显示所有页数
        if self.all_page < 11:
            s = 0
            e = self.all_page

        # 总页数大于11页时候
        else:
            # 当前页数小于6则直接显示1到11页
            if self.current_page <= 6:
                s = 1
                e = 11

            # 当前页数大于6页时候
            else:
                # 当前页加上5也之后就大于总页数则直接显示倒数11页
                if self.current_page + 5 > self.all_page:
                    s = self.all_page - 10
                    e = self.all_page

                # 当前页数大于6页并且加上5页并不超过总页数时候, 显示当前页前后5页以及当前页
                else:
                    s = self.current_page - 5
                    e = self.current_page + 5

        # 首页设置
        first_page = '<a href="https://my.oschina.net/%s/1">首页</a>' % (base_url)
        list_page.append(first_page)

        # 上一页设置
        # 当前页小于等于第一页时候, 点击上一页不做任何操作(这里理论上是不会有小于的情况)
        if self.current_page <= 1:
            pre_page = '<a href="javascript:void(0);">上一页</a>'

        # 当前页大于第一页, 点击上一页则直接跳转到上一页
        else:
            pre_page = '<a href="https://my.oschina.net/%s/%s">上一页</a>' % (base_url, self.current_page - 1)
        list_page.append(pre_page)

        # 根据上边条件过滤后的页码起始位置s以及页码终止位置e来生成对应的11条页码对应的html字符串
        for p in range(s, e + 1):
            if p == self.current_page:
                tmp = '<a class="active" href="https://my.oschina.net/index/%s">%s</a>' % (p, p)
            else:
                tmp = '<a href="https://my.oschina.net/%s/%s">%s</a>' % (base_url, p, p)
            list_page.append(tmp)

        # 下一页设置
        # 下一页要大于或者等于最大页数时候, 不做任何操作
        if self.current_page >= self.all_page:
            next_page = '<a href="javascript:void(0);">下一页</a>'
        else:
            next_page = '<a href="https://my.oschina.net/%s/%s">下一页</a>' % (base_url, self.current_page + 1)
        list_page.append(next_page)

        # 尾页设置
        last_page = '<a href="https://my.oschina.net/%s/%s">尾页</a>' % (base_url, self.all_page)
        list_page.append(last_page)

        # 页面跳转
        jump_page = """<input type="text" /><a onclick='JumpTo("%s",this)'>GO</a>""" % base_url
        # 页面跳转的js代码, 本质就是location.href的使用
        jspt = """<script>
            function JumpTo(base_url,th){
                var val=th.previousElementSibling.value;
                if(val.trim().length>0){
                    location.href="https://my.oschina.net/"+base_url+"/"+val
                }
            }
        </script>"""
        list_page.append(jump_page)
        list_page.append(jspt)
        return "".join(list_page)

pager.py文件如下

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.web
from commons.all import USER_LIST
from commons.pager import Page

class IndexHandle(tornado.web.RequestHandler):
    def get(self, c_page):
        # c_page表示url传递过来的当前页数, len(USER_LIST)表示总共有多少条数据
        pg = Page(c_page, len(USER_LIST))
        #开始页数
        start = pg.start_page
        #尾页
        end = pg.end_page
        #当前显示的数据片段
        current_list = USER_LIST[start:end]
        #传入index并返回对应的下面分页部分的html代码
        str_page = pg.page_str('index')
        #将当前显示的数据片段, 当前页数以及分页的html代码返回给浏览器
        self.render('index.html', list_info=current_list, current_page=pg.current_page, str_page=str_page)

    def post(self, c_page):
        #获取传入的username的值
        username = self.get_argument('username', None)
        #获取传入的email的值
        email = self.get_argument("email", None)
        #生成临时的字典对象, 表示一个完整的数据片段
        tmp = {'username': username, 'email': email}
        #将该数据片段加入的全局变量USER_LIST, 这里用全局变量模拟数据库获取的数据
        USER_LIST.append(tmp)
        self.redirect('/index/' + c_page)

home.py文件如下

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .pager a{
            display: inline-block;
            padding: 5px;
            margin: 3px;
            background-color: aquamarine;
        }
        .pager a.active {
            background-color: crimson;
            color: aliceblue;
        }
    </style>
</head>
<body>
<h1>提交数据</h1>
<form method="post" action="/index/{{current_page}}">
    <input name="username" type="text">
    <input name="email" type="text">
    <input type="submit" value="提交">
</form>
<h1>显示数据</h1>
<table border="1">
    <thead>
    <tr>
        <th>用户名</th>
        <th>邮箱</th>
    </tr>
    </thead>
    <tbody>
    {%for tmp in list_info%}
    <tr>
        <td>{{tmp['username']}}</td>
        <td>{{tmp['email']}}</td>
    </tr>
    {%end%}
    </tbody>
</table>
<div class="pager">
    <!--加raw 以此来直接执行原始的字符串, 不做转义-->
    {%raw str_page%}
</div>
</body>
</html>

index.html文件如下

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.web,tornado.ioloop
from controllers import home

if __name__ == '__main__':
    settings = {
        # 模板路径配置
        'template_path': 'views',
    }

    application = tornado.web.Application([
        (r"/index/(?P<c_page>\d*)", home.IndexHandle),
    ], **settings)
    application.listen(80)
    tornado.ioloop.IOLoop.instance().start()

start.py文件如下

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

模板引擎:

基本使用
    继承,extends    页面整体布局用继承
    导入,include    如果是小组件等重复的那么就用导入

Tornado框架中, 模板引擎能带给我们很多方便, 它是便捷展现页面的极佳方式. 在上一节中我们介绍了模板引擎对于{{}}以及对于 {%%}的用法. 我们简单回顾一下:

**{{}}使用: **

  • 直接取服务端在render()函数中传递参数的值, 例如服务端中有self.render('index.html', contents=CONTENTS_LIST), 在html文件中有{{contents}} 则表示在html中取服务端的CONTENTS_LIST的内容
  • 我们通过配置'ui_methods': 需要执行的自定义python模块,之后, 我们可以在html文件中通过{{自定义python模块中的函数名()}}来执行对应的函数取得该函数的返回结果以此来显示

**{%%}的使用: **

  • {%for tmp in iterable%} 用于循环语句, 注意要加上{%end%}结束语句
  • {%if condition%} 用于条件判断, 同样同上需要结束语句
  • 通过配置ui_modules : 需要执行的python模块 之后, 我们可以在文件中通过{%module python模块名()%}来直接执行该模块中对应的方法, 注意该模块需要继承tornado.web.UIModule

以上有不懂的请参照上一篇博客(Tornado框架01-入门总概)中的具体实例实现后再对应解释来理解

接下来我们老规矩, 先使用一下模板引擎的继承之后, 再详细介绍

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.web


class IndexHandle(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("extend/index.html")


class AccountHandle(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("extend/account.html")

home

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

{%extends "../template/master.html"%}

<!--自定义css具体内容-->
{%block tm_css%}
    <style type="text/css">
        .page-content{
            background-color: green;
        }
    </style>
{%end%}

<!--#自定义的文本内容-->
{%block tm_content%}
    <h1>This is Account</h1>
{%end%}

<!--#自定义的js文件-->
{%block tm_js%}
    <script type="text/javascript">
        console.log('This is Account')
    </script>
{%end%}

acount.html

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

{%extends "../template/master.html"%}

<!--对应的自定义css的具体内容-->
{%block tm_css%}
    <style type="text/css">
        .page-content{
            background-color: yellow;
        }
    </style>
{%end%}

<!--自定义的文本内容-->
{%block tm_content%}
    <h1>This is Index</h1>
{%end%}

<!--自定义的js文件-->
{%block tm_js%}
    <script type="text/javascript">
        console.log('This is Index')
    </script>
{%end%}

index.html

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Master</title>
    <style type="text/css">
        * {
            margin: 0px;
            padding: 0px;
        }
        .page-header{
            height: 50px;
            background-color: red;
        }
        .page-content {
            height: 200px;
            background-color: azure;
        }
        .page-footer{
            height: 50px;
            background-color: black;
        }
    </style>

    <!--为后边自定义的css命名并占位置-->
    {%block tm_css%}{%end%}
</head>
<body>
    <div class="page-header"></div>
    <div class="page-content">
        <!--自定义的内容, 命名并占位-->
        {%block tm_content%}{%end%}
    </div>
    <div class="page-footer"></div>

    <!--自定义的js文件位置-->
    {%block tm_js%}{%end%}
</body>
</html>

master.html

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.web, tornado.ioloop
from controllers import home

if __name__ == '__main__':
    CONTENTS_LIST = []
    settings = {
        'template_path': 'views',

    }

    application = tornado.web.Application([
        (r"/index", home.IndexHandle),
        (r"/account", home.AccountHandle),
    ], **settings)

    application.listen(80)
    tornado.ioloop.IOLoop.instance().start()

start.py

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

  • 从运行结果来看, 两个网页的主体结构相同, 只是里边包含的css具体样式, 具体内容以及js文件不同
  • 要继承模板文件来使用我们要在当前文件首行写上{%extends "../template/master.html"%} , 这里表示当前文件以master.html来进行渲染

{%block tm_css%}

{%end%}```
index.html的这部分其实就是master.htmltm_css的具体内容

  • master.html文件中{%block tm_css%}{%end%}相当与为后面具体要写入的内容做一个占位符, 并且起名为tm_css.

 使用模板的继承可以重复使用相同结构的模板, 可以大大减少代码量. 但是有时候并不是所有的网页结构都是我需要的, 我们会想要单独包含所有网页都有的相同的一小部分内容. 此时就需要模板文件的包含来实现

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<form>
    <input type="text">
    <input type="submit" value="提交">
</form>

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

{%extends "../template/master.html"%}

<!--对应的自定义css的具体内容-->
{%block tm_css%}
    <style type="text/css">
        .page-content{
            background-color: yellow;
        }
    </style>
{%end%}

<!--自定义的文本内容-->
{%block tm_content%}
    <h1>This is Index</h1>
    {%include "../include/form.html"%}
    {%include "../include/form.html"%}
    {%include "../include/form.html"%}
{%end%}

<!--自定义的js文件-->
{%block tm_js%}
    <script type="text/javascript">
        console.log('This is Index')
    </script>
{%end%}

将上面的index.html修改如下

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

cookie:

cookie:在浏览器端保存键值对,       特性:每次http请求都会附加在请求中并发送给服务器端

 tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.web


class IndexHandle(tornado.web.RequestHandler):
    def get(self):
        username = self.get_argument('u', None)
        if not username:
# 设置未加密的cookie, 键为'name', 值为test
            self.set_cookie('name', 'test')
 #设置加密cookie, 键为'user', 值为test.
# 设置加密cookie我们需要在配置中添加自定义的加密串(俗称对加密结果加盐)"cookie_secret": 'test-secret,'
            self.set_secure_cookie('user', 'test')
            self.redirect('/admin')

    def post(self):
        pass


class AdminHandle(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
#获取指定key未加密的cookie的值
        name = self.get_cookie('name', None)
#获取指定key的加密后的cookie的值
        user = self.get_cookie('user', None)
        print('name: ', name, "\nuser: ", user)


# 对于set_cookie()和set_secure_cookie()都用以下常见参数
# name 表示传入cookie的键
# value 表示传入cookie的name对应的值
# domain=None 表示域名
# expires=None 设置过期时间, 这里单位为秒
# path="/" 表示当前的cookie在那些路径下有效, /表示当前域名下所有的路径均有效
# expires_days=None 设置过期时间, 单位为天

home

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.web, tornado.ioloop
from controllers import home

if __name__ == '__main__':
    settings = {
        # 模板路径配置
        'template_path': 'views',
        "cookie_secret": 'test-secret,'
    }

    application = tornado.web.Application([
        (r"/index", home.IndexHandle),
        (r"/admin", home.AdminHandle),
    ], **settings)
    application.listen(803)
    tornado.ioloop.IOLoop.instance().start()

start

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

加密cookie的加密和解密原理:

 tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

  • 解密时候将加密cookie中的base64(test)也就是加密后的值和时间戳再加上cookie_secret生成新的加密串和加密cookie中的加密串比较, 若相同则合法验证通过, 然后再通过反解加密base64(test)取其本来的值

JavaScript操作Cookie

由于Cookie保存在浏览器端,所以在浏览器端也可以使用JavaScript来操作Cookie。

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>adasd</h1>
    <script>
        /*
设置cookie,指定秒数过期
 */
function setCookie(name,value,expires){
    var temp = [];
    var current_date = new Date();
    current_date.setSeconds(current_date.getSeconds() + 5);
    document.cookie = name + "= "+ value +";expires=" + current_date.toUTCString();
}
    </script>
</body>
</html>


然后在浏览器中访问
setCookie("k22=11",5)            设置cookie,超时时间为5秒
undefined
document.cookie                查看cookie
"k1=999; k22=11= 5"
document.cookie                5秒后查看
"k1=999"





<!--toUTCString() 方法可根据世界时 (UTC) 把 Date 对象转换为字符串,并返回结果-->
<!--设置cookie,指定秒数过期-->
        <!--current_date.getSeconds()      获取当前秒-->
        <!--current_date.setSeconds        设置秒-->
        <!--data.setDate(data.getDate()+7),表示获取超过现在7天的时间-->
        <!--current_date                  当前时间+5秒-->
        <!--toUTCString()                   当前统一时间-->

HTML代码

对于参数

  • domain   指定域名下的cookie
  • path       域名下指定url中的cookie
  • secure    https使用

jquery中设置cookie

要用需要下载  这里

1、    导入jquery
2、    导入jQuery Cookie Plugin v1.4.1
注意点:
    如果用jquery导入的时候expires这里如果为数字的时候,表示天数
    如果不想用天数,那么要用,这里的超时时间必须要用toUTCString()统一时间

current_date.setSeconds(current_date.getSeconds() + 5); 用天数,然后用字符串拼接的方式";expires="+ current_date.toUTCString()
等来设置时间,js数组的.join方法是吧数组变成字符串
            $.cookie(“k1”,”22”,{“path”:””,”domin”:””,expires=1})
            上面的cookie中的数组,在内部用了join方法分割成了字符串

tornado支持两种方式

  • 一、简单的方式

  • 二、签名的方式

    首先服务端让浏览器端生成cookie的时候会经过base64加密,首先生成加密串, 注意这里的当前时间是

    import time time.time() 1491471613.5271676 --->生成的这个值就是当前时间

    加密串 =v1(value)+当前时间+内部自定义字符串 之后生成的这个cookie就是k1(key)=v1|加密串|当前时间

    如何验证这个cookie有没有被篡改: 客户端向浏览器端发送请求:会把v1和加密串和当前时间发送给浏览器,浏览器内部会经过md5生成一个新的加密串
    自定义字符串+发送过来的时间+v1等于新的加密串,然后加密串进行对比,如果一致就能通过

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.ioloop
import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    #这里判断判断用户登录
    def get(self):
        if self.get_argument("u",None) in ["aa","eric"]:
            self.set_cookie("name",self.get_argument("u"))
            # self.set_secure_cookie("name",self.get_argument("u"))
        else:
            self.write("请登录")

class ManagerHandler(tornado.web.RequestHandler):
    #如果有cookie的时候就登录
    def get(self):
        if self.get_cookie("name",None) in ["aa","eric"]:
            self.write("欢迎登录:"+self.get_cookie("name"))
        else:
            self.redirect("/index")

settings={
    "template_path":"views",
    "static_path":"statics"
}
application=tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManagerHandler)
],**settings)

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

# 上面就是用一种简单的模式登录,登录的时候
# 在浏览器中输入
# http://127.0.0.1:8000/index?u=aa
# 之后就会执行IndexHandler方法中的get方法首先存入用户输入的cookie,对比后台,然后访问manager网站的时候,判断,如果有对应的cookie那么就会出现欢迎登录

基于cookie实现用户验证

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.ioloop
import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    #这里判断判断用户登录
    def get(self):
        if self.get_argument("u",None) in ["alex","eric"]:
# 这里设置加密的cookie
            self.set_secure_cookie("user",self.get_argument("u"))
        else:
            self.write("请登录")


class ManagerHandler(tornado.web.RequestHandler):
    #如果有cookie的时候就登录
        def get(self):
# 获取加密的cookie
             if str(self.get_secure_cookie("user",None),encoding="utf-8") in ["alex","eric"]:
                self.write("欢迎登录:"+str(self.get_secure_cookie("user")))
             else:
                self.redirect("/index")

settings={
    "template_path":"views",
    "static_path":"statics",
# 这必须设置配置
    "cookie_secret":"hello",
}
application=tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManagerHandler)
],**settings)

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

# 设置加密的cookie用set_secure_cookie()方法,如果获取cookie的时候用get_secure_cookie()
# 注意这里获取加密cookie
# 注意:这里获取的cookie是byte类型,所以必须要转换一下类型

下面是带签名的cookie

Cookie 很容易被恶意的客户端伪造。加入你想在 cookie 中保存当前登陆用户的 id 之类的信息,
你需要对 cookie 作签名以防止伪造。Tornado 通过 set_secure_cookie 和 get_secure_cookie
 方法直接支持了这种功能。 要使用这些方法,你需要在创建应用时提供一个密钥,名字为 cookie_secret。 
你可以把它作为一个关键词参数传入应用的设置中

签名Cookie的本质是:

写cookie过程:

将值进行base64加密
对除值以外的内容进行签名,哈希算法(无法逆向解析)
拼接 签名 + 加密值
读cookie过程:

读取 签名 + 加密值
对签名进行验证
base64解密,获取值内容
注:许多API验证机制和安全cookie的实现机制相同

session:

我们将许多信息放在cookie中势必会造成浏览器端的臃肿, 此时便需要在服务端保存原本在浏览器端的那些键值对. 在浏览器端只需存储一个表示身份的随机加密字符串, 当浏览器端访问服务端时候携带该字符串, 经过比较, 验证合法之后便可以取该用户在服务端存储的相应信息. 但是在Tornado中并没有session的模块, 我们需要自定义来实现.

session其实就是定义在服务器端用于保存用户回话的容器,其必须依赖cookie才能实现。

所有的web框架都是session[key]=value的方法实现的

优化后代码:

在Tornado框架中,默认执行Handler的get/post等方法之前默认会执行 initialize方法,所以可以通过自定义的方式使得所有请求在处理前执行操作

这里的initialize就是钩子函数

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

#定义tornado中的钩子函数和反射函数来优化下面的类
class BaseHandler(tornado.web.RequestHandler):
    def initialize(self):
        self.session=Session(self)

class IndexHandler(BaseHandler):
    #这里判断判断用户登录,get方法是被反射调用的getattr
    def get(self):
        if self.get_argument("u",None) in ["aa","eric"]:
            self.session.set_value("is_login",True)
            self.session.set_value("name",self.get_argument("u",None))
        else:
            self.write("请登录")

class ManagerHandler(BaseHandler):
    def get(self):
        val=self.session.get_value("is_login")
        if val:
            self.write(self.session.get_value("name"))
        else:
            self.write("请重新登录")

两个类继承一个共同的父类,利用tornado内置的钩子函数来优化代码

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web

#这个字典必须定制成为全局变量用来保存用户的信息,如果是局部变量,
# 那么http请求断开下次用户登录这个用户信息就会消失
container={}

#把Sesson封装起来
class Session:
    def __init__(self,handler):
        self.handler=handler
        self.random_str=None  #用户连接初始化随机数
    def __genarate_random_str(self):   #创建随机字符串
        import hashlib
        import time
        #首先通过md5生成随机数据,电脑中的数据都是16进制保存的
        obj=hashlib.md5()
        ## 加入自定义参数来更新md5对象
        obj.update(bytes(str(time.time()),encoding="utf-8"))
        # 得到加严后的十六进制随机字符串来作为用户的索引
        random_str=obj.hexdigest()
        return random_str

    def __setitem__(self,key,value):
        #这里判断如果服务端没有随机数
        if not self.random_str:     #用户连接,首先服务端没有随机数,那么去客户端拿随机数
            random_str=self.handler.get_cookie("__kakaka__") #去客户端中拿随机数
            if not  random_str:     #如果客户端也没有随机数,那么服务端就自己创建随机数
                random_str=self.__genarate_random_str()  #创建随机数
                container[random_str]={}                 #清空随机数字典中的内容
            else:
                if random_str in container.keys(): #如果客户端有随机数,并且为真那么就直接登录成功
                    pass
                else:              #如果客户端到的随机数是伪造的,那么服务端就自己创建随机数
                    random_str=self.__genarate_random_str()
                    container[random_str]={}
            self.random_str=random_str   #最后把上面判断出来的随机数传递给类
        container[self.random_str][key]=value
        #这里是为写超时时间做准备
        self.handler.set_cookie("__kakaka__",self.random_str)  #设置cookie给浏览器,这里可以设置超时时间


    def __getitem__(self,key):  #获取值, 注意加密方式返回的cookie的值是bytes类型的
        random_str=self.handler.get_cookie("__kakaka__")
        # 如果客户端没有随机字符串(索引值为空表示当前用户是新用户,直接返回空,),就结束,程序到此终止
        if not  random_str:
            return None
        user_info_dict=container.get(random_str,None)#客户端有随机字符串,但是内容服务器不匹配,就退出
        if not user_info_dict:
            return None
        value=user_info_dict.get(key,None)   #前面如果都满足,有值就拿值,没有就为None
        return value

#定义tornado中的钩子函数和反射函数来优化下面的类
class BaseHandler(tornado.web.RequestHandler):
    def initialize(self):
        self.session=Session(self)

class IndexHandler(BaseHandler):
    #这里判断判断用户登录,get方法是被反射调用的getattr
    def get(self):
        if self.get_argument("u",None) in ["aa","eric"]:
            self.session["is_login"]=True
            self.session["name"]=self.get_argument("u",None)
            # self.session.set_value("is_login",True)
            # self.session.set_value("name",self.get_argument("u",None))
        else:
            self.write("请登录")

class ManagerHandler(BaseHandler):
    def get(self):
        val=self.session["is_login"]
        if val:
            self.write(self.session["name"])
        else:
            self.write("请重新登录")


settings={
    "template_path":"views",
    "static_path":"statics",
    "cookie_secret":"hello",
}
application=tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManagerHandler)
],**settings)

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

__getitem__/__setitem__优化后

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

用户如果直接连接manager会提示必须登录,主要原因是浏览器cookie中没有登录信息
1、    用户访问index网页的时候就是访问IndexHandler这个类,用户连接,服务器内部就会初始化随机数
2、    服务器就会执行set_value方法,并且传入参数is_login参数,首先为了判断用户是否第一次登陆,所以用if not self.random_str,没有就用get_cookie()方法去客户端中拿随机数,这里需要判断,如果客户端也没有随机数,那么服务端就要自己创建随机数,并且把这个随机数传递给服务器这个类;如果客户端有随机数,要判断这个随机数是否是伪造的,如果是伪造的,服务器需要自己创建随机数,并且把这个随机数传递给服务器这个类;之后把is_login参数替代key传递给session这个字典求出来value这个值,并且设置一下这个cookie传递给浏览器;然后设置key为name的cookie
3、    用户访问manager这个网站,会执行get方法,并且获取浏览器随机数,如果浏览器中没有随机数或者浏览器的随机数是伪造的,那么就会退出,如果经过了2这个步骤,那么就能登录成功并且得到设置cookie中key为name的值

流程详解

验证码:

验证码原理在于后台自动创建一张带有随机内容的图片,然后将内容通过img标签输出到页面。
这个验证码是放在tornado的session里面的
验证码机制:服务器首先创建验证码,并且把验证码放入到随机数这个字典里面,用户通过get方法接收到验证码,然后用户输入验证码和账户信息发送给服务器,服务器通过对比用户发来的验证码和自己产生的验证码,(这里要创建不分辨大小写,可以让用户输入的和自己产生的转成全部大写或者全部小写)对比,如果一样那么就显示登录成功,如果没有一样,那么就显示输入的验证码错误。并且在前端添加一个点击事件,只要用户一点击那么验证码就会刷新pip3 install pillow  安装图像处理模块

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        .aa{
            cursor: pointer;
        }
    </style>
</head>
<body>
    <h1>请输入登录信息</h1>
    <form action="/login" method="post">
        <p><input name="user" type="text" placeholder="用户名" /></p>
        <p><input name="pwd" type="password" placeholder="密码" /></p>
        <p>
            <input name='code' type="text" placeholder="验证码" />
            <img class="aa" src="/check_code" onclick='ChangeCode();' id='imgCode'>
        </p>
        <input type="submit" value="提交"/><span style="color: red">{{status}}</span>
    </form>
   <script type="text/javascript">

       function ChangeCode() {
            var code = document.getElementById('imgCode');
            //url后面只能添加问号,添加问号就是改变地址
            code.src += '?';
        }
    </script>
</body>
</html>

login.html

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

#/usr/bin/env python
#-*- coding:utf-8-*-
import tornado.ioloop
import tornado.web
import tornado.httpserver
import tornado.ioloop
import tornado.process
import tornado.web
#


#这个字典必须定制成为全局变量用来保存用户的信息,如果是局部变量,
# 那么http请求断开下次用户登录这个用户信息就会消失
container={}

        #把Sesson封装起来
class Session:
    def __init__(self,handler):
        self.handler=handler
        self.random_str=None  #用户连接初始化随机数
    def __genarate_random_str(self):   #创建随机字符串
        import hashlib
        import time
        #首先通过md5生成随机数据,电脑中的数据都是16进制保存的
        obj=hashlib.md5()
        obj.update(bytes(str(time.time()),encoding="utf-8"))
        random_str=obj.hexdigest()
        return random_str
    def __setitem__(self,key,value):
        #这里判断如果服务端没有随机数
        if not self.random_str:               #用户连接,首先服务端没有随机数,那么去客户端拿随机数
            random_str=self.handler.get_cookie("__kakaka__") #去客户端中拿随机数
            if not  random_str:                           #如果客户端也没有随机数,那么服务端就自己创建随机数
                random_str=self.__genarate_random_str()  #创建随机数
                container[random_str]={}                 #清空随机数字典中的内容
            else:
                if random_str in container.keys():    #如果客户端有随机数,并且为真那么就直接登录成功
                    pass
                else:                                   #如果客户端到的随机数是伪造的,那么服务端就自己创建随机数
                    random_str=self.__genarate_random_str()
                    container[random_str]={}
            self.random_str=random_str   #最后把上面判断出来的随机数传递给类
        container[self.random_str][key]=value
        #这里是为写超时时间做准备
        self.handler.set_cookie("__kakaka__",self.random_str)  #设置cookie给浏览器,这里可以设置超时时间


    def __getitem__(self,key):  #获取值
        random_str=self.handler.get_cookie("__kakaka__")
        if not  random_str:#如果客户端没有随机字符串,就结束
            return None
        user_info_dict=container.get(random_str,None)#客户端有随机字符串,但是内容服务器不匹配,就退出
        if not user_info_dict:
            return None
        value=user_info_dict.get(key,None)   #前面如果都满足,有值就拿值,没有就为None
        return value

#定义tornado中的钩子函数和反射函数来优化下面的类
class BaseHandler(tornado.web.RequestHandler):
    def initialize(self):
        self.session=Session(self)

class IndexHandler(BaseHandler):
    #这里判断判断用户登录,get方法是被反射调用的getattr
    def get(self):
        if self.get_argument("u",None) in ["aa","eric"]:
            self.session["is_login"]=True
            self.session["name"]=self.get_argument("u",None)
            # self.session.set_value("is_login",True)
            # self.session.set_value("name",self.get_argument("u",None))
        else:
            self.write("请登录")

class ManagerHandler(BaseHandler):
    def get(self):
        val=self.session["is_login"]
        if val:
            self.write(self.session["name"])
        else:
            self.write("请重新登录")
# class CheckCodeHandler(BaseHandler):
#     def get(self):
#         import io
#         import check_code
#
#         mstream = io.BytesIO()
#         img, code = check_code.create_validate_code()
#         img.save(mstream, "GIF")
#         # self.session["CheckCode"] = code
#         self.write(mstream.getvalue())
class MainHandler(BaseHandler):
    def get(self):
        self.render('login.html',status="")
    def post(self, *args, **kwargs):
        user=self.get_argument("user",None)
        pwd=self.get_argument("pwd",None)
        code=self.get_argument("code",None)
        #比较用户输入的验证码和服务器给出的验证码的值
        check_code=self.session["CheckCode"]
        if code.upper()==check_code.upper():
            self.write("验证码正确")
        else:
            # self.redirect("/login")
            self.render("login.html",status="验证码错误")


class CheckCodeHandler(BaseHandler):
    def get(self, *args, **kwargs):
        #生成图片并且返回
        import io
        import check_code
        #建立内存级别文件,相当于一个容器
        mstream = io.BytesIO()
        #创建图片并且写入验证码
        img, code = check_code.create_validate_code()
        #将图片内容写入到IO中mstream
        img.save(mstream, "GIF")
        #为每个用户保存其对应的验证码
        self.session["CheckCode"] = code
        self.write(mstream.getvalue())

settings={
    'template_path': 'views',
    'static_path': 'static',
    "static_url_prefix":"/statics/",
    "cookie_secret":"hello",
    # "xsrf_cookies":True,
}

application=tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManagerHandler),
    # (r"/login",LoginHandler),
    (r"/login",MainHandler),
    (r"/check_code",CheckCodeHandler),


],**settings)

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

python代码

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

下载下面源码之后,需要把check_code.py和Monaco.ttf导入到这个代码目录中(仅仅限制与python3.5)

验证码Demo源码下载:猛击这里

CSRF:

会集群要会:分布式哈希haxi redis
CSRF限制post请求的
用户访问是先请求服务器调用get请求,然后发送post请求,之后服务器会给用户一个随机字符串,当用户离开后,下次再访问会带着这个随机字符串访问服务器,如果用户没有这个随机字符串,那么CSRF会阻止这个用户请求,这样可以使服务器免遭受恶意攻击造成服务器宕机

要加上CSRF:

1、在配置文件中加上配置文件”xsrf_cookies”:True

2、在前台代码中加上{% raw xsrf__form_html %}

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

class CsrfHandler(BaseHandler):
    def get(self, *args, **kwargs):
        self.render("csrf.html")
    def post(self, *args, **kwargs):
        self.write("csrf.post")
settings={
    'template_path': 'views',
    'static_path': 'static',
    "static_url_prefix":"/statics/",
    "cookie_secret":"hello",
    "xsrf_cookies":True,   这里加上配置文件
}

application=tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManagerHandler),
    # (r"/login",LoginHandler),
    (r"/login",MainHandler),
    (r"/check_code",CheckCodeHandler),
    (r"/csrf",CsrfHandler)


],**settings)

View Code

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<form action="/csrf" method="post">
    {% raw xsrf_form_html() %}
    <p><input type="text" placeholder="用户"/></p>
    <p><input type="text" placeholder="密码"/></p>
    <p>
        <input name="code" type="text" placeholder="验证码"/>
        <!--<img src="https://my.oschina.net/check_code">-->
    </p>
    <input type="submit" value="Submit"/>

html

提交的是AJAX的post请求

如果你提交的是 AJAX 的 POST 请求,你还是需要在每一个请求中通过脚本添加上 _xsrf 这个值。下面是在 FriendFeed 中的 AJAX 的 POST 请求,使用了 jQuery 函数来为所有请求组添加 _xsrf 值:

function getCookie(name) {
    var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
    return r ? r[1] : undefined;
}
 
jQuery.postJSON = function(url, args, callback) {
    args._xsrf = getCookie("_xsrf");
    $.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
        success: function(response) {
        callback(eval("(" + response + ")"));
    }});
};

对于 PUTDELETE 请求(以及不使用将 form 内容作为参数的 POST 请求) 来说,你也可以在 HTTP 头中以 X-XSRFToken这个参数传递 XSRF token。

如果你需要针对每一个请求处理器定制 XSRF 行为,你可以重写 RequestHandler.check_xsrf_cookie()。例如你需要使用一个不支持 cookie 的 API, 你可以通过将 check_xsrf_cookie() 函数设空来禁用 XSRF 保护机制。然而如果 你需要同时支持 cookie 和非 cookie 认证方式,那么只要当前请求是通过 cookie 进行认证的,你就应该对其使用 XSRF 保护机制,这一点至关重要。

1、cookie和session的区别
1)cookie是保存在客户端的,session是保存在服务端的,因为服务器端,表示可能在内存中,可能在数据库端,可能在缓存中统称为服务器端
2、session和cookie有什么联系?:
答:有。session是通过cookie人为构建起来的,在web开发里面本身没有session这个东西的。在服务器端可以高层一个数据库,可以在内存中搞成一个字典,每一次用户来访问的时候,给用户发一对token,下一次,用户访问再带着这一对token来,服务器端就知道你是不是上一次的你。如果再问就来画一张图

3、分页 XSS  跨站脚本攻击
4、CSRF/别名XSRF工作方式:
答:跨站请求伪造,  理解为:攻击者盗用了你的身份,以你的名义发送恶意请求
验证:第一次请求的时候是get方式请求,防止没有经过验证就来post请求,造成大并发机器宕机
5、    Ajax
为什么要有Ajax
答:防止页面批量刷新
    利用:
        iframe   忽略
        XMLHttpRequest
            自己写
            xhr
            xhr.open()
            xhr.onreadystatechange
            xhr.send()
        jQuery
        会用下面的就会jquery,ajax
            $.ajax({
            url:
            type
            data
            dataType
            success
            error
})
6、    验证码、
7、    上传文件
form标签
        form标签 enctype=““form标签里面必须要有这个才能进行上传文件
通过Ajax上传文件
        利用formDate()
            XMLHttpRequest
            jQuery
        iframe+form标签为了兼容性设计,ifram就相当于设置一个通道,form把数据提交到这个通道,然后不刷页面上传文件

tornado 结合前端进行文件上传:

在表单中我们获取用户提交的数据,使用的是get_argument,复选框使用的是get_arguments,但是文件的不一样,文件的使用request.files。

form文件上传

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>上传文件</title>
</head>
<body>
    <form id="my_form" name="form" action="/index" method="POST"  enctype="multipart/form-data" >
        <input name="fff" id="my_file"  type="file" />
        <input type="submit" value="提交"  />
    </form>
</body>
</html>

HTML

注意:

form文件上传,一定要在form表单上设置enctype的参数。enctype="multipart/form-data"。不然上传无法成功。

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')

    def post(self, *args, **kwargs):
        file_metas = self.request.files["fff"]
        # print(file_metas)
        for meta in file_metas:
            file_name = meta['filename']
            with open(file_name, 'wb') as up:
                up.write(meta['body'])


settings = {
    'template_path': 'template',
}

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

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

PYthon

说明:

1、代码中self.request封装了所有发送过来请求的内容。

2、self.request.files:可以获取上传文件的所有信息。此方法获取的是一个生成器,内部是由yield实现的,因此我们在利用此方法返回的对象的时候,不能通过下标获取里面的对象,只能通过迭代的方法。

3、迭代出来的对象的filename:就表示上传文件的文件名。

4、迭代出来的对象的body:表示上传文件的内容。获取的文件内容是字节形式的。

ajax上传文件

  • 原生ajax
  • jquery

原生ajax上传文件

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <input type="file" id="img" />
    <input type="button" onclick="UploadFile();" />
    <script>
        function UploadFile(){
            var fileObj = document.getElementById("img").files[0];
 
            var form = new FormData();
            form.append("k1", "v1");
            form.append("fff", fileObj);
 
            var xhr = new XMLHttpRequest();
            xhr.open("post", '/index', true);
            xhr.send(form);
        }
    </script>
</body>
</html>

HTML

说明:

代码中利用原生的ajax进行文件上传。

关键点:

1、获取文件对象,通过files[0],获取当前上传的文件对象。

2、通过FormData(),实例化一个对象form对象。

3、然后将要传递的参数,文件以键和值以逗号分隔的形式append到form对象中去。

4、然后将整个form对象发送到服务端。

注意:

后台代码和上面的代码一样,不变。注意接收的文件名要同步。

jquery文件上传:

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <input type="file" id="img" />
    <input type="button" onclick="UploadFile();" />
    <script>
        function UploadFile(){
            var fileObj = $("#img")[0].files[0];
            var form = new FormData();
            form.append("k1", "v1");
            form.append("fff", fileObj);
 
            $.ajax({
                type:'POST',
                url: '/index',
                data: form,
                processData: false,  // tell jQuery not to process the data
                contentType: false,  // tell jQuery not to set contentType
                success: function(arg){
                    console.log(arg);
                }
            })
        }
    </script>
</body>
</html>

HTML

说明:

1、和原生的一样,都是显得获取当前上传文件的对象。files[0];然后实例化form对象,将要传递的内容append到实例化的对象form中。

2、后台代码同前,注意字段名对应。

关键点:

processData:false和contentType:false。这2个是关键。

默认的jquery会将我们上传的数据做部分处理。上面两段代码,就是告诉jquery不要处理我们的文件,不然会将我们的文件处理得不完整。

iframe文件上传

原生的ajaxjquery上传的时候,我们都是通过实例化一个form对象来进行文件的上传。但是实例化这个form的对象并不是所有的浏览器都存在,比如低版本的IE就可能没有合格FormData对象,那上面的方法就存在兼容性,没有form对象就不能发送。因此的使用一个兼容性更好的来进行操作,iframe

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form id="my_form" name="form" action="/index" method="POST"  enctype="multipart/form-data" >
        <div id="main">
            <input name="fff" id="my_file"  type="file" />
            <input type="button" name="action" value="Upload" onclick="redirect()"/>
            <iframe id='my_iframe' name='my_iframe' src=""  class="hide"></iframe>
        </div>
    </form>
 
    <script>
        function redirect(){
            document.getElementById('my_iframe').onload = Testt;
            document.getElementById('my_form').target = 'my_iframe';
            document.getElementById('my_form').submit();
 
        }
         
        function Testt(ths){
            var t = $("#my_iframe").contents().find("body").text();
            console.log(t);
        }
    </script>
</body>
</html>

html

关键点:

1、document.getElementById('my_form').target = 'my_iframe':这段代码就是获取iframe标签。

     target就是目标,只要给form设置了target的话,form提交的时候,就会提交到这个target指定的目标上。所以上面的代码表示只要form提交,就会提交到iframe上去。

2、当iframe操作完后会执行Testt方法,Testt方法就是获取后台返回的信息,并打印。

Jsonp实现ajax跨域请求

同源策略

浏览器有一个很重要的概念——同源策略(Same-Origin Policy)。所谓同源是指,域名,协议,端口相同。不同源的客户端脚本(javascript、ActionScript)在没明确授权的情况下,不能读写对方的资源。比较特别的是:由于同源策略是浏览器的限制,所以请求的响应和发送是可以进行的,只不过浏览器不支持罢了.

  • 限制:XmlHttpRequest,  AJax
  • 不限制:img iframe script块等等具有src属性的标签

JSONP(JSON with Padding)

是JSON的一种”使用模式”,可用于解决主流浏览器的跨域数据访问的问题。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的script 元素是一个例外。利用 <script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 资料,而这种使用模式就是所谓的 JSONP。用 JSONP 抓到的资料并不是 JSON,而是任意的JavaScript,用 JavaScript 直译器执行而不是用 JSON 解析器解析

基本思路

利用script标签,src导入目标域名的接口,在文档数的head标签中添加一行script标签,得到内容后将scprit标签删除,返回的解析后的参数即为得到的数据.

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

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

    <input type="button" onclick="Ajax();" value="普通AJax"/>
    <input type="button" onclick="Ajax2();" value="跨域普通AJax"/>
    <input type="button" onclick="Ajax3();" value="跨域牛逼AJax"/>
    <input type="button" onclick="Ajax4();" value="江西TV"/>

    <script src="/static/jquery-2.1.4.min.js"></script>
    <script>
            // 原生ajax,测试无效
        function Ajax(){
            $.ajax({
                url: '/get_data/',
                type: 'POST',
                data: {'k1': 'v1'},
                success: function (arg) {
                    alert(arg);
                }
            })
        }
            // 使用ajax跨域请求,测试无效
        function Ajax2(){
            $.ajax({
                url: 'http://wupeiqi.com:8001/api/',//需修改
                type: 'GET',
                data: {'k1': 'v1'},
                success: function (arg) {
                    alert(arg);
                }
            })
        }
        
        // 利用script标签,得到数据
        function Ajax3(){
            // script
            // alert(api)
            var tag = document.createElement('script');
            tag.src = 'http://wupeiqi.com:8001/api/';//需修改
            document.head.appendChild(tag);
            document.head.removeChild(tag);
        }
        function fafafa(arg){
            console.log(arg);
        }
        
        // 例子,获取江西卫视的节目单
        function Ajax4(){
            // script
            // alert(api)
            var tag = document.createElement('script');
            tag.src = 'http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403';
            document.head.appendChild(tag);
            document.head.removeChild(tag);
        }
        function list(arg){
            console.log(arg);
        }
    </script>
</body>
</html>

利用script标签实现跨域代码

JSONP实现ajax跨域

以上的代码其实也是jsonp的基本思路

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

$.ajax({
    url:..
    type: 'GET',
    dataType: 'jsonp',
    //传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
    jsonp: 'callback',
    //自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据
    jsonpCallback: 'list'
})
    
function list(arg){
    
}

基本的jsonp写法

jsonp: callback      #发送给请求处理程序的,被请求端通过request.GET.get("callback"),获得jsonp回调函数的参数

jsonpCallback: 'list' #定义回调函数的名称,然后后面通过list(...)来处理获取数据

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

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

    <p>
        <input type="button" onclick="Jsonp1();"  value='提交'/>
    </p>

    <p>
        <input type="button" onclick="Jsonp2();" value='提交'/>
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function Jsonp1(){
            var tag = document.createElement('script');
            tag.src = "http://c2.com:8000/test/";
            document.head.appendChild(tag);
            document.head.removeChild(tag);

        }

        function Jsonp2(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'GET',
                dataType: 'JSONP',
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                }
            })
        }


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

生产示例-###基于JSONP实现跨域Ajax - Demo

JSONP不能发送POST请求

究其根源,通过script标签的src属性进行跨域请求,<script src='http://www.jxntv.cn/data/jmd-jxtv2.html?callback=qwerqweqwe&_=1454376870403'>最后全部都会转换成GET请求,哪怕是你把type改为POST.

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session 其它例子参考

其他ajax跨站请求方式

需要顺带提一句的是,跨站请求还有另一种方式:cors,跨站资源共享,但此中方式对浏览器版本有要求,IE8以下的均不支持.

CORS与JSONP相比,无疑更为先进、方便和可靠。

 1、 JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。

 2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。

 3、 JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS(这部分会在后文浏览器支持部分介绍)。

CORS(跨域资源共享)

背后的原理是:使用自定的HTTP头部与服务器进行沟通,从而由服务器决定响应是否成功

它允许浏览器向跨源(协议 + 域名 + 端口)服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

**只要同时满足以下两大条件,就属于简单请求。

  1. 请求方法是以下三种方法之一:
    HEAD
    GET
    POST
    2)HTTP的头信息不超出以下几种字段:
    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
    Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain**

凡是不同时满足上面两个条件,就属于非简单请求。

浏览器对这两种请求的处理,是不一样的。

简单请求和非简单请求的区别?

简单请求:一次请求

非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。

预检:
请求方式:OPTIONS
- “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
     => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
        Access-Control-Request-Method
     => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
        Access-Control-Request-Headers

基于cors实现AJAX请求:

a、支持跨域,简单请求

服务器设置响应头:Access-Control-Allow-Origin = '域名' 或 '*'

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

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

    <p>
        <input type="submit" onclick="XmlSendRequest();" />
    </p>

    <p>
        <input type="submit" onclick="JqSendRequest();" />
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function XmlSendRequest(){
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4) {
                    var result = xhr.responseText;
                    console.log(result);
                }
            };
            xhr.open('GET', "http://c2.com:8000/test/", true);
            xhr.send();
        }

        function JqSendRequest(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'GET',
                dataType: 'text',
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                }
            })
        }


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

HTML

HTML

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
        self.write('{"status": true, "data": "seven"}')

tornado

b、支持跨域,复杂请求

由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。

  • “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method

  • “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers

  • “预检”缓存时间,服务器设置响应头:Access-Control-Max-Age

    tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    
        <p>
            <input type="submit" onclick="XmlSendRequest();" />
        </p>
    
        <p>
            <input type="submit" onclick="JqSendRequest();" />
        </p>
    
        <script type="text/javascript" src="jquery-1.12.4.js"></script>
        <script>
            function XmlSendRequest(){
                var xhr = new XMLHttpRequest();
                xhr.onreadystatechange = function(){
                    if(xhr.readyState == 4) {
                        var result = xhr.responseText;
                        console.log(result);
                    }
                };
                xhr.open('PUT', "http://c2.com:8000/test/", true);
                xhr.setRequestHeader('k1', 'v1');
                xhr.send();
            }
    
            function JqSendRequest(){
                $.ajax({
                    url: "http://c2.com:8000/test/",
                    type: 'PUT',
                    dataType: 'text',
                    headers: {'k1': 'v1'},
                    success: function(data, statusText, xmlHttpRequest){
                        console.log(data);
                    }
                })
            }
    
    
        </script>
    </body>
    </html>
    

    HTML

    tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

    class MainHandler(tornado.web.RequestHandler):
        
        def put(self):
            self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
            self.write('{"status": true, "data": "seven"}')
    
        def options(self, *args, **kwargs):
            self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
            self.set_header('Access-Control-Allow-Headers', "k1,k2")
            self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
            self.set_header('Access-Control-Max-Age', 10)
    

    Tornado

c 、跨域获取响应头

默认获取到的所有响应头只有基本信息,如果想要获取自定义的响应头,则需要再服务器端设置Access-Control-Expose-Headers。

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

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

    <p>
        <input type="submit" onclick="XmlSendRequest();" />
    </p>

    <p>
        <input type="submit" onclick="JqSendRequest();" />
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function XmlSendRequest(){
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4) {
                    var result = xhr.responseText;
                    console.log(result);
                    // 获取响应头
                    console.log(xhr.getAllResponseHeaders());
                }
            };
            xhr.open('PUT', "http://c2.com:8000/test/", true);
            xhr.setRequestHeader('k1', 'v1');
            xhr.send();
        }

        function JqSendRequest(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'PUT',
                dataType: 'text',
                headers: {'k1': 'v1'},
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                    // 获取响应头
                    console.log(xmlHttpRequest.getAllResponseHeaders());
                }
            })
        }


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

HTML

tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

class MainHandler(tornado.web.RequestHandler):
    
    def put(self):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")

        self.set_header('xxoo', "seven")
        self.set_header('bili', "daobidao")

        self.set_header('Access-Control-Expose-Headers', "xxoo,bili")


        self.write('{"status": true, "data": "seven"}')

    def options(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
        self.set_header('Access-Control-Allow-Headers', "k1,k2")
        self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
        self.set_header('Access-Control-Max-Age', 10)

Tornado

d、跨域传输cookie

在跨域请求中,默认情况下,HTTP Authentication信息,Cookie头以及用户的SSL证书无论在预检请求中或是在实际请求都是不会被发送。

如果想要发送:

  • 浏览器端:XMLHttpRequest的withCredentials为true

  • 服务器端:Access-Control-Allow-Credentials为true

  • 注意:服务器端响应的 Access-Control-Allow-Origin 不能是通配符 *

    tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    
        <p>
            <input type="submit" onclick="XmlSendRequest();" />
        </p>
    
        <p>
            <input type="submit" onclick="JqSendRequest();" />
        </p>
    
        <script type="text/javascript" src="jquery-1.12.4.js"></script>
        <script>
            function XmlSendRequest(){
                var xhr = new XMLHttpRequest();
                xhr.onreadystatechange = function(){
                    if(xhr.readyState == 4) {
                        var result = xhr.responseText;
                        console.log(result);
                    }
                };
    
                xhr.withCredentials = true;
    
                xhr.open('PUT', "http://c2.com:8000/test/", true);
                xhr.setRequestHeader('k1', 'v1');
                xhr.send();
            }
    
            function JqSendRequest(){
                $.ajax({
                    url: "http://c2.com:8000/test/",
                    type: 'PUT',
                    dataType: 'text',
                    headers: {'k1': 'v1'},
                    xhrFields:{withCredentials: true},
                    success: function(data, statusText, xmlHttpRequest){
                        console.log(data);
                    }
                })
            }
    
    
        </script>
    </body>
    </html>
    

    HTML

    tornado框架&三层架构&MVC&MTV&模板语言&cookie&session tornado框架&三层架构&MVC&MTV&模板语言&cookie&session

    class MainHandler(tornado.web.RequestHandler):
        
        def put(self):
            self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
            self.set_header('Access-Control-Allow-Credentials', "true")
            
            self.set_header('xxoo', "seven")
            self.set_header('bili', "daobidao")
            self.set_header('Access-Control-Expose-Headers', "xxoo,bili")
    
            self.set_cookie('kkkkk', 'vvvvv');
    
            self.write('{"status": true, "data": "seven"}')
    
        def options(self, *args, **kwargs):
            self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
            self.set_header('Access-Control-Allow-Headers', "k1,k2")
            self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
            self.set_header('Access-Control-Max-Age', 10)
    

    Tornado

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
3年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k