cbv源码分析
执行流程
# 1 app.add_url_rule('/user', 'user', UserView.as_view('user')) -第三个参数,放视图函数的内存地址---》UserView.as_view('user') 是函数内存地址 # 2 UserView中找类方法as_view--》返回值是一个函数内存地址 -UserView中没有 -MethodView---》View--》as_view() @classmethod def as_view(cls, name,*class_args, **class_kwargs): def view(**kwargs): return self.dispatch_request(**kwargs) return view # 3 app.add_url_rule的第三个位置,放的是 view 内层函数 # 4 请求来了--》路由匹配成功--》执行 view(参数)---》本质在执行 --》self.dispatch_request # 5 MethodView 上的 dispatch_request --》有具体实现 -不要去View中找dispatch_request---》因为它直接抛了异常,没有实现 # 6 MethodView 上的 dispatch_request def dispatch_request(self, **kwargs: t.Any) -> ft.ResponseReturnValue: # request.method.lower() 请求方式变小写 # self是视图类的对象 # 去视图类的对象中反射跟请求方式同名的方法 假设是get方法 meth = getattr(self, request.method.lower(), None) if meth is None and request.method == "HEAD": meth = getattr(self, "get", None) assert meth is not None, f"Unimplemented method {request.method!r}" # get(传入参数) return meth(**kwargs) # -----------上述跟django一模一样------
endpoint 的使用
##2 endpoint 的使用 app.add_url_rule('/item', endpoint='xxx',view_func=ItemAPI.as_view('item')) 如果写了endpoint---》别名以它为准,如果不写以as_view的参数为准 ## 逻辑: 1 app.add_url_rule('/item',endpoint='xxx', view_func=ItemAPI.as_view('item')) 2 endpoint = _endpoint_from_view_func(view_func) 如果endpoint没传,就会走这句 view_func 是 ItemAPI.as_view('item') 它就是 view 3 _endpoint_from_view_func(view_func)---》返回了传入的函数的名字 return view_func.__name__ # 如果没有其他更改--->所有cbv的函数内存地址都是view 4 如果上面传入了ItemAPI.as_view('item'),它的函数名就是view---》endpoint就是view -view.__name__ = name -view的名字 是咱么 ItemAPI.as_view('名字') 传入的名字,不再是view了 ## 总结: endpoint如果不传,会以视图函数的函数名作为endpoint -fbv:如果不写endpoint,会以函数名作为endpoint,但是如果多个视图函数加了同一个装饰器,又没有指定endpoint,就会出错了 -cbv:调用as_view一定要传入一个字符串---》如果endpoint没写,endpoint就是传入的这个字符串,如果写了,这个字符串没用 如果传了,直接以endpoint传入的作为endpoint
CBV加装饰器
### cbv中加装饰器 1 使用步骤:在类中加入类属性: class ItemAPI(MethodView): decorators = [装饰器,装饰器2] # 先写的装饰器放在最内部---》最后执行 def get(self): # print(url_for('xxx')) print(url_for('item')) return 'get' 2 源码中 if cls.decorators: for decorator in cls.decorators: # 这就是装饰器的本质原理 view = decorator(view) ''' @装饰器 def view() '''
session执行流程分析
基本使用
# cookie 是客户端浏览器的键值对 # session是存在于服务端的键值对 # flask的session存在哪了? 加密后放到了cookie中 from flask import Flask, request, session from flask.views import MethodView app = Flask(__name__) app.secret_key = 'asdfasdf' @app.route('/login', methods=['GET']) def login(): name = request.args.get('name') # 假设登录成功 session['name'] = name return 'hello' @app.route('/', methods=['GET']) def index(): print(session.get('name')) res = session.get('name') if res: return '欢迎你,%s' % res else: return '您没有登录' if __name__ == '__main__': app.run()
执行流程
# flask中通过SecureCookieSessionInterface对象来控制整个session的使用流程 app.session_interface=SecureCookieSessionInterface() # 1 登录成功---》放到session中数据----》把session中数据取出来,加密(dump)---》放到cookie-----》返回给前端--》save_session # 2 请求来了---》请求中携带了cookie ---》取出cookie-->解密(load)得到数据--》把数据放到session中---》进入视图函数--》后续视图函数中直接使用session.get 就能取到当时放入的值--》open_session # 不同浏览器,放的不一样,取出来也不一样 ### SecureCookieSessionInterface 俩方法来支撑上面的流程 # open_session: 请求来的时候--》解析出cookie,放到session中 def open_session(self, app: Flask, request: Request) -> SecureCookieSession | None: # 拿到解密或加密的对象 s = self.get_signing_serializer(app) if s is None: return None # request.cookie 取出客户端携带的cookie #self.get_cookie_name(app) ---》 app.config["SESSION_COOKIE_NAME"]--》session # eyJuYW1lIjoieHh4In0.ZeVMAw.2iCEYHAW7rDaH6Z5ntmKonErTbw val = request.cookies.get(self.get_cookie_name(app)) if not val: # 如果是空,走它---》做成空session--》返回 return self.session_class() # cookie在过期时间内 max_age = int(app.permanent_session_lifetime.total_seconds()) try: # 解密--》把eyJuYW1lIjoieHh4In0.ZeVMAw.2iCEYHAW7rDaH6Z5ntmKonErTbw解成字典 data = s.loads(val, max_age=max_age) # 组装成有 数据的session return self.session_class(data) except BadSignature: return self.session_class() # save_session:请求走的时候---》放到cookie中 def save_session(self, app: Flask, session: SessionMixin, response: Response) -> None: # 拿到 session 字符串 拿到配置文件中对应的配置 name = self.get_cookie_name(app) domain = self.get_cookie_domain(app) path = self.get_cookie_path(app) secure = self.get_cookie_secure(app) samesite = self.get_cookie_samesite(app) httponly = self.get_cookie_httponly(app) if session.accessed: response.vary.add("Cookie") # 如果不为空---》往下走---》视图函数中放入了值 session[name]=lqz if not session: # 判断session是否被修改过 if session.modified: # 删除cookie response.delete_cookie( name, domain=domain, path=path, secure=secure, samesite=samesite, httponly=httponly, ) response.vary.add("Cookie") return if not self.should_set_cookie(app, session): return # 获取过期时间 expires = self.get_expiration_time(app, session) # 获得加密对象 ---》 dumps 对 session 加密---》asdfas.asdfas.asdfasd val = self.get_signing_serializer(app).dumps(dict(session)) # type: ignore # 放到cookie中 response.set_cookie( name, val, # type: ignore expires=expires, httponly=httponly, domain=domain, path=path, secure=secure, samesite=samesite, ) response.vary.add("Cookie") # django 在中间件中做的 session不是直接加密放在cookie 中得, 加密放在 django-session表中--》有个随机字符串对应
django中session的流程
# 1 app中要注册 session ---》生成django-session表 'django.contrib.sessions', # 2 中间件要注册--》真正支撑session流程的 'django.contrib.sessions.middleware.SessionMiddleware', # 3 支撑session流程的 请求走了---》把session--》写入到cookie中 -process_response(self, request, response):---》等价于---》save_session 请求来了---》从cookie中取出来--》查数据库--》放到session中 -def process_request(self, request)---》等价于---》open_session def process_request(self, request): # 取出 前端在cookie中携带的 随机字符串 session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME) # SessionStore 实例化得到对象---》session_key是随机字符串 # 内部 根据随机字符串去django-session表中--》查出 session_data--》解密--》包装成对象 # 赋值给request.session -->后续所有视图函数中,都能使用request.session 放值或取值 request.session = self.SessionStore(session_key) def process_response(self, request, response): try: accessed = request.session.accessed modified = request.session.modified empty = request.session.is_empty() except AttributeError: return response if settings.SESSION_COOKIE_NAME in request.COOKIES and empty: response.delete_cookie( settings.SESSION_COOKIE_NAME, path=settings.SESSION_COOKIE_PATH, domain=settings.SESSION_COOKIE_DOMAIN, samesite=settings.SESSION_COOKIE_SAMESITE, ) patch_vary_headers(response, ('Cookie',)) else: if accessed: patch_vary_headers(response, ('Cookie',)) if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty: if request.session.get_expire_at_browser_close(): max_age = None expires = None else: max_age = request.session.get_expiry_age() expires_time = time.time() + max_age expires = http_date(expires_time) # Save the session data and refresh the client cookie. # Skip session save for 500 responses, refs #3881. if response.status_code != 500: try: request.session.save() except UpdateError: raise SessionInterrupted( "The request's session was deleted before the " "request completed. The user may have logged " "out in a concurrent request, for example." ) # 往cookie中放入 key是:session value值是随机字符串: request.session.session_key response.set_cookie( settings.SESSION_COOKIE_NAME, request.session.session_key, max_age=max_age, expires=expires, domain=settings.SESSION_COOKIE_DOMAIN, path=settings.SESSION_COOKIE_PATH, secure=settings.SESSION_COOKIE_SECURE or None, httponly=settings.SESSION_COOKIE_HTTPONLY or None, samesite=settings.SESSION_COOKIE_SAMESITE, ) return response
闪现
#1 它是:flash 翻译过来的 #2 闪现的作用 在某次请求中,有些数据,可以放在闪现中----》下次请求,从闪现中取出来使用 取一次就没了,下次再取就是空的 谁(浏览器)放的 ,谁(浏览器)才能取到 实际上放了session中了---》cookie # 3 如何设置,如何取值 ###基本使用 # 设置 flash('用户名或密码错误,%s' % username) # 取值 res = get_flashed_messages()[0] ## 高级使用---》设置flash时,给它设置category 分类 # 设置 flash('用户名或密码错误,%s' % username, category='login') #取值(注意取出来的格式,列表套元组) res=get_flashed_messages(True,category_filter=['login']) # django中有没有这种东西? 消息框架 django.contrib.messages # 1 注册app 'django.contrib.messages', # 2 注册中间件 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', # 3 配置 templates TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR,'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ ... 'django.contrib.messages.context_processors.messages', ], }, }, ] # 3 以后再视图函数中 放入 from django.contrib import messages messages.debug messages.info messages.success messages.warning messages.error messages.warning(request,'登陆失败,用户名或密码无效') # 4 在 视图函数中取 get_messages(request) # 4 在模板中 {% if messages %} {% for message in messages %} <div class="alert alert-{{ message.tags }} fade in"> {{ message }} </div> {% endfor %} {% endif %}
请求扩展
# 是几个装饰器--》功能是完成类似于django 的中间件--》在请求来和请求走的某些位置执行它 1 before_request:任意一次请求来了,都会执行这个装饰器装饰的函数 process_request 2 after_request:任意一次请求走了,就会执行这个装饰器装饰的函数 process_response 3 before_first_request # 新版本去掉了 第一次请求时,跟浏览器无关 4 teardown_request :每一个请求之后绑定一个函数,即使遇到了异常 5 errorhandler:监听http响应码:只要响应码对应,就会执行对应的函数 路径不存在时404,服务器内部错误500 6 template_global :标签 7 template_filter:过滤器
#1 before_request @app.before_request def before_request(): # 1 判断如果没有登录,重定向到登录 if session.get("name") or 'login' in request.path: print('请求来了1') # 2 做频率限制 # 3 登录认证,认证通过,在request.user 当前登录用户 jwt认证 else: return redirect('/login') @app.before_request def before_request(): print('请求来了2') #2 after_request @app.after_request def after_request(response): # 1 在响应头中加入一些数据 --》跨域处理 response.headers['ee'] = 'xx' print('请求走了1') return response @app.after_request def after_request(response): print('请求走了2') return response #3 记录日志 @app.teardown_request def teardown_request(error): print(error) # 4 errorhandler @app.errorhandler(404) def error_404(error): print('----',error) return render_template('404.html') @app.errorhandler(500) def error_500(error): print('----',error) return render_template('500.html') ##5 标签 @app.template_global() def sb(a1, a2): return a1 + a2 # 6 过滤器 @app.template_filter() def db(a1, a2, a3): return a1 + a2 + a3
g对象
from flask import Flask, g # g 对象 global 全局的意思---》每次请求---》这个g一直存在--》可以设置值和取值 # g本质是为了避免request对象 放值导致的数据污染问题,以后都用g放入值和使用 app = Flask(__name__) app.secret_key = 'asdfasdf' @app.before_request def before(): # jwt认证---》认证通过--》request.user=当前登录用户 # g.user=当前登录用户 request.methods='lqz' g.name = 'lqz' # 放入了 @app.after_request def after(response): print('----', g.name) return response def showName(): print('showName', g.name) @app.route('/', methods=['GET']) def index(): # 取出g中得值 print(g.name) showName() return '欢迎你' if __name__ == '__main__': app.run()
标签:04days,flask,app,request,---,session,cookie,view From: https://www.cnblogs.com/wzh366/p/18052160