python web框架之Tornado

说Tornado之前分享几个前端不错的网站:

    -- Bootstraphttp://www.bootcss.com/-- Font Awesomehttp://fontawesome.io/-- bxsliderhttp://bxslider.com/-- jQuery EasyUIhttp://www.jeasyui.com/download/index.php-- jQuery UIhttp://jqueryui.com/-- parsleyjs        http://parsleyjs.org/-- jQuery Validate  http://jqueryvalidation.org/
View Code

web框架的本质

总所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
import socketdef request_handler(client):a = client.recv(1024)client.send(bytes("HTTP/1.1 200 OK\r\n\r\n",encoding='utf-8'))client.send(bytes("hello",encoding="utf-8"))def main():sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)sock.bind(("127.0.0.1",8000))sock.listen(5)while True:con,add = sock.accept()request_handler(con)con.close()if __name__ == '__main__':main()

上述通过socket来实现了其本质,而对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。

WSGI(Web Server Gateway Interface)是一种规范,它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦。

python标准库提供的独立WSGI服务器称为wsgiref。

2.x环境运行

from wsgiref.simple_server import make_serverdef RunServer(environ, start_response):start_response('200 OK', [('Content-Type', 'text/html')])return '<h1>Hello, web!</h1>'if __name__ == '__main__':httpd = make_server('', 8888, RunServer)httpd.serve_forever()

自定义Web框架  

#!/usr/bin/env python
# _*_ coding:utf-8 _*_from wsgiref.simple_server import make_serverdef index():return "hi"URLS = {"/index":index,
}def RunServer(environ, start_response): #environ 封装了请求头信息start_response('200 OK', [('Content-Type', 'text/html')])url = environ["PATH_INFO"]if url in URLS.keys():ret=URLS[url]()else:ret = "404"return retif __name__ == '__main__':httpd = make_server('', 8000, RunServer)httpd.serve_forever()

在上一步骤中,对于所有的index返回给用户浏览器一个简单的字符串,在现实的Web请求中一般会返回一个复杂的符合HTML规则的字符串,所以我们一般将要返回给用户的HTML写在指定文件中,然后再返回。如:  

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title>
</head>
<body><form><input type="text" /><input type="text" /><input type="submit" /></form>
</body>
</html>
返回表单
#!/usr/bin/env python
# _*_ coding:utf-8 _*_from wsgiref.simple_server import make_serverdef login():file_list = open("temp/s2.html",'r')data = file_list.read()return datadef index():file_list = open("temp/s1.html",'r')data = file_list.read()return dataURLS = {"/index":index,"/login":login,
}def RunServer(environ, start_response):start_response('200 OK', [('Content-Type', 'text/html')])url = environ["PATH_INFO"]if url in URLS.keys():ret=URLS[url]()else:ret = "404"return retif __name__ == '__main__':httpd = make_server('', 8000, RunServer)httpd.serve_forever()

对于上述代码,虽然可以返回给用户HTML的内容以现实复杂的页面,但是还是存在问题:如何给用户返回动态内容?  

  • 自定义一套特殊的语法,进行替换
  • 使用开源工具jinja2,遵循其指定语法
<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title>
</head>
<body><h1>{{name}}</h1><ul>{% for item in user_list %}<li>{{item}}</li>{% endfor %}</ul></body>
</html>
index

 

#!/usr/bin/env python
# _*_ coding:utf-8 _*_from wsgiref.simple_server import make_server
from jinja2 import Templatedef index():file_list = open("temp/s3.html",'r')file_text = file_list.read()temp = Template(file_text)data = temp.render(name="john Doe",user_list = ['tangseng','wukong'])return data.encode("utf-8")def login():file_list = open("temp/s2.html",'r')data = file_list.read()return dataURLS = {"/index":index,"/login":login,
}def RunServer(environ, start_response):start_response('200 OK', [('Content-Type', 'text/html')])url = environ["PATH_INFO"]if url in URLS.keys():ret=URLS[url]()else:ret = "404"return retif __name__ == '__main__':httpd = make_server('', 8000, RunServer)httpd.serve_forever()

遵循jinja2的语法规则,其内部会对指定的语法进行相应的替换,从而达到动态的返回内容

 Tornado 前戏

执行字符串表示的函数,并为该函数提供全局变量

本篇的内容从题目中就可以看出来,就是为之后剖析tornado模板做准备,也是由于该知识点使用的巧妙,所有就单独用一篇来介绍了。废话不多说,直接上代码:

#!usr/bin/env python
#coding:utf-8namespace = {'name':'wukong','data':[18,73,84]}code =  '''def hellocute():return  "name %s ,age %d" %(name,data[0],) '''func = compile(code, '<string>', "exec")exec func in namespaceresult = namespace['hellocute']()

此段代码的执行结果是:name wukong,age 18  

上述代码解析:

  • 第6行,code是一个字符串,该字符串的内容是一个函数体。
  • 第8行,将code字符串编译成函数 hello
  • 第10行,将函数 hello 添加到namespace字典中(key为hello),同时也将python的所有内置函数添加到namespace字段中(key为__builtins__),如此一来,namespace中的内容好比是一个个的全局变量,即
  • 第12行,执行Hello函数并将返回值复制给result
  • 第14行,输入result
name = wupeiqi
data = [18,73,84]def hellocute():return  "name %s ,age %d" %(name,data[0],)
View Code

这段代码用的很是巧妙有木有,亮瞎狗眼有木有,居然把字符串变成了函数并且还为该函数提供了全局变量。对于该功能,它就是python的web框架中模板语言部分至关重要的部分,因为在模板处理过程中,首先会读取html文件,然后分割html文件,再然后讲分割的文件组成一个字符串表示的函数,再再然后就是利用上述方法执行字符串表示的函数。

   快速上手Tornado

Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本。这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过为了能有效利用非阻塞式服务器环境,这个 Web 框架还包含了一些相关的有用工具 和优化。

Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,这意味着对于实时 Web 服务来说,Tornado 是一个理想的 Web 框架。我们开发这个 Web 服务器的主要目的就是为了处理 FriendFeed 的实时功能 ——在 FriendFeed 的应用里每一个活动用户都会保持着一个服务器连接。(关于如何扩容 服务器,以处理数以千计的客户端的连接的问题,请参阅 C10K problem。)

 一、安装如下:

pip install tornado
源码安装https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz

第一次运行Tornado代码:  

#!/usr/bin/env python
# -*- coding:utf-8 -*-import tornado.ioloop
import tornado.webclass 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()

第一步:执行脚本,监听 8888 端口

第二步:浏览器客户端访问 /index  -->  http://127.0.0.1:8888/index

第三步:服务器接受请求,并交由对应的类处理该请求

第四步:类接受到请求之后,根据请求方式(post / get / delete ...)的不同调用并执行相应的方法

第五步:方法返回值的字符串内容发送浏览器

#!/usr/bin/env python
# -*- coding:utf-8 -*-import tornado.ioloop
import tornado.web
from tornado import httpclient
from tornado.web import asynchronous
from tornado import genimport uimodules as md
import uimethods as mtclass MainHandler(tornado.web.RequestHandler):@asynchronous@gen.coroutinedef get(self):print 'start get 'http = httpclient.AsyncHTTPClient()http.fetch("http://127.0.0.1:8008/post/", self.callback)self.write('end')def callback(self, response):print response.bodysettings = {'template_path': 'template','static_path': 'static','static_url_prefix': '/static/','ui_methods': mt,'ui_modules': md,
}application = tornado.web.Application([(r"/index", MainHandler),
], **settings)if __name__ == "__main__":application.listen(8009)tornado.ioloop.IOLoop.instance().start()
异步非阻塞

 二、路由系统

路由系统其实就是 url 和 类 的对应关系,这里不同于其他框架,其他很多框架均是 url 对应 函数,Tornado中每个url对应的是一个类。  

#!/usr/bin/env python
# _*_ coding:utf-8 _*_import tornado.ioloop
import tornado.webclass LoginHandler(tornado.web.RequestHandler):def get(self, *args, **kwargs):self.render("s2.html")class IndexHandler(tornado.web.RequestHandler):def get(self, *args, **kwargs):self.render("index.html")
#路由系统
settings = {"template_path":"templates",}application = tornado.web.Application([(r"/index",IndexHandler),(r"/login",LoginHandler),
])if __name__ == '__main__':httpd = application.listen(8888)tornado.ioloop.IOLoop.instance().start()

Tornado中原生支持二级域名的路由,如:

 

 

三、模板

Tornao中的模板语言和django中类似,模板引擎将模板文件载入内存,然后将数据嵌入其中,最终获取到一个完整的字符串,再将字符串返回给请求者。

Tornado 的模板支持“控制语句”和“表达语句”,控制语句是使用 {% 和 %} 包起来的 例如 {% if len(items) > 2 %}。表达语句是使用 {{ 和 }} 包起来的,例如 {{ items[0] }}

控制语句和对应的 Python 语句的格式基本完全相同。我们支持 ifforwhile 和 try,这些语句逻辑结束的位置需要用 {% end %} 做标记。还通过 extends 和 block 语句实现了模板继承。这些在 template 模块 的代码文档中有着详细的描述。

1.基本使用

#!/usr/bin/env python
# -*- coding:utf-8 -*-import tornado.ioloop
import tornado.webclass MainHandler(tornado.web.RequestHandler):def get(self):self.render("index.html", list_info = [11,22,33])application = tornado.web.Application([(r"/index", MainHandler),
])if __name__ == "__main__":application.listen(8888)tornado.ioloop.IOLoop.instance().start()
app
<!DOCTYPE html>
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/><title>Title</title><link href="{{static_url("css/common.css")}}" rel="stylesheet" />
</head>
<body><div><ul>{% for item in list_info %}<li>{{item}}</li>{% end %}</ul></div><script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script></body>
</html>
index

在模板中默认提供了一些函数、字段、类以供模板使用:

escape: tornado.escape.xhtml_escape 的別名
xhtml_escape: tornado.escape.xhtml_escape 的別名
url_escape: tornado.escape.url_escape 的別名
json_encode: tornado.escape.json_encode 的別名
squeeze: tornado.escape.squeeze 的別名
linkify: tornado.escape.linkify 的別名
datetime: Python 的 datetime 模组
handler: 当前的 RequestHandler 对象
request: handler.request 的別名
current_user: handler.current_user 的別名
locale: handler.locale 的別名
_: handler.locale.translate 的別名
static_url: for handler.static_url 的別名
xsrf_form_html: handler.xsrf_form_html 的別名 
其他方法

2.母板

<!DOCTYPE html>
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/><title>老男孩</title><link href="{{static_url("css/common.css")}}" rel="stylesheet" />{% block CSS %}{% end %}
</head>
<body><div class="pg-header"></div>{% block RenderBody %}{% end %}<script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script>{% block JavaScript %}{% end %}
</body>
</html>
layout.html
{% extends 'layout.html'%}
{% block CSS %}<link href="{{static_url("css/index.css")}}" rel="stylesheet" />
{% end %}{% block RenderBody %}<h1>Index</h1><ul>{%  for item in li %}<li>{{item}}</li>{% end %}</ul>{% end %}{% block JavaScript %}{% end %}
index.html

3.导入

<!DOCTYPE html>
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/><title>老男孩</title><link href="{{static_url("css/common.css")}}" rel="stylesheet" />
</head>
<body><div class="pg-header">{% include 'header.html' %}</div><script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script></body>
</html>
index.html

4.自定义UIMethod以UIModule

Tornado默认提供的这些功能其实本质上就是 UIMethod 和 UIModule,我们也可以自定义从而实现类似于Django的simple_tag的功能:

1、定义

# uimethods.pydef tab(self):return 'UIMethod'
uimethods.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from tornado.web import UIModule
from tornado import escapeclass custom(UIModule):def render(self, *args, **kwargs):return escape.xhtml_escape('<h1>wukong</h1>')#return escape.xhtml_escape('<h1>wukong</h1>')
uimmodule

注册

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#!/usr/bin/env python
# -*- coding:utf-8 -*-import tornado.ioloop
import tornado.web
from tornado.escape import linkify
import uimodules as md
import uimethods as mtclass MainHandler(tornado.web.RequestHandler):def get(self):self.render('index.html')settings = {'template_path': 'template','static_path': 'static','static_url_prefix': '/static/','ui_methods': mt,'ui_modules': md,
}application = tornado.web.Application([(r"/index", MainHandler),
], **settings)if __name__ == "__main__":application.listen(8009)tornado.ioloop.IOLoop.instance().start()
main.py

使用

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title><link href="{{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body><h1>hello</h1>{% module custom(123) %}{{ tab() }}
</body>
index

四、静态文件

 

对于静态文件,可以配置静态文件的目录和前段使用时的前缀,并且Tornaodo还支持静态文件缓存。

#!/usr/bin/env python
# -*- coding:utf-8 -*-import tornado.ioloop
import tornado.webclass MainHandler(tornado.web.RequestHandler):def get(self):self.render('home/index.html')settings = {'template_path': 'template','static_path': 'static',                  //静态资源的的路径比如 css,js'static_url_prefix': '/static/',       // 静态资源的的 URL 前缀
}application = tornado.web.Application([(r"/index", MainHandler),
], **settings)if __name__ == "__main__":application.listen(80)tornado.ioloop.IOLoop.instance().start()   
View Code

备注:静态文件缓存的实现

 

五、cookie

Tornado中可以对cookie进行操作,并且还可以对cookie进行签名以放置伪造。

a、基本操作

class MainHandler(tornado.web.RequestHandler):def get(self):if not self.get_cookie("mycookie"):self.set_cookie("mycookie", "myvalue")self.write("Your cookie was not set yet!")else:self.write("Your cookie was set!")
View Code

b、加密cookie签名

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

class MainHandler(tornado.web.RequestHandler):def get(self):if not self.get_secure_cookie("mycookie"):self.set_secure_cookie("mycookie", "myvalue")self.write("Your cookie was not set yet!")else:self.write("Your cookie was set!")application = tornado.web.Application([(r"/", MainHandler),
], cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")//随意增加
Code
def _create_signature_v1(secret, *parts):hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)for part in parts:hash.update(utf8(part))return utf8(hash.hexdigest())def _create_signature_v2(secret, s):hash = hmac.new(utf8(secret), digestmod=hashlib.sha256)hash.update(utf8(s))return utf8(hash.hexdigest())
内部算法
def create_signed_value(secret, name, value, version=None, clock=None,key_version=None):if version is None:version = DEFAULT_SIGNED_VALUE_VERSIONif clock is None:clock = time.timetimestamp = utf8(str(int(clock())))value = base64.b64encode(utf8(value))if version == 1:signature = _create_signature_v1(secret, name, value, timestamp)value = b"|".join([value, timestamp, signature])return valueelif version == 2:# The v2 format consists of a version number and a series of# length-prefixed fields "%d:%s", the last of which is a# signature, all separated by pipes.  All numbers are in# decimal format with no leading zeros.  The signature is an# HMAC-SHA256 of the whole string up to that point, including# the final pipe.#
        # The fields are:# - format version (i.e. 2; no length prefix)# - key version (integer, default is 0)# - timestamp (integer seconds since epoch)# - name (not encoded; assumed to be ~alphanumeric)# - value (base64-encoded)# - signature (hex-encoded; no length prefix)def format_field(s):return utf8("%d:" % len(s)) + utf8(s)to_sign = b"|".join([b"2",format_field(str(key_version or 0)),format_field(timestamp),format_field(name),format_field(value),b''])if isinstance(secret, dict):assert key_version is not None, 'Key version must be set when sign key dict is used'assert version >= 2, 'Version must be at least 2 for key version support'secret = secret[key_version]signature = _create_signature_v2(secret, to_sign)return to_sign + signatureelse:raise ValueError("Unsupported version %d" % version)
算法加密
def _decode_signed_value_v1(secret, name, value, max_age_days, clock):parts = utf8(value).split(b"|")if len(parts) != 3:return Nonesignature = _create_signature_v1(secret, name, parts[0], parts[1])if not _time_independent_equals(parts[2], signature):gen_log.warning("Invalid cookie signature %r", value)return Nonetimestamp = int(parts[1])if timestamp < clock() - max_age_days * 86400:gen_log.warning("Expired cookie %r", value)return Noneif timestamp > clock() + 31 * 86400:# _cookie_signature does not hash a delimiter between the# parts of the cookie, so an attacker could transfer trailing# digits from the payload to the timestamp without altering the# signature.  For backwards compatibility, sanity-check timestamp# here instead of modifying _cookie_signature.gen_log.warning("Cookie timestamp in future; possible tampering %r",value)return Noneif parts[1].startswith(b"0"):gen_log.warning("Tampered cookie %r", value)return Nonetry:return base64.b64decode(parts[0])except Exception:return Nonedef _decode_fields_v2(value):def _consume_field(s):length, _, rest = s.partition(b':')n = int(length)field_value = rest[:n]# In python 3, indexing bytes returns small integers; we must# use a slice to get a byte string as in python 2.if rest[n:n + 1] != b'|':raise ValueError("malformed v2 signed value field")rest = rest[n + 1:]return field_value, restrest = value[2:]  # remove version numberkey_version, rest = _consume_field(rest)timestamp, rest = _consume_field(rest)name_field, rest = _consume_field(rest)value_field, passed_sig = _consume_field(rest)return int(key_version), timestamp, name_field, value_field, passed_sigdef _decode_signed_value_v2(secret, name, value, max_age_days, clock):try:key_version, timestamp, name_field, value_field, passed_sig = _decode_fields_v2(value)except ValueError:return Nonesigned_string = value[:-len(passed_sig)]if isinstance(secret, dict):try:secret = secret[key_version]except KeyError:return Noneexpected_sig = _create_signature_v2(secret, signed_string)if not _time_independent_equals(passed_sig, expected_sig):return Noneif name_field != utf8(name):return Nonetimestamp = int(timestamp)if timestamp < clock() - max_age_days * 86400:# The signature has expired.return Nonetry:return base64.b64decode(value_field)except Exception:return Nonedef get_signature_key_version(value):value = utf8(value)version = _get_version(value)if version < 2:return Nonetry:key_version, _, _, _, _ = _decode_fields_v2(value)except ValueError:return Nonereturn key_version
算法解密

签名Cookie的本质是:

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

注:许多API验证机制和安全cookie的实现机制相同。

#!/usr/bin/env python
# -*- coding:utf-8 -*-import tornado.ioloop
import tornado.webclass MainHandler(tornado.web.RequestHandler):def get(self):login_user = self.get_secure_cookie("login_user", None)if login_user:self.write(login_user)else:self.redirect('/login')class LoginHandler(tornado.web.RequestHandler):def get(self):self.current_user()self.render('login.html', **{'status': ''})def post(self, *args, **kwargs):username = self.get_argument('name')password = self.get_argument('pwd')if username == 'wukong' and password == '123':self.set_secure_cookie('login_user', '悟空')self.redirect('/')else:self.render('login.html', **{'status': '用户名或密码错误'})settings = {'template_path': 'template','static_path': 'static','static_url_prefix': '/static/','cookie_secret': 'aiuasdhflashjdfoiuashdfiuh'
}application = tornado.web.Application([(r"/index", MainHandler),(r"/login", LoginHandler),
], **settings)if __name__ == "__main__":application.listen(8888)tornado.ioloop.IOLoop.instance().start()
基于cookie进行用户验证
#!/usr/bin/env python
# -*- coding:utf-8 -*-import tornado.ioloop
import tornado.webclass BaseHandler(tornado.web.RequestHandler):def get_current_user(self):return self.get_secure_cookie("login_user")class MainHandler(BaseHandler):@tornado.web.authenticateddef get(self):login_user = self.current_userself.write(login_user)class LoginHandler(tornado.web.RequestHandler):def get(self):self.current_user()self.render('login.html', **{'status': ''})def post(self, *args, **kwargs):username = self.get_argument('name')password = self.get_argument('pwd')if username == 'wukong' and password == '123':self.set_secure_cookie('login_user', '悟空')self.redirect('/')else:self.render('login.html', **{'status': '用户名或密码错误'})settings = {'template_path': 'template','static_path': 'static','static_url_prefix': '/static/','cookie_secret': 'aiuasdhflashjdfoiuashdfiuh','login_url': '/login'
}application = tornado.web.Application([(r"/index", MainHandler),(r"/login", LoginHandler),
], **settings)if __name__ == "__main__":application.listen(8888)tornado.ioloop.IOLoop.instance().start()
Demo-Toando内部提供基于cookie进行用户验证

3、JavaScript操作Cookie

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

/*
设置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();
}  

对于参数:

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

注:jQuery中也有指定的插件 jQuery Cookie 专门用于操作cookie,猛击这里

六、CSRF

Tornado中的夸张请求伪造和Django中的相似,跨站伪造请求(Cross-site request forgery)

限制与POST请求

View Code
普通表单使用
ajax使用

注:Ajax使用时,本质上就是去获取本地的cookie,携带cookie再来发送请求

七、上传文件

1、Form表单上传

<!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
#!/usr/bin/env python
# -*- coding:utf-8 -*-import tornado.ioloop
import tornado.webclass 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(8000)tornado.ioloop.IOLoop.instance().start()
python

2、AJAX上传

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title>
</head>
<body><input type="file" id="img" /><input type="button" οnclick="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 - XMLHttpRequest
<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title>
</head>
<body><input type="file" id="img" /><input type="button" οnclick="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 datacontentType: false,  // tell jQuery not to set contentTypesuccess: function(arg){console.log(arg);}})}</script>
</body>
</html>
HTML - jQuery
<!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" οnclick="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 - iframe
#!/usr/bin/env python
# -*- coding:utf-8 -*-import tornado.ioloop
import tornado.webclass 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(8000)tornado.ioloop.IOLoop.instance().start()
Python
<script type="text/javascript">$(document).ready(function () {$("#formsubmit").click(function () {var iframe = $('<iframe name="postiframe" id="postiframe" style="display: none"></iframe>');$("body").append(iframe);var form = $('#theuploadform');form.attr("action", "/upload.aspx");form.attr("method", "post");form.attr("encoding", "multipart/form-data");form.attr("enctype", "multipart/form-data");form.attr("target", "postiframe");form.attr("file", $('#userfile').val());form.submit();$("#postiframe").load(function () {iframeContents = this.contentWindow.document.body.innerHTML;$("#textarea").html(iframeContents);});return false;});});</script><form id="theuploadform"><input id="userfile" name="userfile" size="50" type="file" /><input id="formsubmit" type="submit" value="Send File" />
</form><div id="textarea">
</div>
扩展:基于iframe实现Ajax上传示例

八、验证码

验证码原理在于后台自动创建一张带有随机内容的图片,然后将内容通过img标签输出到页面。

安装图像处理模块:

pip3 install pillow

示例截图:

  

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

源码中有两个文件要注意Monaco.ttf和check_code.py 要引入到工程内;demo:

自定义Web组件

一、Session

1、面向对象基础

面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ 、__delitem__、__setitem__方法

#!/usr/bin/env python
# -*- coding:utf-8 -*-class Foo(object):def __getitem__(self, key):print  '__getitem__',keydef __setitem__(self, key, value):print '__setitem__',key,valuedef __delitem__(self, key):print '__delitem__',keyobj = Foo()
result = obj['k1']
#obj['k2'] = 'wupeiqi'
#del obj['k1']
View Code

2、Tornado扩展

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

class BaseHandler(tornado.web.RequestHandler):def initialize(self):self.xxoo = "wupeiqi"class MainHandler(BaseHandler):def get(self):print(self.xxoo)self.write('index')class IndexHandler(BaseHandler):def get(self):print(self.xxoo)self.write('index')
View Code

3、session

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

import  hashlib
import timecontainer = {}
class Session:def __init__(self, handler):self.handler = handlerself.random_str = Nonedef __genarate_random_str(self):'''用于生成加密字符串:return:'''obj = hashlib.md5()obj.update(bytes(str(time.time()), encoding='utf-8'))random_str = obj.hexdigest()return random_strdef __setitem__(self, key, value):# 在container中加入随机字符串# 定义专属于自己的数据# 在客户端中写入随机字符串# 判断,请求的用户是否已有随机字符串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(): #判断字符串是否在container中passelse:random_str = self.__genarate_random_str() #生成字符串container[random_str] = {} #生成专属的字典self.random_str = random_str # self.random_str = asdfasdfasdfasdf
container[self.random_str][key] = valueself.handler.set_cookie("__kakaka__", self.random_str)def __getitem__(self,key):# 获取客户端的随机字符串# 从container中获取专属于我的数据#  专属信息【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 Nonevalue = user_info_dict.get(key, None)return value
View Code

4、分布式Session

#!/usr/bin/env python
#coding:utf-8import sys
import math
from bisect import bisectif sys.version_info >= (2, 5):import hashlibmd5_constructor = hashlib.md5
else:import md5md5_constructor = md5.newclass HashRing(object):"""一致性哈希"""def __init__(self,nodes):'''初始化nodes : 初始化的节点,其中包含节点已经节点对应的权重默认每一个节点有32个虚拟节点对于权重,通过多创建虚拟节点来实现如:nodes = [{'host':'127.0.0.1:8000','weight':1},{'host':'127.0.0.1:8001','weight':2},{'host':'127.0.0.1:8002','weight':1},]'''self.ring = dict()self._sorted_keys = []self.total_weight = 0self.__generate_circle(nodes)def __generate_circle(self,nodes):for node_info in nodes:self.total_weight += node_info.get('weight',1)for node_info in nodes:weight = node_info.get('weight',1)node = node_info.get('host',None)virtual_node_count = math.floor((32*len(nodes)*weight) / self.total_weight)for i in xrange(0,int(virtual_node_count)):key = self.gen_key_thirty_two( '%s-%s' % (node, i) )if self._sorted_keys.__contains__(key):raise Exception('该节点已经存在.')self.ring[key] = nodeself._sorted_keys.append(key)def add_node(self,node):''' 新建节点node : 要添加的节点,格式为:{'host':'127.0.0.1:8002','weight':1},其中第一个元素表示节点,第二个元素表示该节点的权重。'''node = node.get('host',None)if not node:raise Exception('节点的地址不能为空.')weight = node.get('weight',1)self.total_weight += weightnodes_count = len(self._sorted_keys) + 1virtual_node_count = math.floor((32 * nodes_count * weight) / self.total_weight)for i in xrange(0,int(virtual_node_count)):key = self.gen_key_thirty_two( '%s-%s' % (node, i) )if self._sorted_keys.__contains__(key):raise Exception('该节点已经存在.')self.ring[key] = nodeself._sorted_keys.append(key)def remove_node(self,node):''' 移除节点node : 要移除的节点 '127.0.0.1:8000''''for key,value in self.ring.items():if value == node:del self.ring[key]self._sorted_keys.remove(key)def get_node(self,string_key):'''获取 string_key 所在的节点'''pos = self.get_node_pos(string_key)if pos is None:return Nonereturn self.ring[ self._sorted_keys[pos]].split(':')def get_node_pos(self,string_key):'''获取 string_key 所在的节点的索引'''if not self.ring:return Nonekey = self.gen_key_thirty_two(string_key)nodes = self._sorted_keyspos = bisect(nodes, key)return posdef gen_key_thirty_two(self, key):m = md5_constructor()m.update(key)return long(m.hexdigest(), 16)def gen_key_sixteen(self,key):b_key = self.__hash_digest(key)return self.__hash_val(b_key, lambda x: x)def __hash_val(self, b_key, entry_fn):return (( b_key[entry_fn(3)] << 24)|(b_key[entry_fn(2)] << 16)|(b_key[entry_fn(1)] << 8)| b_key[entry_fn(0)] )def __hash_digest(self, key):m = md5_constructor()m.update(key)return map(ord, m.digest())"""
nodes = [{'host':'127.0.0.1:8000','weight':1},{'host':'127.0.0.1:8001','weight':2},{'host':'127.0.0.1:8002','weight':1},
]ring = HashRing(nodes)
result = ring.get_node('98708798709870987098709879087')
print result"""
一致性哈西
from hashlib import sha1
import os, timecreate_session_id = lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest()class Session(object):session_id = "__sessionId__"def __init__(self, request):session_value = request.get_cookie(Session.session_id)if not session_value:self._id = create_session_id()else:self._id = session_valuerequest.set_cookie(Session.session_id, self._id)def __getitem__(self, key):# 根据 self._id ,在一致性哈西中找到其对应的服务器IP# 找到相对应的redis服务器,如: r = redis.StrictRedis(host='localhost', port=6379, db=0)# 使用python redis api 链接# 获取数据,即:# return self._redis.hget(self._id, name)def __setitem__(self, key, value):# 根据 self._id ,在一致性哈西中找到其对应的服务器IP# 使用python redis api 链接# 设置session# self._redis.hset(self._id, name, value)def __delitem__(self, key):# 根据 self._id 找到相对应的redis服务器# 使用python redis api 链接# 删除,即:return self._redis.hdel(self._id, name)
session

二、表单验证

在Web程序中往往包含大量的表单验证的工作,如:判断输入是否为空,是否符合规则。

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title><link href="{{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body><h1>hello</h1><form action="/index" method="post"><p>hostname: <input type="text" name="host" /> </p><p>ip: <input type="text" name="ip" /> </p><p>port: <input type="text" name="port" /> </p><p>phone: <input type="text" name="phone" /> </p><input type="submit" /></form>
</body>
</html>
HTML
#!/usr/bin/env python
# -*- coding:utf-8 -*-import tornado.ioloop
import tornado.web
from hashlib import sha1
import os, time
import reclass MainForm(object):def __init__(self):self.host = "(.*)"self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"self.port = '(\d+)'self.phone = '^1[3|4|5|8][0-9]\d{8}$'def check_valid(self, request):form_dict = self.__dict__for key, regular in form_dict.items():post_value = request.get_argument(key)# 让提交的数据 和 定义的正则表达式进行匹配ret = re.match(regular, post_value)print key,ret,post_valueclass MainHandler(tornado.web.RequestHandler):def get(self):self.render('index.html')def post(self, *args, **kwargs):obj = MainForm()result = obj.check_valid(self)self.write('ok')settings = {'template_path': 'template','static_path': 'static','static_url_prefix': '/static/','cookie_secret': 'aiuasdhflashjdfoiuashdfiuh','login_url': '/login'
}application = tornado.web.Application([(r"/index", MainHandler),
], **settings)if __name__ == "__main__":application.listen(8888)tornado.ioloop.IOLoop.instance().start()
Python

由于验证规则可以代码重用,所以可以如此定义:

#!/usr/bin/env python
# -*- coding:utf-8 -*-import tornado.ioloop
import tornado.web
import reclass Field(object):def __init__(self, error_msg_dict, required):self.id_valid = Falseself.value = Noneself.error = Noneself.name = Noneself.error_msg = error_msg_dictself.required = requireddef match(self, name, value):self.name = nameif not self.required:self.id_valid = Trueself.value = valueelse:if not value:if self.error_msg.get('required', None):self.error = self.error_msg['required']else:self.error = "%s is required" % nameelse:ret = re.match(self.REGULAR, value)if ret:self.id_valid = Trueself.value = ret.group()else:if self.error_msg.get('valid', None):self.error = self.error_msg['valid']else:self.error = "%s is invalid" % nameclass IPField(Field):REGULAR = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"def __init__(self, error_msg_dict=None, required=True):error_msg = {}  # {'required': 'IP不能为空', 'valid': 'IP格式错误'}if error_msg_dict:error_msg.update(error_msg_dict)super(IPField, self).__init__(error_msg_dict=error_msg, required=required)class IntegerField(Field):REGULAR = "^\d+$"def __init__(self, error_msg_dict=None, required=True):error_msg = {'required': '数字不能为空', 'valid': '数字格式错误'}if error_msg_dict:error_msg.update(error_msg_dict)super(IntegerField, self).__init__(error_msg_dict=error_msg, required=required)class CheckBoxField(Field):def __init__(self, error_msg_dict=None, required=True):error_msg = {}  # {'required': 'IP不能为空', 'valid': 'IP格式错误'}if error_msg_dict:error_msg.update(error_msg_dict)super(CheckBoxField, self).__init__(error_msg_dict=error_msg, required=required)def match(self, name, value):self.name = nameif not self.required:self.id_valid = Trueself.value = valueelse:if not value:if self.error_msg.get('required', None):self.error = self.error_msg['required']else:self.error = "%s is required" % nameelse:if isinstance(name, list):self.id_valid = Trueself.value = valueelse:if self.error_msg.get('valid', None):self.error = self.error_msg['valid']else:self.error = "%s is invalid" % nameclass FileField(Field):REGULAR = "^(\w+\.pdf)|(\w+\.mp3)|(\w+\.py)$"def __init__(self, error_msg_dict=None, required=True):error_msg = {}  # {'required': '数字不能为空', 'valid': '数字格式错误'}if error_msg_dict:error_msg.update(error_msg_dict)super(FileField, self).__init__(error_msg_dict=error_msg, required=required)def match(self, name, value):self.name = nameself.value = []if not self.required:self.id_valid = Trueself.value = valueelse:if not value:if self.error_msg.get('required', None):self.error = self.error_msg['required']else:self.error = "%s is required" % nameelse:m = re.compile(self.REGULAR)if isinstance(value, list):for file_name in value:r = m.match(file_name)if r:self.value.append(r.group())self.id_valid = Trueelse:self.id_valid = Falseif self.error_msg.get('valid', None):self.error = self.error_msg['valid']else:self.error = "%s is invalid" % namebreakelse:if self.error_msg.get('valid', None):self.error = self.error_msg['valid']else:self.error = "%s is invalid" % namedef save(self, request, upload_path=""):file_metas = request.files[self.name]for meta in file_metas:file_name = meta['filename']with open(file_name,'wb') as up:up.write(meta['body'])class Form(object):def __init__(self):self.value_dict = {}self.error_dict = {}self.valid_status = Truedef validate(self, request, depth=10, pre_key=""):self.initialize()self.__valid(self, request, depth, pre_key)def initialize(self):passdef __valid(self, form_obj, request, depth, pre_key):"""验证用户表单请求的数据:param form_obj: Form对象(Form派生类的对象):param request: Http请求上下文(用于从请求中获取用户提交的值):param depth: 对Form内容的深度的支持:param pre_key: Html中name属性值的前缀(多层Form时,内部递归时设置,无需理会):return: 是否验证通过,True:验证成功;False:验证失败"""depth -= 1if depth < 0:return Noneform_field_dict = form_obj.__dict__for key, field_obj in form_field_dict.items():print key,field_objif isinstance(field_obj, Form) or isinstance(field_obj, Field):if isinstance(field_obj, Form):# 获取以key开头的所有的值,以参数的形式传至self.__valid(field_obj, request, depth, key)continueif pre_key:key = "%s.%s" % (pre_key, key)if isinstance(field_obj, CheckBoxField):post_value = request.get_arguments(key, None)elif isinstance(field_obj, FileField):post_value = []file_list = request.request.files.get(key, None)for file_item in file_list:post_value.append(file_item['filename'])else:post_value = request.get_argument(key, None)print post_value# 让提交的数据 和 定义的正则表达式进行匹配
                field_obj.match(key, post_value)if field_obj.id_valid:self.value_dict[key] = field_obj.valueelse:self.error_dict[key] = field_obj.errorself.valid_status = Falseclass ListForm(object):def __init__(self, form_type):self.form_type = form_typeself.valid_status = Trueself.value_dict = {}self.error_dict = {}def validate(self, request):name_list = request.request.arguments.keys() + request.request.files.keys()index = 0flag = Falsewhile True:pre_key = "[%d]" % indexfor name in name_list:if name.startswith(pre_key):flag = Truebreakif flag:form_obj = self.form_type()form_obj.validate(request, depth=10, pre_key="[%d]" % index)if form_obj.valid_status:self.value_dict[index] = form_obj.value_dictelse:self.error_dict[index] = form_obj.error_dictself.valid_status = Falseelse:breakindex += 1flag = Falseclass MainForm(Form):def __init__(self):# self.ip = IPField(required=True)# self.port = IntegerField(required=True)# self.new_ip = IPField(required=True)# self.second = SecondForm()self.fff = FileField(required=True)super(MainForm, self).__init__()#
# class SecondForm(Form):
#
#     def __init__(self):
#         self.ip = IPField(required=True)
#         self.new_ip = IPField(required=True)
#
#         super(SecondForm, self).__init__()class MainHandler(tornado.web.RequestHandler):def get(self):self.render('index.html')def post(self, *args, **kwargs):# for i in  dir(self.request):#     print i# print self.request.arguments# print self.request.files# print self.request.query# name_list = self.request.arguments.keys() + self.request.files.keys()# print name_list# list_form = ListForm(MainForm)# list_form.validate(self)#
        # print list_form.valid_status# print list_form.value_dict# print list_form.error_dict# obj = MainForm()# obj.validate(self)#
        # print "验证结果:", obj.valid_status# print "符合验证结果:", obj.value_dict# print "错误信息:"# for key, item in obj.error_dict.items():#     print key,item# print self.get_arguments('favor'),type(self.get_arguments('favor'))# print self.get_argument('favor'),type(self.get_argument('favor'))# print type(self.get_argument('fff')),self.get_argument('fff')# print self.request.files# obj = MainForm()# obj.validate(self)# print obj.valid_status# print obj.value_dict# print obj.error_dict# print self.request,type(self.request)# obj.fff.save(self.request)# from tornado.httputil import HTTPServerRequest# name_list = self.request.arguments.keys() + self.request.files.keys()# print name_list# print self.request.files,type(self.request.files)# print len(self.request.files.get('fff'))# obj = MainForm()# obj.validate(self)# print obj.valid_status# print obj.value_dict# print obj.error_dict# obj.fff.save(self.request)self.write('ok')settings = {'template_path': 'template','static_path': 'static','static_url_prefix': '/static/','cookie_secret': 'aiuasdhflashjdfoiuashdfiuh','login_url': '/login'
}application = tornado.web.Application([(r"/index", MainHandler),
], **settings)if __name__ == "__main__":application.listen(8888)tornado.ioloop.IOLoop.instance().start()
View Code

转载于:https://www.cnblogs.com/yyyg/p/5690427.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/490249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

火狐导cookie文件没反应_效率指南|实操区分FireFox火狐的国内版和国际版

Firefox分为了国内版、国际版&#xff0c;书签、历史记录、设置等不支持互相同步。本来想图个方便&#xff0c;在国内的互联网环境下&#xff0c;放弃了无法登录的Chrome&#xff0c;上车Firefox就是看中电脑、手机端&#xff08;iPhone端不同步&#xff0c;国内Android没试过哈…

Struts2小结

Struts 2是在WebWork2基础发展而来的。 注意&#xff1a;struts 2和struts 1在代码风格上几乎不一样。 Struts 2 相比Struts 1的优点&#xff1a; 1、在软件设计上Struts 2 没有像Struts 1那样跟Servlet API 和 struts API 有着紧密的耦合。 Struts 2的应用可以不依赖于Servlet…

郭仁忠院士 | 新城市科学认知与智慧城市建设

来源&#xff1a;中国地理信息产业协会7月25日&#xff0c;郭仁忠院士在2019中国地理信息产业大会&#xff08;珠海&#xff09;高端论坛上作题为《新城市科学认知与智慧城市建设》的精彩报告。经郭院士授权&#xff0c;现将报告PPT发布分享&#xff0c;以飨读者。《崛起的超级…

是否顺应互联网大脑的发育趋势决定科技企业兴衰

来源&#xff1a;今日头条前言&#xff1a;本文摘录自2019年7月中信出版社出版的《崛起的超级智能&#xff1a;互联网大脑如何影响科技未来》为什么谷歌、脸书、亚马逊、阿里巴巴、腾讯等互联网企业发展得如此迅速&#xff0c;成为世界科技巨头&#xff1f;科大讯飞、商汤科技、…

sql 两个表列包含_Oracle数据库扩展语言PL/SQL之运算符

【本文详细介绍了Oracle数据库扩展语言PL/SQL的运算符&#xff0c;欢迎读者朋友们阅读、转发和收藏&#xff01;】1 基本概念运算符是一个符号&#xff0c;告诉编译器执行特定的数学或逻辑操作。 PL/SQL 语言有丰富的内置运算符&#xff0c;运算符提供的以下几种类型&#xff1…

麦肯锡:企业数字化转型不要被技术“绑架”

来源&#xff1a;澳财网随着技术带动流程的改善和数字化时代的带来&#xff0c;平台经济日益兴起&#xff0c;视频租赁、电影、实体零售、广告和媒体、数据存储、出租车行业、餐馆和外卖、酒店和其他行业开始出现颠覆。在这个过程中&#xff0c;想客户之所想&#xff0c;急客户…

世界最大全脑神经元三维数据库建成

来源&#xff1a;科技日报神经元作为大脑的基础组成单元&#xff0c;对研究大脑的结构和功能至关重要。以往&#xff0c;受神经元标注和成像技术的局限&#xff0c;得到的图像有较高的噪声和不均匀的信号。9月15日&#xff0c;在东南大学举办的2019神经元重建及应用国际研讨会中…

Thinkphp js、css压缩类minify

说明&#xff1a;Minify 是用PHP5开发的应用&#xff0c;通过遵循一些Yahoo的优化规则来提高网站的性能。它会合并多个CSS或者JavaScript文件&#xff0c;移除一些不必要的空格和注释&#xff0c;进行gzip压缩&#xff0c;并且会设置浏览器的缓存头。Minify 在设计上和Yahoo的 …

新iPhone的黑科技:UWB技术揭秘!

来源&#xff1a;鲜枣课堂9月11日&#xff0c;苹果公司秋季发布会在乔布斯剧院举行。以iPhone 11为代表的一系列新品揭开了神秘的面纱&#xff0c;与“果粉”见面。新iPhone的特点和参数&#xff0c;相信大家通过各大媒体的介绍已经有所了解。不过有一个细节&#xff0c;不知道…

LeetCode——1954. 收集足够苹果的最小花园周长

通过万岁&#xff01;&#xff01;&#xff01; 题目&#xff1a;这个题目比较复杂&#xff0c;就是给你一个坐标轴&#xff0c;然后让你以0,0为中心选择一个边长为整数的正方形&#xff0c;使得正方形中所有点坐标的绝对值之和要大于给定的neededApples。但是我们需要输出的是…

【人工智能】清华大学张钹院士:人工智能技术已进入第三代

来源&#xff1a;经济观察报近日&#xff0c;中科院院士、清华大学人工智能研究院院长张钹教授接受记者采访时认为&#xff0c;目前基于深度学习的人工智能在技术上已经触及天花板。从长远来看&#xff0c;必须得走人类智能这条路&#xff0c;最终要发展人机协同&#xff0c;人…

莫侵残日噪,正在异乡听

莫侵残日噪,正在异乡听 闷热的傍晚&#xff0c;出来转转&#xff0c;身体被热流裹着&#xff0c;随手抓拍一张&#xff0c;撤&#xff01; 转载于:https://www.cnblogs.com/xxldannyboy/p/5719460.html

threadlocal get为空_面试常见知识点:ThreadLocal

老套路&#xff0c;先列举下关于ThreadLocal常见的疑问&#xff0c;希望可以通过这篇学习笔记来解决这几个问题&#xff1a;ThreadLocal是用来解决什么问题的&#xff1f;如何使用ThreadLocal&#xff1f;ThreadLocal的实现原理是什么&#xff1f;可否举几个实际项目中使用Thre…

行业观察 | 新一轮AI周期里,华为拿什么破解核心难题?

来源&#xff1a;财经杂志第三方研究机构OpenAI近期发布的研究显示&#xff0c;仅2012年以来&#xff0c;人们对于算力的需求增长六年就超过30万倍&#xff0c;平均每年增长10倍&#xff0c;远超摩尔定律的发展速度。面对AI算力需求的爆发式增长&#xff0c;华为作为AI的后入局…

swot分析法案例_(附数据集)SWOT分析实战案例!

最近一直在为一件事苦恼&#xff0c;开在市中心的那家西点店的生意越做越好&#xff0c;分店也开了两家&#xff0c;但挣得这点钱还是不够买房子。这不&#xff0c;最近看上了这如火如荼的奶茶市场&#xff0c;想借现有资源开一家奶茶店&#xff0c;但不知道这个想法靠不靠谱&a…

人工智能、核聚变、碳捕捉……最有可能帮助拯救地球的10项技术

来源&#xff1a; 资本实验室亚马逊森林大火、加速消逝的北极冰面、有记录以来最热的7月、越来越多的极端天气事件……各种因素正在让地球、让我们的生存环境经受日趋严重的考验。大问题往往意味着大机会。在人类对环境制造麻烦的过程中&#xff0c;势必又不断催生出各种新技术…

点击按钮弹出iframe_WEB安全(四) :CSRF与点击劫持

跨站点请求伪造CSRF是跨站点请求伪造&#xff0c;它的请求有两个关键点&#xff0c;跨站点的请求与请求是伪造的&#xff0c;从字面上看&#xff0c;跨站点的请求来源应该是其他站点&#xff0c;比如&#xff0c;目标网站接收到来源网站的恶意操作&#xff0c;但是&#xff0c;…

两种超级智能,哪一个对人类未来影响更为深远?

来源&#xff1a;刘锋科学网博客前言&#xff1a;无论在学术界还是产业界&#xff0c;超级智能并没有被纳入正式的体系概念中&#xff0c;因此超级智能究竟是什么&#xff0c;也没有统一的的定义&#xff0c;事实上在过去的时间里&#xff0c;出现了两种不同的超级智能概念&…

Java9都快发布了,Java8的十大新特性你了解多少呢?

Java 9预计将于今年9月份发布&#xff0c;这是否会是一次里程碑式的版本&#xff0c;我们拭目以待。今天&#xff0c;我们先来复习一下2014年发布的Java 8的十大新特性。 Java 8可谓是自Java 5以来最具革命性的版本了&#xff0c;她在语言、编译器、类库、开发工具以及Java虚拟…

struts2 redirect 获取参数null_社会化登录支持授权域以便于获取更大权限

使用 Authing 可以轻松接入许多社会化登录&#xff0c;同时无需翻看社会化登录提供方的繁琐接入文档&#xff0c;让开发者更加专注业务从而提升开发效率。这看起来很方便&#xff0c;But&#xff0c;通过 Authing 登录的社会化登录用户&#xff0c;无法获取更高级的权限&#x…