Flask请求和应用上下文源码分析
flask的request和session设置方式比较新颖,如果没有这种方式,那么就只能通过参数的传递。
flask是如何做的呢?
1:本地线程,保证即使是多个线程,自己的值也是互相隔离
1 import threading
2
3 local_values = threading.local()
4
5
6 def func(num):
7 local_values.name = num
8 import time
9 time.sleep(1)
10 print(local_values.name, threading.current_thread().name)
11
12
13 for i in range(20):
14 th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
15 th.start()
2:自定义threading.local
1 """
2 {
3 1368:{}
4 }
5
6
7
8 """
9 import threading
10 try:
11 from greenlet import getcurrent as get_ident # 协程
12 except ImportError:
13 try:
14 from thread import get_ident
15 except ImportError:
16 from _thread import get_ident # 线程
17
18
19 class Local(object):
20 def __init__(self):
21 self.storage = {}
22 self.get_ident = get_ident
23
24 def set(self,k,v):
25 ident = self.get_ident()
26 origin = self.storage.get(ident)
27 if not origin:
28 origin = {k:v}
29 else:
30 origin[k] = v
31 self.storage[ident] = origin
32
33 def get(self,k):
34 ident = self.get_ident()
35 origin = self.storage.get(ident)
36 if not origin:
37 return None
38 return origin.get(k,None)
39
40 local_values = Local()
41
42
43 def task(num):
44 local_values.set('name',num)
45 import time
46 time.sleep(1)
47 print(local_values.get('name'), threading.current_thread().name)
48
49
50 for i in range(20):
51 th = threading.Thread(target=task, args=(i,),name='线程%s' % i)
52 th.start()
升级版
1 import threading
2 try:
3 from greenlet import getcurrent as get_ident # 协程
4 except ImportError:
5 try:
6 from thread import get_ident
7 except ImportError:
8 from _thread import get_ident # 线程
9
10
11 class Local(object):
12
13 def __init__(self):
14 object.__setattr__(self, '__storage__', {})
15 object.__setattr__(self, '__ident_func__', get_ident)
16
17
18 def __getattr__(self, name):
19 try:
20 return self.__storage__[self.__ident_func__()][name]
21 except KeyError:
22 raise AttributeError(name)
23
24 def __setattr__(self, name, value):
25 ident = self.__ident_func__()
26 storage = self.__storage__
27 try:
28 storage[ident][name] = value
29 except KeyError:
30 storage[ident] = {name: value}
31
32 def __delattr__(self, name):
33 try:
34 del self.__storage__[self.__ident_func__()][name]
35 except KeyError:
36 raise AttributeError(name)
37
38
39 local_values = Local()
40
41
42 def task(num):
43 local_values.name = num
44 import time
45 time.sleep(1)
46 print(local_values.name, threading.current_thread().name)
47
48
49 for i in range(20):
50 th = threading.Thread(target=task, args=(i,),name='线程%s' % i)
51 th.start()
说明解释:
1 - threading.local对象,用于为每个线程开辟一块空间来保存它独有的值。
2
3 - 源码(request)
4 - 情况一:单进程单线程,基于全局变量做。
5 - 情况二:单进程多线程,threading.local对象。
6 - 情况二:单进程单线程(多个协程),threading.local对象做不到。
7
8 - 决定:
9 - 以后不支持协程:threading.local对象。
10 - 支持:自定义类似threading.local对象(支持协程)
11 - 自定义类似threading.local对象
12 PS:
13 a.
14 object.__setattr__(self, 'storage', {})
15 self.storage = {}
16 b.
17 对象.xx
18 def __setattr__(self, key, value):
19 print(key,value)
3:请求上下文原理
1 from functools import partial
2 from flask.globals import LocalStack, LocalProxy
3
4 ls = LocalStack()
5
6
7 class RequestContext(object):
8 def __init__(self, environ):
9 self.request = environ
10
11
12 def _lookup_req_object(name):
13 top = ls.top
14 if top is None:
15 raise RuntimeError(ls)
16 return getattr(top, name)
17
18
19 session = LocalProxy(partial(_lookup_req_object, 'request'))
20
21 ls.push(RequestContext('c1')) # 当请求进来时,放入
22 print(session) # 视图函数使用
23 print(session) # 视图函数使用
24 ls.pop() # 请求结束pop
25
26
27 ls.push(RequestContext('c2'))
28 print(session)
29
30 ls.push(RequestContext('c3'))
31 print(session)
4:请求上下文源码
1 from greenlet import getcurrent as get_ident
2
3
4 def release_local(local):
5 local.__release_local__()
6
7
8 class Local(object):
9 __slots__ = ('__storage__', '__ident_func__')
10
11 def __init__(self):
12 # self.__storage__ = {}
13 # self.__ident_func__ = get_ident
14 object.__setattr__(self, '__storage__', {})
15 object.__setattr__(self, '__ident_func__', get_ident)
16
17 def __release_local__(self):
18 self.__storage__.pop(self.__ident_func__(), None)
19
20 def __getattr__(self, name):
21 try:
22 return self.__storage__[self.__ident_func__()][name]
23 except KeyError:
24 raise AttributeError(name)
25
26 def __setattr__(self, name, value):
27 ident = self.__ident_func__()
28 storage = self.__storage__
29 try:
30 storage[ident][name] = value
31 except KeyError:
32 storage[ident] = {name: value}
33
34 def __delattr__(self, name):
35 try:
36 del self.__storage__[self.__ident_func__()][name]
37 except KeyError:
38 raise AttributeError(name)
39
40
41 class LocalStack(object):
42 def __init__(self):
43 self._local = Local()
44
45 def __release_local__(self):
46 self._local.__release_local__()
47
48 def push(self, obj):
49 """Pushes a new item to the stack"""
50 rv = getattr(self._local, 'stack', None)
51 if rv is None:
52 self._local.stack = rv = []
53 rv.append(obj)
54 return rv
55
56 def pop(self):
57 """Removes the topmost item from the stack, will return the
58 old value or `None` if the stack was already empty.
59 """
60 stack = getattr(self._local, 'stack', None)
61 if stack is None:
62 return None
63 elif len(stack) == 1:
64 release_local(self._local)
65 return stack[-1]
66 else:
67 return stack.pop()
68
69 @property
70 def top(self):
71 """The topmost item on the stack. If the stack is empty,
72 `None` is returned.
73 """
74 try:
75 return self._local.stack[-1]
76 except (AttributeError, IndexError):
77 return None
78
79
80 stc = LocalStack()
81
82 stc.push(123)
83 v = stc.pop()
84
85 print(v)
5:个人源码剖析
a.偏函数
1 import functools
2
3 def func(a1):
4 print(a1)
5
6
7 new_func = functools.partial(func,666)
8
9 new_func()
10
11 说明:将666这个参数当做func函数的第一个参数
b.第一阶段
1 1.当携带这用户的请求过来之后,首先会执行app.run,会执行 run_simple(host, port, self, **options),因为run方法是由app调用的,app又是Flask的对象,因此当执行了app.run()之后就会调用Flask类中的__call__方法
2 2.进入app.__call__拿到return self.wsgi_app(environ, start_response)
3 3.进入app.wsgi_app之后就会执行:ctx = self.request_context(environ)
4 app.request_context(environ)会返回 return RequestContext(self, environ);然后执行__init__方法得到request = app.request_class(environ);最后将请求的信息放入request中。经过上面的操作,得出ctx就是携带有请求信息的request_context对象,ctx里面具有ctx.app,ctx.request,ctx.session
5 4.然后执行ctx.push()
6 进入push()里面找到 _request_ctx_stack.push(self),这里面的self指的是ctx对象,_request_ctx_stack是全局变量,_request_ctx_stack = LocalStack(),_request_ctx_stack 是LocalStack这个类的对象;通过def push(self, obj)这个方法将ctx这个对象存入threadinglocal中,给ctx这个对象分配专属与他的一块内存空间。
c.第二阶段
1 如果需要打印print(request),源码是如何执行的呢?
2 进入request
3 1.request = LocalProxy(partial(_lookup_req_object, 'request'))
4 这个里面:偏函数=partial(_lookup_req_object, 'request')
5 首先执行LocalProxy这个类实例化执行__init__方法:
6 def __init__(self, local, name=None):
7 object.__setattr__(self, '_LocalProxy__local', local)
8 在这里:_LocalProxy__local其实就是强制获取私有字段等价于
9 self._local = local
10 2.再执行__str__方法:
11 __str__ = lambda x: str(x._get_current_object())
12 3.def _get_current_object(self):
13 if not hasattr(self.__local, '__release_local__'):
14 return self.__local()
15 try:
16 return getattr(self.__local, self.__name__)
17 返回return self.__local(),执行偏函数
18 def _lookup_req_object(name):
19 top = _request_ctx_stack.top
20 if top is None:
21 raise RuntimeError(_request_ctx_err_msg)
22 return getattr(top, name)
23 4.从top = _request_ctx_stack.top中去拿开始存入的ctx对象,然后再从ctx对象中去拿request: return self._local.stack[-1]
24 5.通过getattr(top, name)==getattr(ctx对象,request)
25
26 如果是打印print(request.method)
27 其实就是多了一步,再执行__str__方法之前执行def __getattr__(self, name)方法
28
29
30 代码示例展示:
31 from flask import Flask,request,session,g,current_app
32
33 app = Flask(__name__)
34
35 @app.route('/',methods=['GET',"POST"])
36 def index():
37 # request是 LocalProxy 的对象
38 print(request) # LocalProxy.__str__ --> str(LocalProxy._get_current_object) --> 调用偏函数 --> ctx.request
39 request.method # LocalProxy.__getattr__ -->
40 # str(LocalProxy._get_current_object) --> 调用偏函数 --> ctx.request
41 # getattr(self._get_current_object(), name) --> ctx.request.method
42
43 request.path # ctx.request.path
44
45 print(session) # LocalProxy.__str__ --> str(LocalProxy._get_current_object) --> 调用偏函数 --> ctx.session
46
47
48 print(g) # 执行g对象的__str__
49 return "index"
50
51
52 if __name__ == '__main__':
53 app.__call__
54 app.wsgi_app
55 app.wsgi_app
56 app.request_class
57 app.run()
d.第三阶段
1 # 寻找视图函数并执行,获取返回值
2 response = self.full_dispatch_request()
3
4 1.先执行before_first_request
5 self.try_trigger_before_first_request_functions()
6
7 2.触发request_started信号,request_started
8 request_started.send(self)
9 补充:信号:
10 from flask import Flask, signals, render_template
11
12 app = Flask(__name__)
13
14
15 # 往信号中注册函数
16 def func(*args, **kwargs):
17 print('触发型号', args, kwargs)
18
19
20 signals.request_started.connect(func)
21
22
23 # 触发信号: signals.request_started.send()
24
25 @app.before_first_request
26 def before_first1(*args, **kwargs):
27 pass
28
29
30 @app.before_first_request
31 def before_first2(*args, **kwargs):
32 pass
33
34
35 @app.before_request
36 def before_first3(*args, **kwargs):
37 pass
38
39
40 @app.route('/', methods=['GET', "POST"])
41 def index():
42 print('视图')
43 return render_template('index.html')
44
45
46 if __name__ == '__main__':
47 app.wsgi_app
48 app.run()
49
50 3.再执行def preprocess_request(self):中的before_request
51
52 4.# 执行视图函数dispatch_request-->def render_template(template_name_or_list, **context):-->before_render_template.send(app, template=template, context=context)-->template_rendered.send(app, template=template, context=context)
53 rv = self.dispatch_request()
54
55 5. 执行self.finalize_request(rv)--> response = self.process_response(response)-->self.session_interface.save_session(self, ctx.session, response)
56
57 6.执行after_request
58
59 7.执行信号request_finished.send(self, response=response)
e.第四阶段
1 ctx.auto_pop,将请求信息从threadinglocal中清除
2 1.ctx.auto_pop -->self.pop(exc)-->_request_ctx_stack.pop()
3
4 2._request_ctx_stack = LocalStack()-->return stack.pop()
6.应用上下文原理(跟请求上下文的原理几乎一致)
1 通过app_ctx.push()将app_ctx对象存储到threadinglocal中,分配一块独有的内存空间,app_ctx对象中具有:app_ctx.app与app_ctx.g
2
3 1.app_ctx.g是存储变量用的
4 示例代码:
5 from flask import Flask,request,g
6
7 app = Flask(__name__)
8
9 @app.before_request
10 def before():
11 g.permission_code_list = ['list','add']
12
13
14 @app.route('/',methods=['GET',"POST"])
15 def index():
16 print(g.permission_code_list)
17 return "index"
18
19
20 if __name__ == '__main__':
21 app.run()
补充:
a:离线脚本示例
1 """
2 需求:不用数据库连接池,显示数据库连接
3 """
4 class SQLHelper(object):
5
6 def open(self):
7 pass
8
9 def fetch(self,sql):
10 pass
11
12 def close(self):
13 pass
14
15 def __enter__(self):
16 self.open()
17 return self
18
19 def __exit__(self, exc_type, exc_val, exc_tb):
20 self.close()
21
22
23 # obj = SQLHelper()
24 # obj.open()
25 # obj.fetch('select ....')
26 # obj.close()
27
28
29 with SQLHelper() as obj: # 自动调用类中的__enter__方法, obj就是__enter__返回值
30 obj.fetch('xxxx')
31 # 当执行完毕后,自动调用类 __exit__ 方法
1 from flask import Flask,current_app,globals,_app_ctx_stack
2
3 app1 = Flask('app01')
4 app1.debug = False # 用户/密码/邮箱
5 # app_ctx = AppContext(self):
6 # app_ctx.app
7 # app_ctx.g
8
9 app2 = Flask('app02')
10 app2.debug = True # 用户/密码/邮箱
11 # app_ctx = AppContext(self):
12 # app_ctx.app
13 # app_ctx.g
14
15
16
17 with app1.app_context():# __enter__方法 -> push -> app_ctx添加到_app_ctx_stack.local
18 # {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438>]}}
19 print(_app_ctx_stack._local.__storage__)
20 print(current_app.config['DEBUG'])
21
22 with app2.app_context():
23 # {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438> ]}}
24 print(_app_ctx_stack._local.__storage__)
25 print(current_app.config['DEBUG'])
26
27 print(current_app.config['DEBUG'])
7.总结
1 1. 面向对象私有
2 class Foo(object):
3
4 def __init__(self):
5 self.name = 'alex'
6 self.__age = 18
7
8 def get_age(self):
9 return self.__age
10
11 obj = Foo()
12 # print(obj.name)
13 # print(obj.get_age())
14 # 强制获取私有字段
15 print(obj._Foo__age)
16
17 2. 谈谈Flask上下文管理
18 - 与django相比是两种不同的实现方式。
19 - django/tornado是通过传参数形式
20 - flask是通过上下文管理
21 两种都可以实现,只不过试下方式不一样。
22 - 上下文管理:
23 - threading.local/Local类,其中创建了一个字典{greelet做唯一标识:存数据} 保证数据隔离
24 - 请求进来:
25 - 请求相关所有数据封装到了RequestContext中。
26 - 再讲RequestContext对象添加到Local中(通过LocalStack将对象添加到Local对象中)
27 - 使用,调用request
28 - 调用此类方法 request.method、print(request)、request+xxx 会执行LocalProxy中对应的方法
29 - 函数
30 - 通过LocalStack去Local中获取值。
31 - 请求终止
32 - 通过LocalStack的pop方法 Local中将值异常。
33
34 3.上下文
35 a. 请求上下文
36 - request
37 - session
38 b. 应用上下文
39
40
41 请求流程:
42 _request_ctx_stack.local = {
43
44 }
45
46 _app_ctx_stack.local = {
47
48 }
49
50
51 3.1. 请求到来 ,有人来访问
52 # 将请求相关的数据environ封装到了RequestContext对象中
53 # 再讲对象封装到local中(每个线程/每个协程独立空间存储)
54 # ctx.app # 当前APP的名称
55 # ctx.request # Request对象(封装请求相关东西)
56 # ctx.session # 空
57 _request_ctx_stack.local = {
58 唯一标识:{
59 "stack":[ctx, ]
60 },
61 唯一标识:{
62 "stack":[ctx, ]
63 },
64 }
65
66
67 # app_ctx = AppContext对象
68 # app_ctx.app
69 # app_ctx.g
70
71 _app_ctx_stack.local = {
72 唯一标识:{
73 "stack":[app_ctx, ]
74 },
75 唯一标识:{
76 "stack":[app_ctx, ]
77 },
78 }
79
80 3.2. 使用
81 from flask import request,session,g,current_app
82
83 print(request,session,g,current_app)
84
85 都会执行相应LocalProxy对象的 __str__
86
87 current_app = LocalProxy(_find_app)
88 request = LocalProxy(partial(_lookup_req_object, 'request'))
89 session = LocalProxy(partial(_lookup_req_object, 'session'))
90
91 current_app = LocalProxy(_find_app)
92 g = LocalProxy(partial(_lookup_app_object, 'g'))
93
94 3.3. 终止,全部pop
95
96 问题1:多线程是如何体现?
97 问题2:flask的local中保存数据时,使用列表创建出来的栈。为什么用栈?
98 - 如果写web程序,web运行环境;栈中永远保存1条数据(可以不用栈)。
99 - 写脚本获取app信息时,可能存在app上下文嵌套关系。
100 from flask import Flask,current_app,globals,_app_ctx_stack
101
102 app1 = Flask('app01')
103 app1.debug = False # 用户/密码/邮箱
104 # app_ctx = AppContext(self):
105 # app_ctx.app
106 # app_ctx.g
107
108 app2 = Flask('app02')
109 app2.debug = True # 用户/密码/邮箱
110 # app_ctx = AppContext(self):
111 # app_ctx.app
112 # app_ctx.g
113
114
115
116 with app1.app_context():# __enter__方法 -> push -> app_ctx添加到_app_ctx_stack.local
117 # {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438>]}}
118 print(_app_ctx_stack._local.__storage__)
119 print(current_app.config['DEBUG'])
120
121 with app2.app_context():
122 # {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438> ]}}
123 print(_app_ctx_stack._local.__storage__)
124 print(current_app.config['DEBUG'])
125
126 print(current_app.config['DEBUG'])
127
128 3.3. 多app应用
129
130 from werkzeug.wsgi import DispatcherMiddleware
131 from werkzeug.serving import run_simple
132 from flask import Flask, current_app
133
134 app1 = Flask('app01')
135
136 app2 = Flask('app02')
137
138
139
140 @app1.route('/index')
141 def index():
142 return "app01"
143
144
145 @app2.route('/index2')
146 def index2():
147 return "app2"
148
149 # http://www.oldboyedu.com/index
150 # http://www.oldboyedu.com/sec/index2
151 dm = DispatcherMiddleware(app1, {
152 '/sec': app2,
153 })
154
155 if __name__ == "__main__":
156 run_simple('localhost', 5000, dm)
157
158 4.信号
159 a. before_first_request
160 b. 触发 request_started 信号
161 c. before_request
162 d. 模板渲染
163 渲染前的信号 before_render_template.send(app, template=template, context=context)
164 rv = template.render(context) # 模板渲染
165 渲染后的信号 template_rendered.send(app, template=template, context=context)
166 e. after_request
167 f. session.save_session()
168 g. 触发 request_finished信号
169
170 如果上述过程出错:
171 触发错误处理信号 got_request_exception.send(self, exception=e)
172
173 h. 触发信号 request_tearing_down
174
175 5.面向对象
176 - 封装
177 class Foo:
178 def __init__(self):
179 self.age = 123
180 self.nmame = 'ssdf'
181
182 class Bar:
183 def __init__(self):
184 self.xx = 111
185
186
187
188 class Base:
189 def __init__(self):
190 self.f = Foo()
191 self.x = Bar()
192 - 某个值+括号
193 - 函数/方法
194 - 类
195 - 对象
196
197 - 特殊的双下划线方法:
198 __new__
199 __call__
200 __str__
201 __setattr__
202 __setitem__
203 __enter__
204 __exit__
205 __add__
206
207 PS: Flask的LocalProxy中全部使用。
208
209 - 强制调用私有字段
210 - 派生类中无法调用基类私有字段
Flask请求和应用上下文源码分析的更多相关文章
- Flask系列10-- Flask请求上下文源码分析
总览 一.基础准备. 1. local类 对于一个类,实例化得到它的对象后,如果开启多个线程对它的属性进行操作,会发现数据时不安全的 import time from threading import ...
- Flask框架 (四)—— 请求上下文源码分析、g对象、第三方插件(flask_session、flask_script、wtforms)、信号
Flask框架 (四)—— 请求上下文源码分析.g对象.第三方插件(flask_session.flask_script.wtforms).信号 目录 请求上下文源码分析.g对象.第三方插件(flas ...
- Flask之 请求,应用 上下文源码解析
什么是上下文? 每一段程序都有很多外部变量.只有像Add这种简单的函数才是没有外部变量的.一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行.你为了使他们运行,就要给所有的外部变量一个一个写 ...
- Flask - 请求处理流程和上下文源码分析
目录 Flask - 请求处理流程和上下文 WSGI Flask的上下文对象及源码解析 0. 请求入口 1.请求上下文对象的创建 2. 将请求上下文和应用上下文入栈 3.根据请求的URl执行响应的视图 ...
- drf复习(一)--原生djangoCBV请求生命周期源码分析、drf自定义配置文件、drf请求生命周期dispatch源码分析
admin后台注册model 一.原生djangoCBV请求生命周期源码分析 原生view的源码路径(django/views/generic/base.py) 1.从urls.py中as_view ...
- Flask上下文源码分析(二)
前面第一篇主要记录了Flask框架,从http请求发起,到返回响应,发生在server和app直接的过程. 里面有说到,Flask框架有设计了两种上下文,即应用上下文和请求上下文 官方文档里是说先理解 ...
- Flask上下文源码分析(一)
flask中的上下文分两种,application context和request context,即应用上下文和请求上下文. 从名字上看,可能会有误解,认为应用上下文是一个应用的全局变量,所有请 ...
- flask请求上下文源码分析
一.什么是上下文 每一段程序都有很多外部变量,只有像add这种简单的函数才是没有外部变量的,一旦你的一段程序有了外部变量,这段程序就不完整了,不能独立运行,你为了使他们能运行,就要给所有的外部变量一个 ...
- flask中路由的本质源码分析
flask中url的本质: 吧url和视图函数封装到一个Rule对象里面去了,并且吧这个对象添加到url_map中 Rule={"url":'/index','method':'i ...
随机推荐
- canopy聚类算法的MATLAB程序
canopy聚类算法的MATLAB程序 凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 1. canopy聚类算法简介 Canopy聚类算法是一个将对象分组到 ...
- 数据分析三剑客 numpy,oandas,matplotlib(2)
Pandas的数据结构 导入pandas: 三剑客 import pandas as pd from pandas import Series,DataFrame import numpy as n ...
- 《为什么说Redis是单线程的以及Redis为什么这么快!》
为什么说Redis是单线程的以及Redis为什么这么快! 一.前言 近乎所有与Java相关的面试都会问到缓存的问题,基础一点的会问到什么是“二八定律”.什么是“热数据和冷数据”,复杂一点的会问到缓 ...
- MySQL实战45讲学习笔记:第四十四讲
一.引子 这是我们专栏的最后一篇答疑文章,今天我们来说说一些好问题. 在我看来,能够帮我们扩展一个逻辑的边界的问题,就是好问题.因为通过解决这样的问题,能够加深我们对这个逻辑的理解,或者帮我们关联到另 ...
- 谈谈vue.js中methods watch和compute的区别和联系
methods,watch和computed都是以函数为基础的,但各自却都不同: 1.watch和computed都是以Vue的依赖追踪机制为基础的,它们都试图处理这样一件事情:当某一个数据(称它为依 ...
- what is variable?
what is variable? variable:pytorch中的变量,存储tensor,数值会不断变动 在 Torch 中的 Variable 就是一个存放会变化的值的地理位置. 里面的值会不 ...
- oracle的instr()函数
我们知道很多语言都提供了indexOf()和lastIndexOf()函数,以便能查找某个字符在某个字符串中的出现的位置和最后一次出现的位置. 但是Oracle没有提供这两个函数,事实上,它提供了一个 ...
- Django学习笔记(16)——扩展Django自带User模型,实现用户注册与登录
一,项目题目:扩展Django自带User模型,实现用户注册与登录 我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统.此时我们需要实现包括用户注册,登录,用户认证,注销,修改密码等功能. ...
- RESTful及API设计(原)
RESTful是一种架构风格,是由Fielding博士在自己的博士论文中提出并详细论述的. 它是用于指导web系统设计的,而指导API设计只是它的一小部分功能而已,如果只用它指导API设计就太大材小用 ...
- .net core 3.0中的Json API
在.net core 3.0中,内置了一套新的json api,主要用于去除asp.net core对json.net的依赖,同时也提供了更好的性能(直接处理 UTF-8,而无需转码到 UTF-16) ...