阿吉的Sanic-examples23教程

Document

你的鼓励是我前进的动力,请为我点个赞吧!
Examples¶

This section of the documentation is a simple collection of example
code that can help you get a quick start on your application
development. Most of these examples are categorized and provide you
with a link to the working code example in the Sanic Repository

Basic Examples¶

This section of the examples are a collection of code that provide a
simple use case example of the sanic application.

Simple Apps¶

A simple sanic application with a single async method with text and
json type response.

from sanic import Sanic
from sanic import response as res
app = Sanic(\_\_name\_\_)
@app.route("/")
async def test(req):
    return res.text("I\\'m a teapot", status=418)
if \_\_name\_\_ == '\_\_main\_\_':
    app.run(host="0.0.0.0", port=8000)

e.g.2:

from sanic import Sanic
from sanic import response
app = Sanic(\_\_name\_\_)
@app.route("/")
async def test(request):
   return response.json({"test": True})
if \_\_name\_\_ == '\_\_main\_\_':
   app.run(host="0.0.0.0", port=8000)

Simple App with Sanic Views¶

Showcasing the simple mechanism of using sanic.viewes.HTTPMethodView
as well as a way to extend the same into providing a custom async
behavior for view.

from sanic import Sanic
from sanic.views import HTTPMethodView
from sanic.response import text
app = Sanic('some_name')
class SimpleView(HTTPMethodView):

    def get(self, request):
        return text('I am get method')

    def post(self, request):
        return text('I am post method')

    def put(self, request):
        return text('I am put method')

    def patch(self, request):
        return text('I am patch method')

    def delete(self, request):
        return text('I am delete method')

class SimpleAsyncView(HTTPMethodView):

    async def get(self, request):
        return text('I am async get method')

    async def post(self, request):
        return text('I am async post method')

    async def put(self, request):
        return text('I am async put method')



app.add_route(SimpleView.as_view(), '/')
app.add_route(SimpleAsyncView.as_view(), '/async')

if \_\_name\_\_ == '\_\_main\_\_':
    app.run(host="0.0.0.0", port=8000, debug=True)

URL Redirect¶

from sanic import Sanic
from sanic import response


app = Sanic(\_\_name\_\_)

    

@app.route('/')
def handle_request(request):
    return response.redirect('/redirect')



@app.route('/redirect')
async def test(request):
    return response.json({"Redirected": True})



if \_\_name\_\_ == '\_\_main\_\_':
    app.run(host="0.0.0.0", port=8000)

Named URL redirection¶

Sanic provides an easy to use way of redirecting the requests via a
helper method called url_for that takes a unique url name as argument
and returns you the actual route assigned for it. This will help in
simplifying the efforts required in redirecting the user between
different section of the application.

from sanic import Sanic
from sanic import response


app = Sanic(\_\_name\_\_)



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



@app.route('/posts/<post_id>')
async def post_handler(request, post_id):
    return response.text('Post - {}'.format(post_id))
    
if \_\_name\_\_ == '\_\_main\_\_':
    app.run(host="0.0.0.0", port=8000, debug=True)

Blueprints¶

Sanic provides an amazing feature to group your APIs and routes under
a logical collection that can easily be imported and plugged into any
of your sanic application and it’s called blueprints

from sanic import Blueprint, Sanic
from sanic.response import file, json


app = Sanic(\_\_name\_\_)
blueprint = Blueprint('name', url_prefix='/my_blueprint')
blueprint2 = Blueprint('name2', url_prefix='/my_blueprint2')
blueprint3 = Blueprint('name3', url_prefix='/my_blueprint3')



@blueprint.route('/foo')
async def foo(request):
    return json({'msg': 'hi from blueprint'})



@blueprint2.route('/foo')
async def foo2(request):
    return json({'msg': 'hi from blueprint2'})



@blueprint3.route('/foo')
async def index(request):
    return await file('websocket.html')



@app.websocket('/feed')
async def foo3(request, ws):
    while True:
        data = 'hello!'
        print('Sending: ' + data)
        await ws.send(data)
        data = await ws.recv()
        print('Received: ' + data)

app.blueprint(blueprint)
app.blueprint(blueprint2)
app.blueprint(blueprint3)

app.run(host="0.0.0.0", port=8000, debug=True)

Logging Enhancements¶

Even though Sanic comes with a battery of Logging support it allows
the end users to customize the way logging is handled in the
application runtime.

from sanic import Sanic
from sanic import response
import logging


logging_format = "\[%(asctime)s\] %(process)d-%(levelname)s "
logging_format += "%(module)s::%(funcName)s():l%(lineno)d: "
logging_format += "%(message)s"

logging.basicConfig(
    format=logging_format,
    level=logging.DEBUG
)
log = logging.getLogger()

\# Set logger to override default basicConfig
sanic = Sanic()



@sanic.route("/")
def test(request):
    log.info("received request; responding with 'hey'")
    return response.text("hey")

sanic.run(host="0.0.0.0", port=8000)

The following sample provides an example code that demonstrates the
usage of sanic.app.Sanic.middleware() in order to provide a mechanism
to assign a unique request ID for each of the incoming requests and
log them via aiotask-context.

*’’’
Based on example from https://github.com/Skyscanner/aiotask-context
and examples/{override_logging,run_async}.py.

Needs https://github.com/Skyscanner/aiotask-context/tree/52efbc21e2e1def2d52abb9a8e951f3ce5e6f690 or newer
$ pip install git+https://github.com/Skyscanner/aiotask-context.git
‘’’*

import asyncio
import uuid
import logging
from signal import signal, SIGINT

from sanic import Sanic
from sanic import response

import uvloop
import aiotask_context as context

log = logging.getLogger(\_\_name\_\_)



class RequestIdFilter(logging.Filter):
    def filter(self, record):
        record.request_id = context.get('X-Request-ID')
        return True



LOG_SETTINGS = {
    'version': 1,
    'disable\_existing\_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
            'formatter': 'default',
            'filters': \['requestid'\],
        },
    },
    'filters': {
        'requestid': {
            '()': RequestIdFilter,
        },
    },
    'formatters': {
        'default': {
            'format': '%(asctime)s  %(levelname)s  %(name)s:%(lineno)d  %(request_id)s | %(message)s',
        },
    },
    'loggers': {
        '': {
            'level': 'DEBUG',
            'handlers': \['console'\],
            'propagate': True
        },
    }
}



app = Sanic(\_\_name\_\_, log_config=LOG_SETTINGS)



@app.middleware('request')
async def set\_request\_id(request):
    request_id = request.headers.get('X-Request-ID') or str(uuid.uuid4())
    context.set("X-Request-ID", request_id)



@app.route("/")
async def test(request):
    log.debug('X-Request-ID: %s', context.get('X-Request-ID'))
    log.info('Hello from test!')
    return response.json({"test": True})



if \_\_name\_\_ == '\_\_main\_\_':
    asyncio.set\_event\_loop(uvloop.new\_event\_loop())
    server = app.create_server(host="0.0.0.0", port=8000)
    loop = asyncio.get\_event\_loop()
    loop.set\_task\_factory(context.task_factory)
    task = asyncio.ensure_future(server)
    try:
        loop.run_forever()
    except:
        loop.stop()

Sanic Streaming Support¶

Sanic framework comes with in-built support for streaming large files and the following code explains the process to setup a Sanic application with streaming support.

from sanic import Sanic
from sanic.views import CompositionView
from sanic.views import HTTPMethodView
from sanic.views import stream as stream_decorator
from sanic.blueprints import Blueprint
from sanic.response import stream, text


bp = Blueprint('blueprint\_request\_stream')
app = Sanic('request_stream')




class SimpleView(HTTPMethodView):

    @stream_decorator
    async def post(self, request):
        result = ''
        while True:
            body = await request.stream.get()
            if body is None:
                break
            result += body.decode('utf-8')
        return text(result)




@app.post('/stream', stream=True)
async def handler(request):
    async def streaming(response):
        while True:
            body = await request.stream.get()
            if body is None:
                break
            body = body.decode('utf-8').replace('1', 'A')
            await response.write(body)
    return stream(streaming)




@bp.put('/bp_stream', stream=True)
async def bp_handler(request):
    result = ''
    while True:
        body = await request.stream.get()
        if body is None:
            break
        result += body.decode('utf-8').replace('1', 'A')
    return text(result)




async def post_handler(request):
    result = ''
    while True:
        body = await request.stream.get()
        if body is None:
            break
        result += body.decode('utf-8')
    return text(result)

app.blueprint(bp)
app.add_route(SimpleView.as_view(), '/method_view')
view = CompositionView()
view.add(\['POST'\], post_handler, stream=True)
app.add_route(view, '/composition_view')




if \_\_name\_\_ == '\_\_main\_\_':
    app.run(host='0.0.0.0', port=8000)

Sample Client app to show the usage of streaming application by a client code.

import requests

    \# Warning: This is a heavy process.
    
    data = ""
    for i in range(1, 250000):
        data += str(i)
    
    r = requests.post('http://0.0.0.0:8000/stream', data=data)
    print(r.text)

Sanic Concurrency Support¶

Sanic supports the ability to start an app with multiple worker
support. However, it’s important to be able to limit the concurrency
per process/loop in order to ensure an efficient execution. The
following section of the code provides a brief example of how to limit
the concurrency with the help of asyncio.Semaphore

from sanic import Sanic
from sanic.response import json


import asyncio
import aiohttp

app = Sanic(\_\_name\_\_)

sem = None




@app.listener('before\_server\_start')
def init(sanic, loop):
global sem
concurrency\_per\_worker = 4
sem = asyncio.Semaphore(concurrency\_per\_worker, loop=loop)

async def bounded_fetch(session, url):
"""
Use session object to perform 'get' request on url
"""
async with sem, session.get(url) as response:
    return await response.json()




@app.route("/")
async def test(request):
"""
Download and serve example JSON
"""
url = "https://api.github.com/repos/channelcat/sanic"

async with aiohttp.ClientSession() as session:
    response = await bounded_fetch(session, url)
    return json(response)




app.run(host="0.0.0.0", port=8000, workers=2)

Sanic Deployment via Docker¶

Deploying a sanic app via docker and docker-compose is an easy task to
achieve and the following example provides a deployment of the sample
simple_server.py

FROM python:3.5
MAINTAINER Channel Cat channelcat@gmail.com

    ADD . /code
    RUN pip3 install git+https://github.com/channelcat/sanic
    
    EXPOSE 8000
    
    WORKDIR /code
    
    CMD \["python", "simple_server.py"\]

version: ‘2’
services:
sanic:
build: .
ports:
- “8000:8000”

Monitoring and Error Handling¶

Sanic provides an extendable bare minimum implementation of a global exception handler via sanic.handlers.ErrorHandler. This example shows how to extend it to enable some custom behaviors.

“”"
Example intercepting uncaught exceptions using Sanic’s error handler framework.
This may be useful for developers wishing to use Sentry, Airbrake, etc.
or a custom system to log and monitor unexpected errors in production.
First we create our own class inheriting from Handler in sanic.exceptions,
and pass in an instance of it when we create our Sanic instance. Inside this
class’ default handler, we can do anything including sending exceptions to
an external service.
“”"
from sanic.handlers import ErrorHandler
from sanic.exceptions import SanicException
“”"
Imports and code relevant for our CustomHandler class
(Ordinarily this would be in a separate file)
“”"

    class CustomHandler(ErrorHandler):
    
        def default(self, request, exception):
            \# Here, we have access to the exception object
            \# and can do anything with it (log, send to external service, etc)
    
            \# Some exceptions are trivial and built into Sanic (404s, etc)
            if not isinstance(exception, SanicException):
                print(exception)
    
            \# Then, we must finish handling the exception by returning
            \# our response to the client
            \# For this we can just call the super class' default handler
            return super().default(request, exception)


    

    """
    This is an ordinary Sanic server, with the exception that we set the
    server's error_handler to an instance of our CustomHandler
    """
    
    from sanic import Sanic
    
    app = Sanic(\_\_name\_\_)
    
    handler = CustomHandler()
    app.error_handler = handler


    

    @app.route("/")
    async def test(request):
        \# Here, something occurs which causes an unexpected exception
        \# This exception will flow to our custom handler.
        raise SanicException('You Broke It!')
    
    if \_\_name\_\_ == '\_\_main\_\_':
        app.run(host="0.0.0.0", port=8000, debug=True)

Monitoring using external Service Providers¶

  • LogDNA

          import logging
          import socket
          from os import getenv
          from platform import node
          from uuid import getnode as get_mac
          
    
          from logdna import LogDNAHandler
          
          from sanic import Sanic
          from sanic.response import json
          from sanic.request import Request
          
          log = logging.getLogger('logdna')
          log.setLevel(logging.INFO)
      
    
          
    
          def get\_my\_ip_address(remote_server="google.com"):
              with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
                  s.connect((remote_server, 80))
                  return s.getsockname()\[0\]
      
    
          
    
          def get\_mac\_address():
              h = iter(hex(get_mac())\[2:\].zfill(12))
              return ":".join(i + next(h) for i in h)
      
    
          
    
          logdna_options = {
              "app": \_\_name\_\_,
              "index_meta": True,
              "hostname": node(),
              "ip": get\_my\_ip_address(),
              "mac": get\_mac\_address()
          }
          
          logdna_handler = LogDNAHandler(getenv("LOGDNA\_API\_KEY"), options=logdna_options)
          
          logdna = logging.getLogger(\_\_name\_\_)
          logdna.setLevel(logging.INFO)
          logdna.addHandler(logdna_handler)
          
          app = Sanic(\_\_name\_\_)
      
    
          
    
          @app.middleware
          def log_request(request: Request):
              logdna.info("I was Here with a new Request to URL: {}".format(request.url))
      
    
          
    
          @app.route("/")
          def default(request):
              return json({
                  "response": "I was here"
              })
      
    
          
    
          if \_\_name\_\_ == "\_\_main\_\_":
              app.run(
                  host="0.0.0.0",
                  port=getenv("PORT", 8080)
              )
    
  • RayGun

      from os import getenv
      
    
      from raygun4py.raygunprovider import RaygunSender
      
      from sanic import Sanic
      from sanic.exceptions import SanicException
      from sanic.handlers import ErrorHandler
    
    
      
    
      class RaygunExceptionReporter(ErrorHandler):
      
          def \_\_init\_\_(self, raygun\_api\_key=None):
              super().\_\_init\_\_()
              if raygun\_api\_key is None:
                  raygun\_api\_key = getenv("RAYGUN\_API\_KEY")
      
              self.sender = RaygunSender(raygun\_api\_key)
      
          def default(self, request, exception):
              self.sender.send_exception(exception=exception)
              return super().default(request, exception)
    
    
      
    
      raygun\_error\_reporter = RaygunExceptionReporter()
      app = Sanic(\_\_name\_\_, error_handler=raygun\_error\_reporter)
    
    
      
    
      @app.route("/raise")
      async def test(request):
          raise SanicException('You Broke It!')
    
    
      
    
      if \_\_name\_\_ == '\_\_main\_\_':
          app.run(
              host="0.0.0.0",
              port=getenv("PORT", 8080)
          )
    
  • Rollbar

      import rollbar
      
    
      from sanic.handlers import ErrorHandler
      from sanic import Sanic
      from sanic.exceptions import SanicException
      from os import getenv
      
      rollbar.init(getenv("ROLLBAR\_API\_KEY"))
    
    
      
    
      class RollbarExceptionHandler(ErrorHandler):
      
          def default(self, request, exception):
              rollbar.report_message(str(exception))
              return super().default(request, exception)
    
    
      
    
      app = Sanic(\_\_name\_\_, error_handler=RollbarExceptionHandler())
    
    
      
    
      @app.route("/raise")
      def create_error(request):
          raise SanicException("I was here and I don't like where I am")
    
    
      
    
      if \_\_name\_\_ == "\_\_main\_\_":
          app.run(
              host="0.0.0.0",
              port=getenv("PORT", 8080)
          )
    
  • Sentry

      from os import getenv
      
    
      from sentry_sdk import init as sentry_init
      from sentry_sdk.integrations.sanic import SanicIntegration
      
      from sanic import Sanic
      from sanic.response import json
      
      sentry_init(
          dsn=getenv("SENTRY_DSN"),
          integrations=\[SanicIntegration()\],
      )
      
      app = Sanic(\_\_name\_\_)
    
    
      
    
      \# noinspection PyUnusedLocal
      @app.route("/working")
      async def working_path(request):
          return json({
              "response": "Working API Response"
          })
    
    
      
    
      \# noinspection PyUnusedLocal
      @app.route("/raise-error")
      async def raise_error(request):
          raise Exception("Testing Sentry Integration")
    
    
      
    
      if \_\_name\_\_ == '\_\_main\_\_':
          app.run(
              host="0.0.0.0",
              port=getenv("PORT", 8080)
          )
    

Security¶

The following sample code shows a simple decorator based authentication and authorization mechanism that can be setup to secure your sanic api endpoints.

– coding: utf-8 –

    from sanic import Sanic
    from functools import wraps
    from sanic.response import json
    
    app = Sanic()


    

    def check\_request\_for\_authorization\_status(request):
        \# Note: Define your check, for instance cookie, session.
        flag = True
        return flag


    

    def authorized():
        def decorator(f):
            @wraps(f)
            async def decorated_function(request, *args, **kwargs):
                \# run some method that checks the request
                \# for the client's authorization status
                is_authorized = check\_request\_for\_authorization\_status(request)
    
                if is_authorized:
                    \# the user is authorized.
                    \# run the handler method and return the response
                    response = await f(request, *args, **kwargs)
                    return response
                else:
                    \# the user is not authorized.
                    return json({'status': 'not_authorized'}, 403)
            return decorated_function
        return decorator


    

    @app.route("/")
    @authorized()
    async def test(request):
        return json({'status': 'authorized'})
    
    if \_\_name\_\_ == "\_\_main\_\_":
        app.run(host="0.0.0.0", port=8000)

Sanic Websocket¶

Sanic provides an ability to easily add a route and map it to a websocket handlers.

    <html>
        <head>
            <title>WebSocket demo</title>
        </head>
        <body>
            <script>
                var ws = new WebSocket('ws://' + document.domain + ':' + location.port + '/feed'),
                    messages = document.createElement('ul');
                ws.onmessage = function (event) {
                    var messages = document.getElementsByTagName('ul')\[0\],
                        message = document.createElement('li'),
                        content = document.createTextNode('Received: ' + event.data);
                    message.appendChild(content);
                    messages.appendChild(message);
                };
                document.body.appendChild(messages);
                window.setInterval(function() {
                    data = 'bye!'
                    ws.send(data);
                    var messages = document.getElementsByTagName('ul')\[0\],
                        message = document.createElement('li'),
                        content = document.createTextNode('Sent: ' + data);
                    message.appendChild(content);
                    messages.appendChild(message);
                }, 1000);
            </script>
        </body>
    </html>

    

	from sanic import Sanic
    from sanic.response import file
    

    app = Sanic(\_\_name\_\_)


    

    @app.route('/')
    async def index(request):
        return await file('websocket.html')


    

    @app.websocket('/feed')
    async def feed(request, ws):
        while True:
            data = 'hello!'
            print('Sending: ' + data)
            await ws.send(data)
            data = await ws.recv()
            print('Received: ' + data)


    

    if \_\_name\_\_ == '\_\_main\_\_':
        app.run(host="0.0.0.0", port=8000, debug=True)

vhost Suppport¶

	from sanic import response
    from sanic import Sanic
    from sanic.blueprints import Blueprint
    

    \# Usage
    \# curl -H "Host: example.com" localhost:8000
    \# curl -H "Host: sub.example.com" localhost:8000
    \# curl -H "Host: bp.example.com" localhost:8000/question
    \# curl -H "Host: bp.example.com" localhost:8000/answer
    
    app = Sanic()
    bp = Blueprint("bp", host="bp.example.com")


    

    @app.route('/', host=\["example.com",
                          "somethingelse.com",
                          "therestofyourdomains.com"\])
    async def hello(request):
        return response.text("Some defaults")


    

    @app.route('/', host="sub.example.com")
    async def hello(request):
        return response.text("42")


    

    @bp.route("/question")
    async def hello(request):
        return response.text("What is the meaning of life?")


    

    @bp.route("/answer")
    async def hello(request):
        return response.text("42")
    
    app.blueprint(bp)
    
    if \_\_name\_\_ == '\_\_main\_\_':
        app.run(host="0.0.0.0", port=8000)

Unit Testing With Parallel Test Run Support¶

The following example shows you how to get up and running with unit testing sanic application with parallel test execution support provided by the pytest-xdist plugin.

“”"pytest-xdist example for sanic server

    Install testing tools:
    
        $ pip install pytest pytest-xdist
    
    Run with xdist params:
    
        $ pytest examples/pytest_xdist.py -n 8  # 8 workers
    """
    import re
    from sanic import Sanic
    from sanic.response import text
    from sanic.testing import PORT as PORT_BASE, SanicTestClient
    import pytest


    

    @pytest.fixture(scope="session")
    def test_port(worker_id):
        m = re.search(r'\[0-9\]+', worker_id)
        if m:
            num_id = m.group(0)
        else:
            num_id = 0
        port = PORT_BASE + int(num_id)
        return port


    

    @pytest.fixture(scope="session")
    def app():
        app = Sanic()
    
        @app.route('/')
        async def index(request):
            return text('OK')
    
        return app


    

    @pytest.fixture(scope="session")
    def client(app, test_port):
        return SanicTestClient(app, test_port)


    

    @pytest.mark.parametrize('run_id', range(100))
    def test_index(client, run_id):
        request, response = client.\_sanic\_endpoint_test('get', '/')
        assert response.status == 200
        assert response.text == 'OK'

Amending Request Object¶

The request object in Sanic is a kind of dict object, this means that reqeust object can be manipulated as a regular dict object.

from sanic import Sanic
from sanic.response import text
from random import randint

    app = Sanic()


    

    @app.middleware('request')
    def append_request(request):
        \# Add new key with random value
        request\['num'\] = randint(0, 100)


    

    @app.get('/pop')
    def pop_handler(request):
        \# Pop key from request object
        num = request.pop('num')
        return text(num)


    

    @app.get('/key_exist')
    def key\_exist\_handler(request):
        \# Check the key is exist or not
        if 'num' in request:
            return text('num exist in request')
    
        return text('num does not exist in reqeust')


    

    app.run(host="0.0.0.0", port=8000, debug=True)

For more examples and useful samples please visit the Huge-Sanic’s GitHub Page

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值