当前位置:   article > 正文

Sanic学习记录_sanic 为啥 快

sanic 为啥 快

Sanic学习记录
为什么要学习sanic,因为它快,因为它对开发者很友好,因为他有完整的中文文档,因为它有活跃的社区。使用它可以快速开发一个webserver,也可以在社区找到很多解决方案。


1 快速开启一个web server

# server.py
from sanic import Sanic
from sanic.response import text

app = Sanic('app') 

@app.get('/')
async def index(request):
    return text('hello word')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

启动, sanic 自带了启动应用

sanic server.app
  • 1

也可以使用asgi的启动

uvicorn server:app
  • 1
1.1 创建app
app = Sanic('app')   # 这里必须有一个参数,主要是用于获取上下文应用,类似flash中的current_app

app2 = Sanic.get_app('app')    # 如果当前只有一个,参数可省略
app2 = Sanic.get_app('app2',force_create=True)   # 如果app2 不存在,则创建


###   Sanic.get_app('app')  在使用时会报错,找不到已创建的sanic对象,不知道是不是window的锅
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
1.2 配置文件
# 方式一:对象属性赋值
app.config.DB_NAME = 'apdb'
# 方式二:字典属性赋值
app.config['DB_USER'] = 'appUser'
# 方式三:字典update
db_setting = {
   
    'DB_HOST':'localhost',
    'DB_NAME':'appdb',
    'DB_USER':'appuser'
}
app.config.update(db_setting)

# 方式四:设置环境变量,并以SANIC_ 开头,sanic会自动识别,也可以自定义前缀
# SANIC_ 前缀的环境变量
export SANIC_MY_SETTING = './setting.py'
print(app.config.MY_SETTING)

# 自定义前缀  APP_ 
export APP_MY_SETTING = './setting.py'
app = Sanic('app',load_env='APP_')

# 方式五:从配置文件更新
app.update_config('./setting')
app.update_config("${APP_MY_SETTING}")   # 这里的变量是环境变量,不是SANIC_开头的

# 方式六:update_config 字典 或者 类
app.update_config({
   "DB_HOST":"127.0.0.1"})

class MYConfig:
    A = 1
    B = 2
    
app.update_config(MYConfig)           # 类
app.update_config(MYConfig())         # 对象
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
1.3 共享应用级对象
app.db = Database()    # 不推荐
app.ctx.db = Database()   # 推荐
  • 1
  • 2

2.视图

2.1 函数视图

它既可以是一个普通函数,也可以是一个异步的函数。

一个api接口的一个请求方法对应一个函数,有几个方法注册几次路由

from sanic import Sanic
from sanic.response import text

app = Sanic('app') 

@app.get('/')
async def index(request):
    return text('这是get请求')

@app.post('/')
async def index_post(request):
    return text('这是post请求')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
2.2 类视图

一个api接口的不同请求方法对应一个类视图,路由只需要注册一次

from sanic import Sanic
from sanic.response import text

app = Sanic('app') 

from sanic.views import HTTPMethodView
# 类视图
class IndexView(HTTPMethodView):
    async def get(self, request):
        return text('这是get请求')
    
    
    async def post(self,request):
        return text('这是post请求')

# 路由注册
app.add_route(IndexView.as_view(), "/")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

无论是函数视图还是类视图,都必须接收一个参数,接收的参数是Request对象,视图返回的对象都是HTTPResponse对象

所以,正常的写法应该是指定参数类型,就像这样

from sanic.response import HTTPResponse, text
from sanic.request import Request
from sanic.views import HTTPMethodView
from sanic import Sanic

app = Sanic('app') 

# 函数视图
@app.get('/')
async def index(request:Request) -> HTTPResponse:
    return text('这是get请求')


# 类视图
class IndexView(HTTPMethodView):
    async def get(self, request:Request) -> HTTPResponse:
        return text('这是get请求')
app.add_route(IndexView.as_view(),'/idx')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

实际上,默认的第一个参数类型就是Request,响应对象类型就是 HTTPResponse ,所以大多数时候,都是省略的

2.3 路由

路由和视图是对应关系

添加路由的几种方式

# 方式一:函数视图使用装饰器,并指定请求方法
@app.route('/test', methods=["POST", "PUT"])
async def handler(request):
    return text('OK')

# 方式二:根据请求方式添加装饰器
@app.get('/test')
async def handler(request):
    return text('OK')

# 还可以使用post,put,delete等等
# 方式三:注册路由
app.add_route(
        handler,    # 如果是函数视图,直接填函数名,如果是类视图,填  类视图.as_view()
        '/test',
        methods=["POST", "PUT"],
)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

路径反解析

反解析 app.url_for(‘视图名’) 视图名由三部分组成(应用名称.蓝图名.函数名),其中函数名可以指定,只需要定义视图时传入name参数,就可以自定义名称

app.url_for("post_handler", post_id=5, arg_one="one", _anchor="anchor")
# post_id是路径参数,如果有的话,arg_one 是查询参数,_anchor 是锚点
# '/posts/5?arg_one=one#anchor'

# _external requires you to pass an argument _server or set SERVER_NAME in app.config if not url will be same as no _external
 app.url_for("post_handler", post_id=5, arg_one="one", _external=True)
# _external 是绝对uri
# '//server/posts/5?arg_one=one'

# when specifying _scheme, _external must be True
app.url_for("post_handler", post_id=5, arg_one="one", _scheme="http", _external=True)
# _scheme 是协议
# 'http://server/posts/5?arg_one=one'

# you can pass all special arguments at once
app.url_for("post_handler", post_id=5, arg_one=["one", "two"], arg_two=2, _anchor="anchor", _scheme="http",
                 _external=True, _server="another_server:8888")
# _server 是执行ip和端口
# 'http://another_server:8888/posts/5?arg_one=one&arg_one=two&arg_two=2#anchor'

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

重定向

重定向往往是和反解析一起使用的,反解析之后的结果传递给重定向就会返回反解析对应的API

@app.route('/')
async def index(request):
    # generate a URL for the endpoint `post_handler`
    url = app.url_for('post_handler', post_id=5)
    
    # Redirect to `/posts/5`
    return redirect(url)


@app.route('/posts/<post_id>')
async def post_handler(request, post_id):
    ...

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

静态路径

app.static("/static", "/path/to/directory")
  • 1

第一个参数是静态文件所需要匹配的路由

第二个参数是渲染文件所在的文件(夹)路径

name参数,自定义的视图名称,可用于反解析

app.static("/static", "/path/to/directory")

# 反解析
app.url_for(
        "static",
        name="static",
        filename="file.txt",
)
# 结果   '/static/file.txt'

# 带name参数的
app.static(
        "/user/uploads",
        "/path/to/uploads",
        name="uploads",
)

# 反解析
app.url_for(
        "static",
        name="uploads",
        filename="image.png",
)
# 结果 '/user/uploads/image.png'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

3 参数

实际上,默认的第一个参数类型就是Request,对象中存储了除路径参数意外的其他参数以及其他数据

3.1 路径参数
@app.get('/index/<name:str>')      # 当参数在路径中时,就是路径参数
async def indexname(request,name):
    return text(name)
  • 1
  • 2
  • 3
3.2 查询参数

查询字符串通常用在get请求中,在浏览器中,请求路径后面的?name=alan&d=1 类似这样的都是查询参数

@app.get('/index',name='index')    # api 接口名称,如果不填,默认是函数名
async def indexname(request):      
    request.args                    # 键值对的方式,及字典的方式
    request.query_args              # [(key,value),...]的方式
    return text('哈哈哈')
  • 1
  • 2
  • 3
  • 4
  • 5
3.3 请求体参数

除了路径参数和查询参数,其他的参数都被封装到了请求体参数中,都可以使用 request.body 进行访问

但是,body是二进制的字符串,需要转译,而对于文件,通过body接收到的内容并不是我们想要的

3.3.1 json

当前端是以json格式的数据传递 的,我们可以使用request.json接收,接收到的内容会被自动转成字典,方便使用

@app.get('/index',name='index')    # api 接口名称,如果不填,默认是函数名
async def indexname(request):       
    request.json                    # 键值对的方式,及字典的方式
    return empty()
  • 1
  • 2
  • 3
  • 4
3.3.2 表单

当前端是以表单的形式传递的参数,一般使用request.form 接收,接收到的也是字典

3.3.3 文件

当前端传递的是文件,后端使用 request.files 接收,接收到的也是字典

3.4 请求头

从请求头中解析到的 Token <token> 或者 Bearer <token> 将会被赋值给 request.token

普通请求头 request.headers 是特殊的字典,每个键对应的都是一个列表

request.headers["foo"],
request.headers.get("Foo"),
request.headers.getone("FOO")
# 以上获取到的都是第一个元素,键不区分大小写
request.headers.getall("fOo")
# 获取的结果是列表
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
list(request.headers.items())
# 获取所有的请求头
  • 1
  • 2
3.5 Cookie

通过 request.cookies获取

3.6请求上下文 request.ctx

自定义上下文是为了应用程序和拓展插件而保留的,Sanic 本身并不使用它,最常用的是通过中间件给request对象添加参数,在视图中访问

可以在中间件处理中在request.ctx对象上自定义赋值,以便在访问视图时使用

3.7 连接上下文 request.conn_info.ctx

当多个请求共享一个连接时,Sanic 将提供一个上下文对象来允许这些请求共享状态。

尚在实验阶段,未完善使用

3.8 流式接收
# 类视图方法一:使用装饰器
from sanic.views import stream

class SimpleView(HTTPMethodView):
    @stream
    async def post(self, request):
        result = ""
        while True:
            body = await request.stream.read()
            if body is None:
                break
            result += body.decode("utf-8"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/136819
推荐阅读
相关标签
  

闽ICP备14008679号