文章目录
一. Tornado简介
Tornado是使用Python编写的Web服务器兼Web应用框架,与主流Web服务器框架不同的是,Tornado是异步非阻塞式服务器,得益于非阻塞式和对epoll模型的运用,Tornado是实时Web服务的一个理想框架,它非常适合开发长轮询、WebSocket和需要与每个用户建立持久连接的应用。
官网:https://www.tornadoweb.org/en/stable/index.html#
安装:https://pypi.org/project/tornado/
二. windows / python3.8 / Tornado / NotImplementedError 问题
windows下,python3.8运行tornado报错,如下
Traceback (most recent call last):
File "E:\PythonProject\tornado2\tornado\server_main.py", line 79, in <module>
app.listen(PORT)
File "C:\Users\linxinfa\AppData\Local\Programs\Python\Python38\lib\site-packages\tornado-6.0.3-py3.8-win-amd64.egg\tornado\web.py", line 2112, in listen
server.listen(port, address)
File "C:\Users\linxinfa\AppData\Local\Programs\Python\Python38\lib\site-packages\tornado-6.0.3-py3.8-win-amd64.egg\tornado\tcpserver.py", line 152, in listen
self.add_sockets(sockets)
File "C:\Users\linxinfa\AppData\Local\Programs\Python\Python38\lib\site-packages\tornado-6.0.3-py3.8-win-amd64.egg\tornado\tcpserver.py", line 165, in add_sockets
self._handlers[sock.fileno()] = add_accept_handler(
File "C:\Users\linxinfa\AppData\Local\Programs\Python\Python38\lib\site-packages\tornado-6.0.3-py3.8-win-amd64.egg\tornado\netutil.py", line 279, in add_accept_handler
io_loop.add_handler(sock, accept_handler, IOLoop.READ)
File "C:\Users\linxinfa\AppData\Local\Programs\Python\Python38\lib\site-packages\tornado-6.0.3-py3.8-win-amd64.egg\tornado\platform\asyncio.py", line 99, in add_handler
self.asyncio_loop.add_reader(fd, self._handle_events, fd, IOLoop.READ)
File "C:\Users\linxinfa\AppData\Local\Programs\Python\Python38\lib\asyncio\events.py", line 501, in add_reader
raise NotImplementedError
NotImplementedError
是由于 python3.8 asyncio 在 windows 上默认使用 ProactorEventLoop 造成的,而不是之前的 SelectorEventLoop。jupyter 依赖 tornado,而 tornado 在 window 上需要使用 SelectorEventLoop,所以产生这个报错.
请看官方文档: https://www.tornadoweb.org/en/stable/index.html#installation
On Windows, Tornado requires the WindowsSelectorEventLoop. This is the default in Python 3.7 and older, but Python 3.8 defaults to an event loop that is not compatible with Tornado. Applications that use Tornado on Windows with Python 3.8 must call
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
at the beginning of their main file/function.
解决方法,在 tornado
开始执行前添加以下代码,在windows
下单独处理:
# windows 系统下 tornado 使用 使用 SelectorEventLoop
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
三. 跨域访问问题
使用javascript的XMLHttpRequest请求post
报错:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
这是跨域访问问题。
1. 什么是跨域
跨域是指从一个域名的网页去请求另一个域名的资源。比如从www.baidu.com
页面去请求 www.google.com
的资源。跨域的严格一点的定义是:只要 协议,域名,端口有任何一个的不同,就被当作是跨域
2. 为什么浏览器要限制跨域访问
原因就是安全问题:如果一个网页可以随意地访问另外一个网站的资源,那么就有可能在客户完全不知情的情况下出现安全问题。比如下面的操作就有安全问题:
用户访问www.mybank.com
,登陆并进行网银操作,这时cookie
啥的都生成并存放在浏览器;
用户突然想起件事,并迷迷糊糊地访问了一个邪恶的网站 www.xiee.com
;
这时该网站就可以在它的页面中,拿到银行的cookie
,比如用户名,登陆token等,然后发起对www.mybank.com
的操作。
如果这时浏览器不予限制,并且银行也没有做响应的安全处理的话,那么用户的信息有可能就这么泄露了。
3. 为什么要跨域
既然有安全问题,那为什么又要跨域呢? 有时公司内部有多个不同的子域,比如一个是location.company.com ,而应用是放在app.company.com , 这时想从 app.company.com去访问 location.company.com 的资源就属于跨域。
4 解决办法
定义BaseHandler,设置默认headers
import tornado.ioloop
import tornado.web
from tornado.web import RequestHandler
class BaseHandler(tornado.web.RequestHandler):
def set_default_headers(self):
self.set_header("Access-Control-Allow-Origin", "*") # 这个地方可以写域名
self.set_header("Access-Control-Allow-Headers", "x-requested-with")
self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
def post(self):
self.write('some post')
def get(self):
self.write('some get')
def options(self):
# no body
self.set_status(204)
self.finish()
然后所有的Handler类都继承BaseHandler即可,如:
class MyHandler(BaseHandler):
def get(self):
self.write('hello world')
def post(self):
self.write('hello world')
四. GET如何获取参数
比如这样的请求:http:localhost:8989?name=linxinfa&age=18
,参数可以通过self.get_argument
来获取
import tornado.ioloop
import tornado.web
# BaseHandler要自己定义,可参考上文
class MyHandler(BaseHandler):
def get(self):
name = self.get_argument('name', default = 'Tony')
age = self.get_argument('age', default = '0')
五. POST如何获取参数
通过self.request.body
来获取,假设传过来的是json
,那么self.request.body
就是json
字符串的二进制流,所以取出参数就是像下面这样:
import json
import tornado.ioloop
import tornado.web
# BaseHandler要自己定义,可参考上文
class MyHandler(BaseHandler):
def post(self):
jsonstr = str(self.request.body, encoding = "utf8")
jd = json.loads(jsonstr)
print(jd['name'], jd['age'])
六. javascript执行GET和POST请求
可以使用XMLHttpRequest
,例:
<button onclick='OnGetTestBtn()'>get_test</button>
<button onclick='OnPostTestBtn()'>post_test</button>
<script>
var ADDRESS = 'http://127.0.0.1:8458/'
function OnGetTestBtn()
{
var request_url = ADDRESS + "?name=linxinfa&age=18";
var request = new XMLHttpRequest();
request.open('GET', request_url, true);
request.send();
request.onload = function(e) {
if (request.status === 200) {
// 假设服务端返回的数据是json串,所以这里使用JSON来解析
jd = JSON.parse(request.response);
}
else{
alert('失败,请重试!');
window.history.back(-1);
}
}
}
function OnPostTestBtn()
{
var request_url = ADDRESS;
var request = new XMLHttpRequest();
request.open('POST', request_url, true);
send_data = {'name': 'linxinfa', 'age': 18 }
request.send(JSON.stringify(send_data));
request.onload = function(e) {
if (request.status === 200) {
// 假设服务端返回的数据是json串,所以这里使用JSON来解析
jd = JSON.parse(request.responseText);
}
else{
alert('失败,请重试!');
window.history.back(-1);
}
}
request.onerror = function(e){
alert('请求失败:' + request.statusText)
}
}
</script>
七. javascript动态创建列表
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
table {
width: 500px;
margin: 100px auto;
border-collapse: collapse;
text-align: center;
}
td,
th {
border: 1px solid #333;
}
thead tr {
height: 40px;
background-color: #ccc;
}
</style>
</head>
<body>
<table cellspacing="0">
<thead>
<tr>
<th>姓名</th>
<th>科目</th>
<th>成绩</th>
<th>操作</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<script>
// 1.将每一行的数据作为一个对象,然后将这些对象封装在一个数组中(没有后台服务器的情况下,暂时采用这种方式添加)
var datas = [{
name: '李寻欢',
subject: 'javascript',
scores: 100
}, {
name: '周杰伦',
subject: 'javascript',
scores: 99
}, {
name: '陈奕迅',
subject: 'javascript',
scores: 98
}, {
name: '昼夜',
subject: 'javascript',
scores: 97
}, {
name: '柯南',
subject: 'javascript',
scores: 100
}]
// 2、添加行tr,利用for循环(i的数量与数组的长度有关)
// 获取元素
var tbody = document.querySelector('tbody');
// 清理列表
var childs = tbody.childNodes;
for(var i = childs .length - 1; i >= 0; i--) {
tbody.removeChild(childs[i]);
}
// 创建列表
for (var i = 0; i < datas.length; i++) {
//创建行tr
var tr = document.createElement('tr');
//将新创建的行tr添加给tbody
tbody.appendChild(tr);
// 3、内层for循环,创建每一行中的所有单元格td,单元格td的数量与对象中的属性多少有关,故用for...in...
for (var k in datas[i]) {
// 创建td元素
var td = document.createElement('td');
// 将每个对象中的属性值传给td
td.innerHTML = datas[i][k];
//给tr添加td子元素
tr.appendChild(td);
}
//4、在每一行的最后,添加一个td,内容为a标签‘删除’
// 创建td新元素
var td = document.createElement('td');
//把a标签的‘删除’传给td
td.innerHTML = "<a href='javascript:;'>删除</ a>";
// 给tr添加上td
tr.appendChild(td);
}
//以上,整个表格就创建完成了
// 5、下面给表格添加点击‘删除’,就删除整行的事件
// 获取元素
var as = document.querySelectorAll('a');
// 添加事件
for (var i = 0; i < as.length; i++) {
as[i].onclick = function() {
tbody.removeChild(this.parentNode.parentNode);
}
}
</script>
</body>
</html>
八. python执行get和post
1. get
import requests
r = requests.get(url)
print(r.text)
2. post
import json
import requests
data = {'name': 'linxinfa','age': '18' }
jsonStr = json.dumps(data)
jsonBytes =jsonStr.encode('utf-8')
r = requests.post(url, jsonBytes)
print(r.text)
九. 判断字符串是否是JSON格式
1. python
def is_json(txt):
try:
json.loads(txt)
except ValueError:
return False
return True
2. javascript
function isJSON(str) {
if (typeof str == 'string') {
try {
JSON.parse(str);
return true;
} catch(e) {
return false;
}
}
console.log('It is not a string!')
}