설치하기

$ pip3 install sanic
or
$ SANIC_NO_UVLOOP=true SANIC_NO_UJSON=true pip3 install sanic
$ echo sanic==19.12.2 >> requirements.txt
$ pip install -r requirements.txt

시작하기

from sanic import Sanic
from sanic.response import json

app = Sanic(name="My API Server")

@app.route("/")
async def test(request):
    return json({"hello": "world"})

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)

디버그 모드

app.run(host="0.0.0.0", port=8000, debug=True)
app.run(host="0.0.0.0", port=8000, auto_reload=True)
  • debug모드시 auto_reload 켜짐.
  • posix OS가 아니면 auto_reload 사용 불가하다.
    • 구현안됨 예외가 발생함.

로깅

from sanic.log import logger

@app.route("/test")
async def test(request):
    logger.info("Test")
    return response.json({"hello":"world"})

라우팅

@app.route('/string/<string_arg:string>')
@app.route('/int/<integer_arg:int>')
@app.route('/number/<number_arg:number>')
@app.route('/alpha/<alpha_arg:alpha>')
@app.route('/path/<path_arg:path>')
@app.route('/uuid/<uuid_arg:uuid>')
@app.route('/person/<name:[A-z]+>')
@app.route('/folder/<folder_id:[A-z0-9]{0,4}>')

@app.route('/company/profile/<name:string>')
async def company_profile(request, name):
    res = fmp.get_data(symbol=name, category='profile', datatype='json')
    fields = res['profile']
    return response.json(fields)

GET, POST

@app.route('/post', methods=['POST'])
async def post_handler(request):
    print(request.args) # {'arg1': ['1'], 'arg2': ['2']}
    print(request.json) # {'title': 'Test Title', 'body': 'Test Body'}
    print(request.args['arg1'])
    return response.json({'result':''})

@app.route('/get', methods=['GET'])
async def get_handler(request):
    print(request.args)
    print(request.json)
    print(request.args['arg1'])
    return response.json({'result':''})

add_route

  • 핸들러에 url을 연결해 라우팅해준다.
  • Method 선택 가능
  • @app.route, @app.get, @app.post 는 모두 add_route를 사용하는 데코레이터다.
from sanic.response import text

async def handler1(request):
    return text('OK')

async def handler2(request, name):
    return text(f'{name}')

async def person_handler2(request, name):
    return text(f'{name}')

app.add_route(handler1, '/test')
app.add_route(handler2, '/folder/<name>')
app.add_route(person_handler2, '/person/<name:[A-z]>', methods=['GET'])

url_for

  • url_for: 핸들러에 연결된 url을 얻는다.
    • 태그명, 매개변수 사용가능.
from sanic.response import redirect

@app.route('/')
async def index(request):
    url = app.url_for('post_handler', post_id=5)
    return redirect(url)

@app.route('/posts/<post_id>')
async def post_handler(request, post_id):
    return text(f'#{post_id}')
url = app.url_for('post_handler', post_id=5, arg_one='one', arg_two='two')
# /posts/5?arg_one=one&arg_two=two

url = app.url_for('post_handler', post_id=5, arg_one=['one', 'two'])
# /posts/5?arg_one=one&arg_one=two

url = app.url_for('post_handler', post_id=5, arg_one='one', _anchor='anchor')
# /posts/5?arg_one=one#anchor

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

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

# you can pass all special arguments at once
url = 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')
# http://another_server:8888/posts/5?arg_one=one&arg_one=two&arg_two=2#anchor

블루프린트

  • TODO

미들웨어

  • TODO

클래스 중심의 뷰

from sanic.views import HTTPMethodView

class UserView(HTTPMethodView):

    # def get(self, request):
    #     return response.text('GET')

    async def get(self, request, name):
        return response.text(f'GET {name}')

    def post(self, request, name):
        return response.text(f'POST {name}')

    def put(self, request, name):
        return response.text(f'PUT {name}')

    def patch(self, request, name):
        return response.text(f'PATCH {name}')

    def delete(self, request, name):
        return response.text(f'DELETE {name}')

app.add_route(UserView.as_view(), '/user/<name>')
  • async 사용 가능.
  • URL 매개변수 넘길 수 있음.

클래스뷰 데코레이터

  1. 모든 메소드에 데코레이터
class ViewWithDecorator(HTTPMethodView):
    decorators = [some_decorator_here]

    def get(self, request, name):
        return text('Hello I have a decorator')

    def post(self, request, name):
        return text("Hello I also have a decorator")

app.add_route(ViewWithDecorator.as_view(), '/url')
  1. 특정 메소드에 데코레이터
class ViewWithSomeDecorator(HTTPMethodView):

    @staticmethod
    @some_decorator_here
    def get(request, name):
        return text("Hello I have a decorator")

    def post(self, request, name):
        return text("Hello I don't have any decorators")

클래스뷰 URL 얻기

url = app.url_for('SpecialClassView')
return redirect(url)

CompositionView

from sanic.views import CompositionView

def get_handler(request):
    return text('I am a get method')

view = CompositionView()
view.add(['GET'], get_handler)
view.add(['POST', 'PUT'], lambda request: text('I am a post/put method'))

app.add_route(view, '/')

참고자료