web框架

2018/07/13 day100


一、Web框架
  - bottle
  - flask
  - tornado

  基本的http请求周期:用户请求-》服务器的路由系统-》业务逻辑处理(操作数据库,原生或者orm)(模板渲染)。django还有中间件。

它们的特点:
# 三大组件
  - 路由系统
  - 控制器(含模版渲染)
  - 数据库操作
# 微型框架
  - 依赖第三方写的socket,都要遵循WSGI
  - 本身功能少

# 安装

pip3 install bottle
pip3 install flask
pip3 install tornado

1. bottle

简单示例:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from bottle import template, Bottle
root = Bottle()

@root.route('/hello/')
def index():
return "Hello World"
# return template('<b>Hello {{name}}</b>!', name="Alex")	 
root.run(host='localhost', port=8080)	

a. 路由系统
  - 静态:@root.route('/hello/')
  - 动态:支持正则表达式、数字

@root.route('/wiki/<pagename>')	#pagename变量
@root.route('/object/<id:int>')	#int检测是否整数,然后传给id
@root.route('/show/<name:re:[a-z]+>')	#name参数,re后面是正则
@root.route('/static/<path:path>')	#静态资源配置路径,传给path参数,root是具体的路径

  - 方法:@root.route('/hello/', method='POST') #允许什么方式访问
  - 二级:路由分发

from framwork_bottle import app01
root.mount('app01', app01.app01)	#就指向了app01.py文件的路由
							
app01 = Bottle()
@app01.route('/hello/', method='GET')


b. 视图
  - 获取用户内容 request

request.headers	#请求头信息
request.query	#get请求信息
request.forms	#post请求信息
request.files	#上传文件信息
request.params	#get和post请求信息
request.GET	#get请求信息
request.POST	#post和上传信息
request.cookies	#cookie信息
request.environ	#环境相关相关

 

  - 数据库操作
  - 文件操作
  - ...
  - 返回给用户内容return ""
  - 模版引擎渲染

单值:			{{name}}
单行Python代码	% s1 = "hello"
Python代码块	<%	%>
Python、Html混合	% if True: % end
函数include:% include('header.tpl', title='Page Title')	#title参数
函数rebase:% rebase('base.tpl', title='Page Title')	#相当于dajngo的{% block css %}{% endblock %}
defined(name):		#检查当前变量是否已经被定义,已定义True,未定义False
get(name, default=None):	#获取某个变量的值,不存在时可设置默认值
setdefault(name, default):	#如果变量不存在时,为变量设置默认值
自定义函数:wupeiqi=custom	{{ wupeiqi() }}		#custom是定义的函数名,传给wupeiqi
xss安全:!相当于mark_safe

 

c. WSGI进行配置
  socket基于wsui接口来实现,请求先到socket,再转交给web框架
  root.run(host='localhost', port=8080, server='wsgiref')
  默认server="wsgiref",即:使用Python内置模块wsgiref,如果想要使用其他时,则需要首先安装相关类库,然后才能使用

2.flask

简单示例:

from flask import Flask
app = Flask(__name__)	
@app.route("/")
def hello():
return "Hello World!"	
if __name__ == "__main__":
app.run()


a.路由系统

@app.route('/user/<username>')	#正则
@app.route('/post/<int:post_id>')	#int传给post_id
@app.route('/post/<float:post_id>')
@app.route('/post/<path:path>')	#.*除换行符外所有
@app.route('/login', methods=['GET', 'POST'])	#方法


常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理:

DEFAULT_CONVERTERS = {
  'default': UnicodeConverter,
  'string': UnicodeConverter,
  'any': AnyConverter,	    #('/<any(about, help, imprint, class, "foo,bar"):page_name>')
  'path': PathConverter,
  'int': IntegerConverter,
  'float': FloatConverter,
  'uuid': UUIDConverter,
}


b.模板
Flask使用的是Jinja2模板,所以其语法和Django无差别

return render_template()
自定义模板:{{k4()|safe}} k4=jinxi def jinxi():

c.请求数据

request.method
request.args
request.form
request.values
request.files
request.cookies
request.headers
request.path
request.full_path
request.script_root
request.url
request.base_url
request.url_root
request.host_url
request.host

d.数据库连接:
  #python的ORM框架:SQLAchemy
  #pymysql
e.响应:
  字符串 return "index"
  模板引擎 return render_template("index.html")
  重定向 return redirect(url_for('login')) #login相当于url的name,flask中与函数同名
  设置相应信息 response = make_response() response.set_cookie response.headers['X-Something'] = 'A value'
        return response
f.Session:
  加密放在cookie中,需要先设置app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'

  设置:session['username'] = 'xxx'

  删除:session.pop('username', None)

g.基于wsgi自定制中间件

class Foo:
  def __init__(self,w):
	self.w = w
  def __call__(self, environ, start_response):
	obj = self.w(environ, start_response)       #前后即可定制操作
	return obj

if __name__ == "__main__":
# app.wsgi_app = my_wsgi_app
app.wsgi_app = Foo(app.wsgi_app)        #加括号执行Foo的call方法
app.run()

 

这样返回的wsgi_app还是内置的,但obj的前后可以自定制一些操作。django的中间件也是这样定制的

h.message
    message是一个基于Session实现的用于保存数据的集合,其特点是:使用一次就删除

    flash(v) #设置值
    {% with messages = get_flashed_messages() %} #模板中取值,一次即删

i. flask程序目录

views
	-__init__.py	#import config,在这里创建app=Flask(__name__,template_folder = config.template_folder),
								#	这样所有的py文件用的是同一个单例app
	-account.py		#from . import app,.表示本级目录,	
	-home.py
templates
	-index.html
	config.py			#写上template_folder = 'templates'
	manage.py			#import views即可,views.app.run() 

3. Tornado

- 异步非阻塞的框架 Node.js



模版语言:
Django: ORM,模板引擎
bottle: 模板引擎
SqlAchmey: ORM
JinJa2: 模板引擎,和django一样



2018/07/14 day101 Tornado


异步非阻塞的框架,遇到IO等待的时候可以处理其他,select

简单示例:

		import tornado.ioloop
		import tornado.web

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

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

		if __name__ == "__main__":
			#创建socket对象
			#将对象添加到select或epoll中
			application.listen(8888)
			#将select或epoll开始死循环,while true
			tornado.ioloop.IOLoop.instance().start()

  

1.路由系统

Tornado中每个url对应的是一个类。
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
2.模板
控制语句: {% %} {% end %}
表达语句: {{ }}
UIMethod:

		UIMethod:
			def tab(self):
				return 'UIMethod'	
			{{ tab() }}
			
		UIModule:
			class custom(UIModule):
				def render(self, *args, **kwargs):
					return escape.xhtml_escape('<h1>wupeiqi</h1>')	#不显示html格式
			{% module custom(123) %}

 

3.静态文件

		settings = {
			'template_path': 'template',
			'static_path': 'static',
			'static_url_prefix': '/static/',
		}
		application = tornado.web.Application([(r"/index", MainHandler),], **settings)


4.cookie

		self.set_cookie("mycookie", "myvalue")	
		self.get_cookie("mycookie")

  

5.session


二、Web组件的定制

1. Session
请求到来

       请求到来
            - 用户浏览器设置cookie   {'session_id':随机字符串}
            - 
               session = {
                    随机字符串: {login:true}
                    随机字符串: {login:true}
               }

 

a.定义session.py文件,编写一个保存键值对的类和容器

              import uuid
			class Session(object):
				container = {}
				def __init__(self,handler):
					# 获取用户cookie,有则不操作,没有就生成随机字符串,写给用户和保存在session
					nid = handler.get_cookie('session_id')
					if nid:
					   if nid in self.container:	#有nid在session中,每次访问时都更新超时时间
						   pass
					   else:
						   nid = str(uuid.uuid4())	#有nid不在session中,访问时创建sessionid,set_cookie
						   self.container[nid] = {}
					else:
						nid = str(uuid.uuid4())		#没有有nid,访问时创建sessionid,set_cookie
						self.container[nid]={}
					handler.set_cookie('session_id', nid, max_age=1000)		
					self.nid = nid          # 当前用户的随机字符串
					self.handler = handler  # 用户所有的请求信息
				def __setitem__(self, key, value):          # 设置session类的值时自动执行
					self.container[self.nid][key] = value
				def __getitem__(self,key):
					return self.container[self.nid].get(key) #如果没有返回none
				def __delitem__(self, key):
					del  self.container[self.nid][key]

 

b.创建session类的对象,传键和值进行设置和获取

			class BaseHandler(object):		#所有的视图函数执行前先执行这个函数:创建session对象给全局self
				def initialize(self):
					# 获取用户cookie,有则不操作,没有就生成随机字符串,写给用户和保存在session
					from session import Session
					self.session = Session(self)   #传给session方法,指IndexHandler等

					super(BaseHandler, self).initialize()   #不影响其他类的同名方法执行,先执行这个,再执行他们的

  

c.视图函数中设置和获取session

			        if v == 'root':
						self.session['is_login']=True	#设置
		
					if self.session['is_login']:		#获取	
						self.write("Hello, world")		

 


d.不同储存类型的session选择
import session
cls = getattr(session,config.session_key) #config.py文件中session_key = 'Session'
self.session = cls(self) #传给session方法,指IndexHandler等

e.哈希算法选择服务器存储
将session的nid转数字,哈希算法分配服务器机台

2. Form组件
a. 表单验证
- 写Form类
- 定义Form类中的字段
- 用户发送
- obj = Form(请求数据)
obj.is_valid()
obj.cleaned_data

示例:

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

			class StringField:	#定义字段
				def __init__(self,name):
					self.rex = '^\w+$'
					self.name = name
					self.value = ''
					self.error = ''
				def __str__(self):  #输出类的对象时显示这个
					return "<input type='text' name=%s value='%s'/>" %(self.name,self.value)
			class EmailField:
				def __init__(self,name):
					self.rex = '^\w+@.*$'
					self.name = name
					self.value = ''
					self.error = ''
				def __str__(self):
					return "<input type='text' name=%s value='%s'/>" %(self.name,self.value)

			class LoginForm:	#Form组件
				def __init__(self):
					self.user = StringField(name='user')
					self.email = EmailField(name='email')
				def is_valid(self,handler):
					value_dict = {}
					flag = True
					for k,v in self.__dict__.items():    #对象里的所有字段
						inp = handler.get_argument(k)
						rex = re.match(v.rex,inp)
						v.value = inp
						if rex:
							value_dict[k]=inp
						else:
							v.error = "%s错误了" % k
							flag = False
					return flag,value_dict

			class LoginHandler(tornado.web.RequestHandler):
				def get(self, *args, **kwargs):
					obj = LoginForm()
					self.render('login.html',**{'obj':obj})		#调用字段的str方法,raw原生显示
				def post(self, *args, **kwargs):
					obj = LoginForm()
					valid,value_dict = obj.is_valid(self)
					if valid:
						print(value_dict)
					else:
						self.render('login.html', **{'obj': obj})

			settings = {'static_path':'static','static_url_prefix':'/css/','template_path':'templates'}
			application = tornado.web.Application([
				(r"/login", LoginHandler),
			],**settings)

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

  

三、tornado异步非阻塞原理:

 

2018/07/15 day102

1. 运算
v = 1 and 2 or 3 an 4
2. 基本数据类型
如字符串有几个方法
3. 列表生成式

4. 作用域
python与js类似
5. 函数的参数和返回值
可变类型当参数,一改整体改
6. 迭代器和生成器

7. 面向对象
- 继承
- metaclass


8. 异步IO模块

9. 异步非阻塞Web框架


=====================================================

支持异步非阻塞Web框架 - Tornado,Nodejs

异步IO模块:
我们作为客户端向服务端发起“并发”请求,select监听socket是否已经有变化


异步非阻塞框架:
服务器收到非计算型即IO请求挂起的同时,处理其他请求,当IO请求有结果,其他服务器返回时,再返回给IO请求,断掉连接。

future = Future()
1. 挂起当前请求,线程可以处理其他请求 *****
2. future设置值,当前挂起的请求返回

3种情况:
- 超时时间到,往future设置值,请求结束,等待同时可以处理其他请求
- 请求来了,再往远处发请求,远处有返回时结束当前请求
- 等待另一个请求设置future值终止

自定义Web框架:
- 同步
- 异步


一、Tornado异步非阻塞

1.设置超时时间

		from tornado import gen
		from tornado.concurrent import Future
		class MainHandler(tornado.web.RequestHandler):
			@gen.coroutine
			def get(self):
				future = Future()
				tornado.ioloop.IOLoop.current().add_timeout(time.time() + 5, self.doing)
				yield future

			def doing(self, *args, **kwargs):
				self.write('async')
				self.finish()

  

2.往远处发请求

		class MainHandler(tornado.web.RequestHandler):
			@gen.coroutine
			def get(self):
				from tornado import httpclient
				http = httpclient.AsyncHTTPClient()
				yield http.fetch("http://www.google.com", self.done)

			def done(self, response):
				print('完事')
				self.finish('666')

  


3.发请求设置future

		future = None
		class MainHandler(tornado.web.RequestHandler):
			@gen.coroutine
			def get(self):
				global future
				future = Future()
				future.add_done_callback(self.done)
				yield future

			def done(self, response):
				print('完事')
				self.finish('666')

		class IndexHandler(tornado.web.RequestHandler):
			def get(self):
				global future
				future.set_result(None)			#设置
				self.write("Hello, world")

  


二、自定义web框架

1.同步

		import socket
		import select

		class HttpRequest(object):
			"""
			用户封装用户请求信息
			"""
			def __init__(self, content):
				"""
				:param content: 请求头、请求体
				"""
				self.content = content

				self.header_bytes = bytes()
				self.header_dict = {}
				self.body_bytes = bytes()

				self.method = ""
				self.url = ""
				self.protocol = ""

				self.initialize()
				self.initialize_headers()

			def initialize(self):
				temp = self.content.split(b'\r\n\r\n', 1)   #分割请求体、头
				if len(temp) == 1:
					self.header_bytes += temp
				else:
					h, b = temp
					self.header_bytes += h
					self.body_bytes += b
					header_flag = True

			@property
			def header_str(self):
				return str(self.header_bytes, encoding='utf-8')

			def initialize_headers(self):
				headers = self.header_str.split('\r\n')
				first_line = headers[0].split(' ')
				if len(first_line) == 3:
					self.method, self.url, self.protocol = headers[0].split(' ')
					for line in headers:
						kv = line.split(':')
						if len(kv) == 2:
							k, v = kv
							self.header_dict[k] = v

		def index(request):
			return 'xxxx'

		def main(request):
			return 'main'

		routers = [
			('/index/',index),
			('/main/',main),
		]

		def run():
			sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
			sock.bind(('127.0.0.1', 9999,))
			sock.setblocking(False)
			sock.listen(128)

			inputs = []
			inputs.append(sock)
			while True:
				rlist,wlist,elist = select.select(inputs,[],[],0.05)
				for r in rlist:
					if r == sock:
						'''新请求到来'''
						conn,addr = sock.accept()
						conn.setblocking(False)     #设置客户端socket非阻塞
						inputs.append(conn)
					else:
						"""客户端发来数据"""
						data = b""      #包含请求头、请求体
						while True:
							try:
								chunks = r.recv(1024)       #接收字节类型
								data += chunks
							except Exception as e:
								chunks = None
							if not chunks:
								break
						request = HttpRequest(data)
						# print(request.url)
						# print(request.method)
						# print(request.header_dict)
						# print(request.body_bytes)
						# 1. 请求头获取url
						# 2. 路由匹配,获取指定函数
						import re
						flag = False
						func = None
						for route in routers:
							if re.match(route[0],request.url):
								flag = True
								func = route[1]
								break
						if flag:
							result = func(request)
							r.sendall(bytes(result,encoding='utf-8'))
						else:
							r.sendall(b'404')
						# 3. 执行函数,获取返回值
						# 4.r.sendall(b'asd')
						inputs.remove(r)
						r.close()

		if __name__ =='__main__':
			run()	

  

总结:
select是IO多路复用,监听状态变化,本质还是同步

2.异步:Future

重点:return Future时pass,放到队列中,for循环监听Future的self.result值是否变化,变化了返回并close
变化可以是timeout作if判断,设置值;
请求的函数中设置(global一下Future);
远处请求返回设置一下。

代码:

a.发送请求设置

			class Future(object):
				def __init__(self):
					self.result = None
			F = None			#全局变量
			def index(request):
				global F
				F = Future()
				return F

			def main(request):
				return 'main'

			def stop(request):
				global F
				F.result = b'sda'		#设置
				return 'stop'	

  


循环监听:

			for conn in async_request_dict.keys():
				future = async_request_dict[conn]
				if future.result:					#监听值是否变化
					conn.sendall(future.result)
					conn.close()
					del async_request_dict[conn]
					inputs.remove(conn)
				else:
					pass

  



b.超时

			class Future(object):
				def __init__(self,timeout):
					self.result = None
					self.timeout = timeout
					self.start_time = time.time()

			def index(request):
				f = Future(5)
				return f

  

循环:

			for conn in async_request_dict.keys():
				future = async_request_dict[conn]
				start_time = future.start_time
				timeout = future.timeout
				ctime = time.time()
				if (start_time + timeout)<= ctime:
					future.result = b'timeout'
				if future.result:
					conn.sendall(future.result)
					conn.close()
					del async_request_dict[conn]
					inputs.remove(conn)
				else:
					pass

  

 

posted @ 2018-07-28 15:47  心平万物顺  阅读(198)  评论(0编辑  收藏  举报