diff --git a/guide/content/ja/built-with-sanic.md b/guide/content/ja/built-with-sanic.md new file mode 100644 index 0000000000..c2b3f33fd1 --- /dev/null +++ b/guide/content/ja/built-with-sanic.md @@ -0,0 +1,67 @@ +--- +title: 前方の全速度-私たちはどのように我々はサニックでこのサイトを構築しました +layout: メイン +--- + +.. attrs:: +:class: タイトル + +``` +全速力: +``` + +.. attrs:: +:class: subtitle + +``` +どのように私たちはサニックでこのサイトを構築しました +``` + +私たちが誇らしげに言うインターネットの小さなコーナーへようこそ、「はい、私たちはこれをSanicで構築しました!」と。 これは単なるウェブサイトではありません。私たちの遊び場、テストラボ、戦場、そして自宅です。 + +![](/assets/images/built-with-sanic.png) + +### 物語: "我々は自分のシャンパンを飲む" + +私たちはSanicをあまりにも信じているので、私たち自身のウェブサイトを運営する究極のテストにしようと決めました。 食中毒のリスクが少ないだけで、自分のレストランでシェフが食事をするようなものです。 + +なぜでしょう? ウェブサイトやウェブアプリケーションを構築することは難しいので。 無数の可動部品、多くの挑戦、そして常に存在するスピードと信頼性の必要性があります。 私たちはあなたが_できる_多くの方法の一つをお見せしたいと思います。 + +このハイステークスのデジタルキッチンでは、Sanicは私たちの秘密の食材です。 Sanicに自社のウェブサイトを導入することで、私たちはその機能を紹介するだけではなく、現実世界でストレステストを行っています。 これは、Sanicが紙に良いだけではないことを証明する私たちの歩くチャンスです。それは堅牢です。 最小のブログから最も忙しいeコマースサイトまであらゆるものを扱える高性能なフレームワークです + +だから、ここで私たちは、Sanicが私たちのサイトを運営することができれば、それもあなたの力になるという知識に自信を持って、私たち自身のシャンパンをすすります。 思考の速さでコーディングに乾杯! 🥂 + +### セットアップ:デジタルオーシャン、アホイ! + +私たちは高性能なクラウドセーリングが大好きなので、Digital OceanのApp Platformでサイトを立ち上げました。 フェラーリをクラウドに搭載していると考えてみてください。高速で洗練されていますが、扱いやすいです。 + +なぜシンプルになるのでしょうか? リーンなチームで、DevOpsの指導者がいない私たちには、簡単で簡単なソリューションが必要でした。 Digital Oceanは、サービスとしてのスムーズなセーリングプラットフォーム(PaaS)を提供してくれます。 簡単なセットアップ、自動展開、そして健全に眠れるような信頼性など、当社のニーズに最適です。 + +私たちの選択は私たちの精神を反映しています: あなたの強みに焦点を当て、プラットフォームは重い持ち上げを行いましょう. 私たちにとって、それはシンプルでありながら強力な展開ソリューションによってサポートされているSanicで素晴らしいWebエクスペリエンスを作成することを意味します。 ⛵ + +### コード:GitHubの場所 + +私たちのコードはすべてオープンで、GitHubの公開審査の栄光を浴びています。 なぜ魔法を隠すのですか? 詳細は format@@0(https://github.com/sanic-org/sanic/tree/main/guide) をご覧ください。 先に進み、覗き見を取る、フォーク、それで遊ぶ、それを壊す(そして親切にそれを修正する)。 + +オープンソースは単なる流行語ではありません私たちの精神です 私たち自身よりも大きなものを一緒に作ることです 私たちのコードは、共同でのイノベーション、開発のための遊び場、そして実際のSanicの実例を証明しています。 + +コードのすべての行、すべてのコミットは、Sanicとの私たちの旅を反映しており、そのスピードとスケーラビリティをどのように活用しているかを示しています。 バグを修正したり、機能を提案したり、ドキュメントを強化したりしても、あなたの貢献はこのプロジェクトを前進させるものです。 + +では、ダイブイン、あなたの天才に貢献し、SanicとWeb開発の未来を形作っていきましょう。 一緒にコーディングするだけでなく、コミュニティ主導のパワーハウスを作っています。 🚀 + +### 招待状:書き込み、コード、ブレイク、修正! + +- **ドキュメンタリー作家**: 複雑なものを簡単に作る愛? 私たちのドキュメントはあなたのキャンバスです。 言葉で絵を描いてみよう! 🎨 + +- **コードニンジャ**:バグを見つけますか? Squash 'em. アイデアはありますか? Code 'em. プルリクエストを雨にしましょう! 🥷 + +- **バグハンター**: バグを見つけたら、じっと見つめるだけではありません。 お知らせください。 私たちは良い虫狩りが大好きです。 🐛 + +### ボトムライン + +私たちはSanicとこのサイトを構築し、それが何ができるかを示しました。 速くて楽しくて私たちが使うものです だから、物事が迅速にロードされる場合は、背面に私たちを軽く叩いてください。 そうでなければ、ええと... 宇宙線を責めるのか? + +サニックを作るだけでなく、良いことにご参加ください, しかし、「私はできません-それは-バターではありません」良い! + +乾杯、 +サニックチーム(時にはマントを着用) diff --git a/guide/content/ja/guide/advanced/class-based-views.md b/guide/content/ja/guide/advanced/class-based-views.md new file mode 100644 index 0000000000..869ade3599 --- /dev/null +++ b/guide/content/ja/guide/advanced/class-based-views.md @@ -0,0 +1,227 @@ +# クラスベースビュー + +## なぜ使うのか? + +.. column:: + +``` +### 問題点 + +APIを設計する際の一般的なパターンは、同じエンドポイントに対してHTTPメソッドによって複数の機能を持つことです。 + +これらのオプションは両方とも機能しますが、優れた設計慣行ではなく、プロジェクトが成長するにつれて時間の経過とともに維持するのが難しい場合があります。 +``` + +.. column:: + +```` +```python +@app.get("/foo") +async def foo_get(request): + ... + +@app.post("/foo") +async def foo_post(request): + ... + +@app.put("/foo") +async def foo_put(request): + ... + +@app.route("/bar", methods=["GET", "POST", "PATCH"]) +async def bar(request): + if request.method == "GET": + ... + + elif request.method == "POST": + ... + + elif request.method == "PATCH": + ... +``` +```` + +.. column:: + +``` +### 解決策 + +クラスベースのビューは、要求に対する応答動作を完結に実装するクラスです。これらは、同じエンドポイントで異なるHTTPリクエストタイプの処理を区分する方法を提供します。 +``` + +.. column:: + +```` +```python +from sanic.views import HTTPMethodView + +class FooBar(HTTPMethodView): + async def get(self, request): + ... + + async def post(self, request): + ... + + async def put(self, request): + ... + +app.add_route(FooBar.as_view(), "/foobar") +``` +```` + +## ビューの定義 + +クラスベースのビューは、 :class:`sanic.views.HTTPMethodView` のサブクラスでなければなりません。 その後、対応するHTTPメソッドの名前でクラスメソッドを実装できます。 定義されたメソッドを持たない要求を受信すると、 `405: Method not allowed` 応答が生成されます。 + +.. column:: + +``` +エンドポイントにクラスベースのビューを登録するために、`app.add_route`メソッドが使用できます。最初の引数は、メソッド`as_view`が呼び出された定義済みクラスで、2番目の引数はURLエンドポイントである必要があります。 + +利用可能なメソッドは: + +- get +- post +- put +- patch +- delete +- head +- options +``` + +.. column:: + +```` +```python +from sanic.views import HTTPMethodView +from sanic.response import text + +class SimpleView(HTTPMethodView): + + def get(self, request): + return text("I am get method") + + # 非同期構文も利用可能 + async 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") + +app.add_route(SimpleView.as_view(), "/") +``` +```` + +## pathパラメータ + +.. column:: + +``` +[ルーティングセクション](/guide/basics/routing.md)で説明されているとおりにpathパラメータを使用できます。 +``` + +.. column:: + +```` +```python +class NameView(HTTPMethodView): + + def get(self, request, name): + return text("こんにちは、{}さん".format(name)) + +app.add_route(NameView.as_view(), "/") +``` +```` + +## デコレータ + +[デコレータのページ](/guide/best-practices/decorators.md)で説明したように、デコレータを使用してエンドポイントに機能を追加する必要があるかもしれません。 CBVによる二つの選択肢があります: + +1. ビュー内の _全ての_ HTTPメソッドに適用する +2. ビュー内のHTTPメソッドに個別に適用する + +これらがどのように動作するか見てみましょう: + +.. column:: + +``` +### すべてのメソッドに適用する + +クラスにデコレータを追加する場合は、`decorators`クラス変数を設定できます。 これらは、`as_view`が呼び出された時にクラスに適用されます。 +``` + +.. column:: + +```` +```python +class ViewWithDecorator(HTTPMethodView): + decorators = [some_decorator_here] + + def get(self, request, name): + return text("やあ、僕はデコレータを持ってるよ") + + def post(self, request, name): + return text("やあ、僕もデコレータを持ってるよ") + +app.add_route(ViewWithDecorator.as_view(), "/url") +``` +```` + +.. column:: + +``` +### 個々のメソッドに適用する + +しかし、すべてのメソッドではなく、いくつかのメソッドにデコレータをつけたい場合は、ここに示すように記述できます。 +``` + +.. column:: + +```` +```python +class ViewWithSomeDecorator(HTTPMethodView): + + @staticmethod + @some_decorator_here + def get(request, name): + return text("やあ、僕はデコレータを持ってるよ") + + def post(self, request, name): + return text("やあ、僕はデコレータを持っていないよ") + + @some_decorator_here + def patch(self, request, name): + return text("やあ、僕もデコレータを持っているよ") +``` +```` + +## URLの生成 + +.. column:: + +``` +これは、クラス名がエンドポイントの一部であることを除いて、[他のURLの生成](/guide/basics/routing.md#generating-a-url)と同じように機能します。 +``` + +.. column:: + +```` +```python +@app.route("/") +def index(request): + url = app.url_for("SpecialClassView") + return redirect(url) + +class SpecialClassView(HTTPMethodView): + def get(self, request): + return text("Hello from the Special Class View!") + +app.add_route(SpecialClassView.as_view(), "/special_class_view") +``` +```` diff --git a/guide/content/ja/guide/advanced/commands.md b/guide/content/ja/guide/advanced/commands.md new file mode 100644 index 0000000000..30bd448117 --- /dev/null +++ b/guide/content/ja/guide/advanced/commands.md @@ -0,0 +1,73 @@ +# カスタムCLIコマンド + +.. new:: v24.12 の新機能 + +``` +This feature was added in version 24.12 +``` + +Sanic は Sanic サーバを実行するための [CLI](../running/running.html#running-via-command) を搭載しています。 場合によっては、独自のコマンドを実行するためにCLIを強化する必要があるかもしれません。 コマンドは以下の基本パターンを使用して呼び出されます。 + +```sh +sanic path.to:app exec [--arg=value] +``` + +.. 列:: + +``` +To enable this, you can use your `Sanic` app instance to wrap functions that can be callable from the CLI using the `@app.command` decorator. +``` + +.. 列:: + +```` +```python +@app.command +async def hello(name="world"): + print(f"Hello, {name}.") +``` +```` + +.. 列:: + +``` +Now, you can easily invoke this command using the `exec` action. +``` + +.. 列:: + +```` +```sh +sanic path.to:app exec hello --name=Adam +``` +```` + +コマンドハンドラは同期または非同期のどちらでも構いません。 ハンドラは、CLI から渡される任意の数のキーワード引数を受け入れることができます。 + +.. 列:: + +``` +By default, the name of the function will be the command name. You can override this by passing the `name` argument to the decorator. +``` + +.. 列:: + +```` +```python +@app.command(name="greet") +async def hello(name="world"): + print(f"Hello, {name}.") +``` + +```sh +sanic path.to:app exec greet --name=Adam +``` +```` + +.. 警告:: + +``` +This feature is still in **BETA** and may change in future versions. There is no type coercion or validation on the arguments passed in from the CLI, and the CLI will ignore any return values from the command handler. Future enhancements and changes are likely. +``` + +_V24.12_に追加しました diff --git a/guide/content/ja/guide/advanced/proxy-headers.md b/guide/content/ja/guide/advanced/proxy-headers.md new file mode 100644 index 0000000000..347557787e --- /dev/null +++ b/guide/content/ja/guide/advanced/proxy-headers.md @@ -0,0 +1,574 @@ +# プロキシ設定 + +リバースプロキシサーバー(nginxなど)を使用する場合、`request.ip`の値にはプロキシのIP(通常は`127.0.0.1`)が含まれます。 ほとんどの場合、これはあなたが望むもの**ではありません**。 + +Sanicは、`request.remote_addr`として利用可能な本当のクライアントIPを決定するためにプロキシヘッダーを使用するように構成できます。 完全な外部URLは、_使用できれば_ヘッダーフィールドからも構築されます。 + +.. tip:: ヒント + +``` +適切な予防措置がなければ、悪意のあるクライアントはプロキシヘッダーを使用して独自のIPを偽装することができます。このような問題を回避するために、Sanicは明示的に有効になっていない限り、プロキシヘッダーを使用しません。 +``` + +.. column:: + +``` +リバースプロキシを使うサービスは、次の[構成値](/guide/deployment/configuration.md)の1つ以上を設定する必要があります。 + +- `FORWARDED_SECRET` +- `REAL_IP_HEADER` +- `PROXIES_COUNT` +``` + +.. column:: + +```` +```python +app.config.FORWARDED_SECRET = "super-duper-secret" +app.config.REAL_IP_HEADER = "CF-Connecting-IP" +app.config.PROXIES_COUNT = 2 +``` +```` + +## Forwarded ヘッダー + +`Forwarded`ヘッダーを使用するには、信頼できるプロキシサーバーに設定されている値を`app.config.FORWARDED_SECRET`に設定する必要があります。 このシークレットは、特定のプロキシサーバーを安全に識別するために使用されます。 + +Sanicはシークレットキーのない要素を無視し、シークレットが設定されていない場合はヘッダーを解析することもありません。 + +信頼された転送要素が見つかると、他のすべてのプロキシヘッダは無視されます。クライアントに関する完全な情報をすでに運んでいるからです。 + +`Forwarded`ヘッダーの詳細については、関連する[MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded)および[Nginx](https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/)の記事を参照してください。(訳注: 両方とも英語です) + +## 従来のプロキシヘッダー + +### IP ヘッダー + +プロキシが既知のヘッダーにIPアドレスを転送するとき`REAL_IP_HEADER`の値を指定すると、Sanicにそれが何かを教えることができます。 + +### X-Forwarded-For + +このヘッダーには、通常、プロキシの各レイヤーを介したIPアドレスのチェーンが含まれています。 `PROXIES_COUNT`を設定すると、クライアントの実際のIPアドレスを取得する深さがSanicに指示されます。 この値は、チェーン内のIPアドレスの _期待される_ 数に等しいはずです。 + +### その他のXヘッダー + +クライアントIPがこれらのメソッドのいずれかで見つかった場合、Sanicは以下のURLパーツのヘッダを使用します: + +- x-forwarded-proto +- x-forwarded-host +- x-forwarded-port +- x-forwarded-path +- x-scheme + +## 例 + +以下の例では、すべてのリクエストはエンドポイントが次のようになると仮定します。 + +```python +@app.route("/fwd") +async def forwarded(request): + return json( + { + "remote_addr": request.remote_addr, + "scheme": request.scheme, + "server_name": request.server_name, + "server_port": request.server_port, + "forwarded": request.forwarded, + } + ) +``` + +--- + +##### 例1 + +FORWARDED_SECRETが設定されていない場合、Xヘッダは尊重されます。 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=1.1.1.1, for=injected;host=", for="[::2]";proto=https;host=me.tld;path="/app/";secret=mySecret,for=broken;;secret=b0rked, for=127.0.0.3;scheme=http;port=1234' \ + -H "X-Real-IP: 127.0.0.2" \ + -H "X-Forwarded-For: 127.0.1.1" \ + -H "X-Scheme: ws" \ + -H "Host: local.site" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +``` +```` + +.. column:: + +```` +```bash +# curlの応答 +{ + "remote_addr": "127.0.0.2", + "scheme": "ws", + "server_name": "local.site", + "server_port": 80, + "forwarded": { + "for": "127.0.0.2", + "proto": "ws" + } +} +``` +```` + +--- + +##### 例2 + +FORWARDED_SECRETが設定された場合 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=1.1.1.1, for=injected;host=", for="[::2]";proto=https;host=me.tld;path="/app/";secret=mySecret,for=broken;;secret=b0rked, for=127.0.0.3;scheme=http;port=1234' \ + -H "X-Real-IP: 127.0.0.2" \ + -H "X-Forwarded-For: 127.0.1.1" \ + -H "X-Scheme: ws" \ + -H "Host: local.site" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```curlの応答 +{ + "remote_addr": "[::2]", + "scheme": "https", + "server_name": "me.tld", + "server_port": 443, + "forwarded": { + "for": "[::2]", + "proto": "https", + "host": "me.tld", + "path": "/app/", + "secret": "mySecret" + } +``` +```` + +--- + +##### 例3 + +空の転送ヘッダー - > Xヘッダーを使用する + +```sh +curl localhost:8000/fwd \ + -H "X-Real-IP: 127.0.0.2" \ + -H "X-Forwarded-For: 127.0.1.1" \ + -H "X-Scheme: ws" \ + -H "Host: local.site" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curlの応答 +{ + "remote_addr": "127.0.0.2", + "scheme": "ws", + "server_name": "local.site", + "server_port": 80, + "forwarded": { + "for": "127.0.0.2", + "proto": "ws" + } +} +``` +```` + +--- + +##### 例4 + +ヘッダーは存在するのに何も一致しない場合。 + +```sh +curl localhost:8000/fwd \ + -H "Forwarded: nomatch" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curlの応答 +{ + "remote_addr": "", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": {} +} + +``` +```` + +--- + +##### 例5 + +Forwarded ヘッダーは存在するが、一致するシークレットはない場合 -> Xヘッダを使用 + +```sh +curl localhost:8000/fwd \ + -H "Forwarded: for=1.1.1.1;secret=x, for=127.0.0.1" \ + -H "X-Real-IP: 127.0.0.2" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curlの応答 +{ + "remote_addr": "127.0.0.2", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "for": "127.0.0.2" + } +} +``` +```` + +--- + +##### 例6 + +フォーマットが異なり、ヘッダーの両端がヒットする場合 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: Secret="mySecret";For=127.0.0.4;Port=1234' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curlの応答 +{ + "remote_addr": "127.0.0.4", + "scheme": "http", + "server_name": "localhost", + "server_port": 1234, + "forwarded": { + "secret": "mySecret", + "for": "127.0.0.4", + "port": 1234 + } +} +``` +```` + +--- + +##### 例7 + +エスケープをテストする場合(quoted-pairsを実装しているのを見かけたら修正してあげてください)。 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=test;quoted="\,x=x;y=\";secret=mySecret' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curlの応答 +{ + "remote_addr": "test", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "for": "test", + "quoted": "\\,x=x;y=\\", + "secret": "mySecret" + } +} +``` +```` + +--- + +##### 例8 + +不正なフィールドによってシークレットが隔離される例 #1 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=test;secret=mySecret;b0rked;proto=wss;' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curlの応答 +{ + "remote_addr": "test", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "for": "test", + "secret": "mySecret" + } +} +``` +```` + +--- + +##### 例9 + +不正なフィールドによってシークレットが隔離される例 #2 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=test;b0rked;secret=mySecret;proto=wss' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curlの応答 +{ + "remote_addr": "", + "scheme": "wss", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "secret": "mySecret", + "proto": "wss" + } +} +``` +```` + +--- + +##### 例10 + +予期しない終了は、既存の許容可能な値を失うことはありません + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: b0rked;secret=mySecret;proto=wss' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curlの応答 +{ + "remote_addr": "", + "scheme": "wss", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "secret": "mySecret", + "proto": "wss" + } +} +``` +```` + +--- + +##### 例11 + +フィールドの正規化 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: PROTO=WSS;BY="CAFE::8000";FOR=unknown;PORT=X;HOST="A:2";PATH="/With%20Spaces%22Quoted%22/sanicApp?key=val";SECRET=mySecret' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curlの応答 +{ + "remote_addr": "", + "scheme": "wss", + "server_name": "a", + "server_port": 2, + "forwarded": { + "proto": "wss", + "by": "[cafe::8000]", + "host": "a:2", + "path": "/With Spaces\"Quoted\"/sanicApp?key=val", + "secret": "mySecret" + } +} +``` +```` + +--- + +##### 例12 + +「by」フィールドを秘密として使用する + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=1.2.3.4; by=_proxySecret' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "_proxySecret" +``` +```` + +.. column:: + +```` +```bash +# curlの応答 +{ + "remote_addr": "1.2.3.4", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "for": "1.2.3.4", + "by": "_proxySecret" + } +} + +``` +```` diff --git a/guide/content/ja/guide/advanced/signals.md b/guide/content/ja/guide/advanced/signals.md new file mode 100644 index 0000000000..16a24871cf --- /dev/null +++ b/guide/content/ja/guide/advanced/signals.md @@ -0,0 +1,400 @@ +# シグナル + +シグナルは、アプリケーションのある部分が別の部分に何かが起こったことを伝える方法を提供します。 + +```python +@app.signal("user.registration.created") +async def send_registration_email(**context): + await send_email(context["email"], template="registration") + +@app.post("/register") +async def handle_registration(request): + await do_registration(request) + await request.app.dispatch( + "user.registration.created", + context={"email": request.json.email} + }) +``` + +## シグナルを追加する + +.. column:: + +``` +シグナルを追加するためのAPIは、ルートの追加と非常によく似ています。 +``` + +.. column:: + +```` +```python +async def my_signal_handler(): + print("何かが起こった") + +app.add_signal(my_signal_handler, "something.happed.ohmy") +``` +```` + +.. 列:: + +``` +しかし、おそらくもう少し便利な方法は、組み込みのデコレータを使用することです。 +``` + +.. 列:: + +```` +```python +@app.signal("something.happened.ohmy") +async def my_signal_handler(): + print("something") +``` +```` + +.. 列:: + +``` +シグナルに条件(conditions)が必要な場合は、ハンドラを追加する際に必ず追加してください。 +``` + +.. 列:: + +```` +```python +async def my_signal_handler1(): + print("何かが起こった") + +app.add_signal( + my_signal_handler, + "something.happened.ohmy1", + conditions={"some_condition": "value"} +) + +@app.signal("something.happened.ohmy2", conditions={"some_condition": "value"}) +async def my_signal_handler2(): + print("何かが起こった") +``` +```` + +.. 列:: + +``` +シグナルはblueprintsで宣言することもできます。 +``` + +.. 列:: + +```` +```python +bp = Blueprint("foo") + +@bp.signal("something.happened.ohmy") +async def my_signal_handler(): + print("何かが起こった") +``` +```` + +## ビルトインシグナル + +新しいシグナルを作成することに加えて、Sanic自体からディスパッチされる組み込みシグナルがいくつかあります。 これらのシグナルは、開発者に要求とサーバーのライフサイクルに機能を追加する機会を増やすために存在します。 + +_v21.9で追加_ + +.. column:: + +``` +他のシグナルと同じように、アプリケーションまたはブループリントインスタンスにアタッチできます。 +``` + +.. column:: + +```` +```python +@app.signal("http.lifycle.complete") +async def my_signal_handler(conn_info): + print("Connection has been closed") +``` +```` + +これらのシグナルは、ハンドラが取る引数、およびアタッチする条件(存在する場合)とともに、利用可能なシグナルです。 + +| イベント名 | 引数 | 条件 | +| ------------------------- | ------------------------------- | ---------------------------------------------------------- | +| `http.routing.before` | request | | +| `http.routing.after` | request, route, kwargs, handler | | +| `http.handler.before` | request | | +| `http.handler.after` | request | | +| `http.lifycle.begin` | conn_info | | +| `http.lifycle.read_head` | head | | +| `http.lifycle.request` | request | | +| `http.lifycle.handle` | request | | +| `http.lifycle.read_body` | body | | +| `http.lifycle.exception` | request, exception | | +| `http.lifycle.response` | request, response | | +| `http.lifycle.send` | data | | +| `http.lifycle.complete` | conn_info | | +| `http.middleware.before` | request, response | `{"attach_to": "request"}` または `{"attach_to": "response"}` | +| `http.middleware.after` | request, response | `{"attach_to": "request"}` または `{"attach_to": "response"}` | +| `server.exception.report` | app, exception | | +| `server.init.before` | app, loop | | +| `server.init.after` | app, loop | | +| `server.shutdown.before` | app, loop | | +| `server.shutdown.after` | app, loop | | + +バージョン22.9で`http.handler.before`と`http.handler.after`が追加されました。 + +バージョン23.6で`server.exception.report`が追加されました。 + +.. column:: + +``` +ビルトインシグナルを使いやすくするために、許可されたビルトインをすべて含む `Enum` オブジェクトが用意されています。 最近の IDE では、イベント名の完全なリストを文字列として覚えておく必要がないので、これは便利です。 + +*v21.12で追加* +``` + +.. column:: + +```` +```python +from sanic.signal import Event + +@app.signal(Event.HTTP_LIFECYCLE_COMPLETE) +async def my_signal_handler(conn_info): + print("Connection has been closed") +``` +```` + +## イベント + +.. column:: + +``` +シグナルは _event_ に基づいています。イベントは以下のパターンの単なる文字列です。 +``` + +.. column:: + +```` +``` +namespace.reference.action +``` +```` + +.. tip:: イベントには3つの部分が必要です。 何を使っていいかわからない場合は、次のパターンを試してみてください。 + +``` +- `my_app.something.happened` +- `sanic.notice.hello` +``` + +### イベントパラメータ + +.. column:: + +``` +イベントは「動的」であり、[pathパラメータ](../basics/routing.md#path-parameters)と同じ構文を使用して宣言できます。これにより、任意の値に基づいてマッチングできます。 +``` + +.. 列:: + +```` +```python +@app.signal("foo.bar.") +async def signal_handler(thing): + print(f"[signal_handler] {thing=}") + +@app.get("/") +async def trigger(request): + await app.dispatch("foo.bar.baz") + return response.text("完了。") +``` +```` + +利用可能な型定義に関する詳細は[pathパラメータ](../basics/routing.md#path-parameters)を参照してください。 + +.. info:: イベントの3番目の部分(アクション)のみが動的です。 + +``` +- `foo.bar.` 🆗 +- `foo..baz` ❌ +``` + +### 待つ + +.. column:: + +``` +アプリケーションは、シグナルハンドラを実行するだけでなく、イベントがトリガーされるのを待つこともできます。 +``` + +.. column:: + +```` +```python +await app.event("foo.bar.baz") +``` +```` + +.. column:: + +``` +**重要**: 待つことはブロッキング機能です。したがって、これを[バックグラウンドタスク](../basics/tasks.md)で実行する必要があります。 +``` + +.. column:: + +```` +```python +async def wait_for_event(app): + while True: + print("> 待機中") + await app.event("foo.bar.baz") + print("> イベント発見\n") + +@app.after_server_start +async def after_server_start(app, loop): + app.add_task(wait_for_event(app)) +``` +```` + +.. column:: + +``` +イベントが動的パスで定義されている場合は、`*`を使用して任意のアクションをキャッチできます。 +``` + +.. column:: + +```` +```python +@app.signal("foo.bar.") + +... + +await app.event("foo.bar.*") +``` +```` + +## ディスパッチ + +_将来的には、Sanicは開発者がライフサイクルイベントに参加するのを支援するために、いくつかのイベントを自動的にディスパッチするようになります。_ + +.. column:: + +``` +イベントをディスパッチすると、2つのことを行います。 + +1. イベントで定義されたシグナルハンドラを実行し、 +2. イベントが完了するまで「待っている」ことをすべて解決します。 +``` + +.. column:: + +```` +```python +@app.signal("foo.bar.") +async def foo_bar(thing): + print(f"{thing=}") + +await app.dispatch("foo.bar.baz") +``` +``` +thing=baz +``` +```` + +### コンテキスト + +.. column:: + +``` +シグナルハンドラに追加情報を渡す必要がある場合があります。 上記の最初の例では、ユーザーのメールアドレスを持つようにメール登録プロセスを望んでいました。 +``` + +.. column:: + +```` +```python +@app.signal("user.registration.created") +async def send_registration_email(**context): + print(context) + +await app.dispatch( + "user.registration.created", + context={"hello": "world"} +) +``` +``` +{'hello': 'world'} +``` +```` + +.. tip:: 参考 + +``` +シグナルはバックグラウンドタスクでディスパッチされます。 +``` + +### Blueprints + +Blueprintシグナルのディスパッチは、[ミドルウェア](../basics/middleware.md)と同様に機能します。 appレベルから行われるシグナルは、blueprintにも伝播します。 ただし、blueprintでディスパッチすると、そのblueprintで定義されているシグナルのみが実行されます。 + +.. column:: + +``` +おそらく、例は説明しやすいでしょう: +``` + +.. column:: + +```` +```python +bp = Blueprint("bp") + +app_counter = 0 +bp_counter = 0 + +@app.signal("foo.bar.baz") +def app_signal(): + nonlocal app_counter + app_counter += 1 + +@bp.signal("foo.bar.baz") +def bp_signal(): + nonlocal bp_counter + bp_counter += 1 +``` +```` + +.. column:: + +``` +`app.dispatch("foo.bar.baz")`を実行すると、両方のシグナルが実行されます。 +``` + +.. column:: + +```` +```python +await app.dispatch("foo.bar.baz") +assert app_counter == 1 +assertt bp_counter == 1 +``` +```` + +.. column:: + +``` +`bp.dispatch("foo.bar.baz")`を実行すると、Blueprintシグナルのみが実行されます。 +``` + +.. column:: + +```` +```python +await bp.dispatch("foo.bar.baz") +assertt app_counter == 1 +assertt bp_counter == 2 +``` +```` diff --git a/guide/content/ja/guide/advanced/streaming.md b/guide/content/ja/guide/advanced/streaming.md new file mode 100644 index 0000000000..6382f6f9c2 --- /dev/null +++ b/guide/content/ja/guide/advanced/streaming.md @@ -0,0 +1,169 @@ +# ストリーミング + +## ストリーミングのリクエスト + +Sanicでは、クライアントから送信されたデータをストリーミングして、バイトが到着するとデータの処理を開始することができます。 + +.. column:: + +``` +エンドポイントで有効にすると、`await request.stream.read()`を使ってリクエストボディをストリーミングできます。 + +このメソッドは、ボディが完了すると`None`を返します。 +``` + +.. 列:: + +```` +```python +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") + return text(result) +``` +```` + +.. column:: + +``` +また、デコレータのキーワード引数で有効にすることも... +``` + +.. column:: + +```` +```python +@app.post("/stream", stream=True) +async def handler(request): + ... + body = await request.stream.read() + ... +``` +```` + +.. column:: + +``` +...`add_route()`メソッドを使うこともできます。 +``` + +.. column:: + +```` +```python +bp.add_route( + bp_handler, + "/bp_stream", + methods=["POST"], + stream=True, +) +``` +```` + +.. tip:: 参考 + +``` +post、put、patchデコレータのみが stream 引数を持っています。 +``` + +## Response ストリーミング + +.. column:: + +``` +Sanicでは、クライアントにコンテンツをストリーミングできます。 +``` + +.. column:: + +```` +```python +@app.route("/") +async def test(request): + response = await request.respond(content_type="text/csv") + await response.send("foo,") + await response.send("bar") + + # 以下の関数を呼び出して、ストリームを明示的に終了することもできます: + await response.eof() +``` +```` + +これは、データベースのような外部サービスで発生するクライアントにコンテンツをストリーミングしたい場合に便利です。 たとえば、`asyncpg` が提供する非同期カーソルを使用して、データベースレコードをクライアントにストリーミングできます。 + +```python +@app.route("/") +async def index(request): + response = await request.respond() + conn = await asyncpg.connect(database='test') + async with conn.transaction(): + async for record in conn.cursor('SELECT generate_series(0, 10)'): + await response.send(record[0]) +``` + +`await response.eof()` を呼び出すことで、ストリームを明示的に終了させることができます。 これは `await response.send("", True)` を置き換える便利なメソッドです。 ハンドラがクライアントに送り返すものが何も残っていないと判断した _後に_ **1度だけ** 呼び出されるべきです。 Sanic サーバーで使用するのは_任意_ですが、SanicをASGIモードで動作させている場合は、**必ず**明示的にストリームを終了させなければなりません。 + +_v21.6で`eof`の呼び出しが任意になりました_ + +## ファイルストリーミング + +.. column:: + +``` +Sanicは、大きなファイルを送信する場合に便利な `sanic.response.file_stream` 関数を提供します。`StreamingHTTPResponse`オブジェクトを返し、デフォルトではチャンク転送エンコーディングを使用します。このため、Sanicはレスポンスに`Content-Length` HTTPヘッダーを追加しません。 + +典型的な使用例は、ビデオファイルをストリーミングしている場合です。 +``` + +.. column:: + +```` +```python +@app.route("/mp4") +async def handler_file_stream(request): + return await response.file_stream( + "/path/to/sample.mp4", + chunk_size=1024, + mime_type="application/metalink4+xml", + headers={ + "Content-Disposition": 'Attachment; filename="nicer_name.meta4"', + "Content-Type": "application/metalink4+xml", + }, + ) +``` +```` + +.. column:: + +``` +`Content-Length`ヘッダーを使用したい場合は、チャンク転送エンコーディングを無効にし、`Content-Length`ヘッダーを手動で追加するだけです。 +``` + +.. column:: + +```` +```python +from aiofiles import os as async_os +from sanic.response import file_stream + +@app.route("/") +async def index(request): + file_path = "/srv/www/whatever.png" + + file_stat = await async_os.stat(file_path) + headers = {"Content-Length": str(file_stat.st_size)} + + return await file_stream( + file_path, + headers=headers, + ) +``` +```` diff --git a/guide/content/ja/guide/advanced/versioning.md b/guide/content/ja/guide/advanced/versioning.md new file mode 100644 index 0000000000..e630c6ede6 --- /dev/null +++ b/guide/content/ja/guide/advanced/versioning.md @@ -0,0 +1,185 @@ +# バージョン進行 + +API構築では、エンドポイントにバージョンを追加するのが標準的な方法です。 これにより、互換性のないエンドポイントを簡単に区別することができます。 + +バージョンを追加すると、`/v{version}`のURLプレフィックスがエンドポイントに追加されます。 + +バージョンは `int` 、`float` 、または `str` にすることができます。 許容値: + +- `1`, `2`, `3` +- `1.1`, `2.25`, `3.0` +- `"1"`、`"v1"`、`"v1.1"` + +## ルートごとのバージョン + +.. column:: + +``` +バージョン番号をルートに直接渡すことができます。 +``` + +.. column:: + +```` +```python +# /v1/text +@app.route("/text", version=1) +def handle_request(request): + return response. ext("Hello world! Version 1") + +# /v2/text +@app.route("/text", version=2) +def handle_request(request): + return response.text("Hello world! Version 2") +``` +```` + +## Blueprintごとのバージョン + +.. column:: + +``` +Blueprintにバージョン番号を渡すこともできます。 これは、そのBlueprint内のすべてのルートに適用されます。 +``` + +.. column:: + +```` +```python +bp = Blueprint("test", url_prefix="/foo", version=1) + +# /v1/foo/html +@bp.route("/html") +def handle_request(request): + return response.html(" "

Hello world!

") +``` +```` + +## Blueprintグループごとのバージョン + +.. column:: + +``` +バージョン化されたBlueprintの管理を簡素化するために、グループにバージョン番号を提供できます。Blueprintインスタンスを作成する際に指定された値で同じ情報を上書きしない場合、 その下方でグループ化されたすべてのBlueprintにも同じ情報が継承されます。 + +バージョン管理にblueprintグループを使用する場合、ルートの登録中に以下の順序でバージョンプレフィックスが適用されます。 + +1. ルートレベルの設定 +2. Blueprintレベルの設定 +3. Blueprintグループレベルの設定 + +もし、より小さな単位でのバージョン管理仕様が見つかれば、BlueprintやBlueprintグループの下で提供される一般的なバージョン管理仕様よりも、そちらを選ぶことになります。 +``` + +.. column:: + +```` +```python +from sanic.blueprints import Blueprint +from sanic.response import json + +bp1 = Blueprint( + name="blueprint-1", + url_prefix="/bp1", + version=1.25, +) +bp2 = Blueprint( + name="blueprint-2", + url_prefix="/bp2", +) + +group = Blueprint.group( + [bp1, bp2], + url_prefix="/bp-group", + version="v2", +) + +# GET /v1.25/bp-group/bp1/endpoint-1 +@bp1.get("/endpoint-1") +async def handle_endpoint_1_bp1(request): + return json({"Source": "blueprint-1/endpoint-1"}) + +# GET /v2/bp-group/bp2/endpoint-2 +@bp2.get("/endpoint-1") +async def handle_endpoint_1_bp2(request): + return json({"Source": "blueprint-2/endpoint-1"}) + +# GET /v1/bp-group/bp2/endpoint-2 +@bp2.get("/endpoint-2", version=1) +async def handle_endpoint_2_bp2(request): + return json({"Source": "blueprint-2/endpoint-2"}) +``` +```` + +## バージョンプレフィックス + +上で見たように、ルートに適用される`version`は、**常に**生成されたURIパスの最初のセグメントになります。 したがって、バージョンの前にパスセグメントを追加できるように、`version`引数が渡されるすべての場所で、`version_prefix`を渡すこともできます。 + +引数 `version_prefix` は以下の場所で定義できます: + +- `app.route` と `bp.route` デコレーター (そして、すべての便利なデコレータ) +- `Blueprint`のインスタンス化 +- `Blueprint.group` コンストラクタ +- `BlueprintGroup`のインスタンス化 +- `app.blueprint` の登録 + +複数の場所に定義がある場合、より具体的な定義はより一般的な定義を上書きします。 上のリストはそのヒエラルキーと対応しています。 + +`version_prefix` のデフォルト値は `/v` です。 + +.. column:: + +``` +バージョン管理されたルートを `/api` にマウントしたいという状況がよくあります。これは`version_prefix`で簡単に実現できます。 +``` + +.. column:: + +```` +```python +# /v1/my/path +app.route("/my/path", version=1, version_prefix="/api/v") +``` +```` + +.. column:: + +``` +おそらく、より説得力のある使用法は、すべての`/api`ルートを単一の`BlueprintGroup`にロードすることです。 +``` + +.. column:: + +```` +```python +# /v1/my/path +app = Sanic(__name__) +v2ip = Blueprint("v2ip", url_prefix="/ip", version=2) +api = Blueprint.group(v2ip, version_prefix="/api/version") + +# /api/version2/ip +@v2ip.get("/") +async def handler(request): + return text(request.ip) + +app.blueprint(api) +``` +```` + +そのため、ルートのURIは以下のようになります。 + +``` +version_prefix + version + url_prefix + URI 定義 +``` + +.. tip:: + +```` +`url_prefix` と同じように、 `version_prefix` 内でパスパラメータを定義することができます。 すべてのルートがハンドラにそのパラメータを注入することを覚えておいてください。 + +```python +version_prefix="//v" +``` +```` + +_V21.6_に追加されました diff --git a/guide/content/ja/guide/advanced/websockets.md b/guide/content/ja/guide/advanced/websockets.md new file mode 100644 index 0000000000..a86a8abd9c --- /dev/null +++ b/guide/content/ja/guide/advanced/websockets.md @@ -0,0 +1,91 @@ +# Websockets + +Sanic は [websockets](https://websockets.readthedocs.io/en/stable/) の上に簡単に使える抽象化を提供します。 + +## ルーティング + +.. 列:: + +``` +Websocket ハンドラは、通常のハンドラと同様にルータにフックアップできます。 +``` + +.. 列:: + +```` +```python +from sanic import Request, Websocket + +async def feed(request: Request, ws: Websocket): + pass + +app.add_websocket_route(feed, "/feed") +``` +```python +from sanic import Request, Websocket + +@app.websocket("/feed") +async def feed(request: Request, ws: Websocket): + pass +``` +```` + +## Handler + +.. 列:: + +``` +一般的に、ウェブソケットハンドラはループを開いたままにします。 + +そして、ハンドラに注入された 2 番目のオブジェクトの `send()` メソッドと `recv()` メソッドを使用します。 + +この例は、受信したメッセージをクライアントにエコーバックする単純なエンドポイントです。 +``` + +.. 列:: + +```` +```python +from sanic import Request, Websocket + +@app.websocket("/feed") +async def feed(request: Request, ws: Websocket): + while True: + data = "こんにちは!" + print("送信中: " + data) + await ws.send(data) + data = await ws.recv() + print("受信: " + data) +``` +```` + +.. column:: + +``` +forループ内の`Websocket`オブジェクトを繰り返すだけで、ループを簡素化できます。 + +*v22.9*に追加されました +``` + +.. column:: + +```` +```python +from sanic import Request, Websocket + +@app.websocket("/feed") +async def feed(request: Request, ws: Websocket): + async for msg in ws: + await ws.send(msg) +``` +```` + +## 設定 + +詳細は [configuration section](/guide/deployment/configuration.md) をご覧ください。 + +```python +app.config.WEBSOCKET_MAX_SIZE = 2 ** 20 +app.config.WEBSOCKET_PING_INTERVAL = 20 +app.config.WEBSOCKET_PING_TIMEOUT = 20 +``` diff --git a/guide/content/ja/guide/basics/README.md b/guide/content/ja/guide/basics/README.md new file mode 100644 index 0000000000..bb73f348aa --- /dev/null +++ b/guide/content/ja/guide/basics/README.md @@ -0,0 +1 @@ +# 基本 diff --git a/guide/content/ja/guide/basics/app.md b/guide/content/ja/guide/basics/app.md new file mode 100644 index 0000000000..a02d7ede41 --- /dev/null +++ b/guide/content/ja/guide/basics/app.md @@ -0,0 +1,634 @@ +--- +title: サニック・アプリケーション +--- + +# サニック・アプリケーション + +API ドキュメント: [sanic.app](/api/sanic.app) を参照してください。 + +## インスタンス + +.. 列:: + +``` +最も基本的なビルディングブロックは、 :class:`sanic.app.Sanic`インスタンスです。 これは必須ではありませんが、カスタムは `server.py` という名前のファイルでインスタンス化します。 +``` + +.. 列:: + +```` +```python +# /path/to/server.py + +from sanic import Sanic + +app = Sanic("MyHelloWorldApp") +``` +```` + +## アプリケーションのコンテキスト + +ほとんどのアプリケーションは、コードベースの異なる部分にわたってデータやオブジェクトを共有/再利用する必要があります。 Sanicはアプリケーションインスタンスに`ctx`オブジェクトを提供するのに役立ちます。 開発者がアプリケーションの寿命を通じて存在すべきあらゆるオブジェクトやデータを添付するための空き領域です。 + +.. 列:: + +``` +最も一般的なパターンは、アプリケーションにデータベース・インスタンスをアタッチすることです。 +``` + +.. 列:: + +```` +```python +app = Sanic("MyApp") +app.ctx.db = Database() +``` +```` + +.. 列:: + +``` +While the previous example will work and is illustrative, it is typically considered best practice to attach objects in one of the two application startup [listeners](./listeners). +``` + +.. 列:: + +```` +```python +app = Sanic("MyApp") + +@app.before_server_start +async def attach_db(app, loop): + app.ctx.db = Database() +``` +```` + +## アプリのレジストリ + +.. 列:: + +``` +Sanicインスタンスをインスタンス化すると、Sanicアプリレジストリから後で取得できます。 たとえば、Sanicインスタンスにアクセスする必要がある場合には、Sanicインスタンスにアクセスできない場合などに便利です。 +``` + +.. 列:: + +```` +```python +# ./path/to/server.py +from sanic import Sanic + +app = Sanic("my_awesome_server") + +# ./path/to/somewhere_else.py +from sanic import Sanic + +app = Sanic.get_app("my_awesome_server") +``` +```` + +.. 列:: + +``` +存在しないアプリで `Sanic.get_app("non-existing")` を呼び出すと、 :class:`sanic.exceptions が発生します。 anicException`はデフォルトです。代わりに、その名前を持つSanicの新しいインスタンスを強制的に返すことができます。 +``` + +.. 列:: + +```` +```python +app = Sanic.get_app( + "non-existing", + force_create=True, +) +``` +```` + +.. 列:: + +``` +Sanic インスタンスが登録されている場合は、引数のない`Sanic.get_app()`を呼び出すと、そのインスタンスが返されます。 +``` + +.. 列:: + +```` +```python +Sanic("My only app") + +app = Sanic.get_app() +``` +```` + +## 設定 + +.. 列:: + +``` +Sanicは`Sanic`インスタンスの`config`属性の設定を保持しています。設定は辞書のようにドット表記を使って**どちらか**変更できます。 +``` + +.. 列:: + +```` +```python +app = Sanic('myapp') + +app.config.DB_NAME = 'appdb' +app.config['DB_USER'] = 'appuser' + +db_settings = { + 'DB_HOST': 'localhost', + 'DB_NAME': 'appdb', + 'DB_USER': 'appuser' +} +app.config.update(db_settings) +``` +```` + +.. Note:: Heads Up + +```` +設定キー _should_ は大文字ですが、これは主に規則によって行われ、小文字はほとんどの場合動作します。 +```python +app.config.GOOD = "yay!" +app.config.bad = "boo" +``` +```` + +後ほど、format@@0(../running/configuration.md) があります。 + +## 出荷時のパターン + +これらのドキュメントの多くの例では、 :class:`sanic.appのインスタンス化が表示されます。 グローバルスコープ内の `server.py` というファイル内の anic` インスタンス(すなわち関数内ではない)。 これは非常に単純な "hello world" スタイルのアプリケーションでは一般的なパターンですが、代わりにファクトリパターンを使用することはしばしば有益です。 + +"factory" は、使用したいオブジェクトのインスタンスを返す関数です。 これにより、オブジェクトのインスタンス化を抽象化できますが、アプリケーションインスタンスを簡単に分離できるようになります。 + +.. 列:: + +``` +超単純なファクトリパターンは次のようになります: +``` + +.. 列:: + +```` +```python +# ./path/to/server.py +from sanic import Sanic +from .path.to.config import MyConfig +from .path.to.some.blueprint import bp + + +def create_app(config=MyConfig) -> Sanic: + app = Sanic("MyApp", config=config) + app.blueprint(bp) + return app +``` +```` + +.. 列:: + +``` +Sanic を後で実行すると、Sanic CLI がこのパターンを検出し、アプリケーションを実行するために使用できることがわかります。 +``` + +.. 列:: + +```` +```sh +sanic path.to.server:create_app +``` +```` + +## カスタマイズ + +Sanicアプリケーションインスタンスは、インスタンス化時にさまざまな方法でアプリケーションのニーズに合わせてカスタマイズできます。 + +詳細については、[API docs](/api/sanic.app) を参照してください。 + +### カスタム構成 + +.. 列:: + +``` +This simplest form of custom configuration would be to pass your own object directly into that Sanic application instance + +If you create a custom configuration object, it is *highly* recommended that you subclass the :class:`sanic.config.Config` option to inherit its behavior. You could use this option for adding properties, or your own set of custom logic. + +*Added in v21.6* +``` + +.. 列:: + +```` +```python +from sanic.config import Config + +class MyConfig(Config): + FOO = "bar" + +app = Sanic(..., config=MyConfig()) +``` +```` + +.. 列:: + +``` +この機能の有用な例として、 [supported]とは異なる形式で設定ファイルを使用したい場合があります。 /running/configuration.md#using-sanicupdateconfig). +``` + +.. 列:: + +```` +```python +from sanic import Sanic, text +from sanic.config import Config + +class TomlConfig(Config): + def __init__(self, *args, path: str, **kwargs): + super().__init__(*args, **kwargs) + + with open(path, "r") as f: + self.apply(toml.load(f)) + + def apply(self, config): + self.update(self._to_uppercase(config)) + + def _to_uppercase(self, obj: Dict[str, Any]) -> Dict[str, Any]: + retval: Dict[str, Any] = {} + for key, value in obj.items(): + upper_key = key.upper() + if isinstance(value, list): + retval[upper_key] = [ + self._to_uppercase(item) for item in value + ] + elif isinstance(value, dict): + retval[upper_key] = self._to_uppercase(value) + else: + retval[upper_key] = value + return retval + +toml_config = TomlConfig(path="/path/to/config.toml") +app = Sanic(toml_config.APP_NAME, config=toml_config) +``` +```` + +### カスタム コンテキスト + +.. 列:: + +``` +デフォルトでは、アプリケーションのコンテキストは[`SimpleNamespace()`](https://docs.python.org/3/library/types.html#types)です。 任意のプロパティを設定することができます。 ただし、任意のオブジェクトを渡すオプションもあります。 + +*v21.6*に追加されました +``` + +.. 列:: + +```` +```python +app = Sanic(..., ctx=1) +``` + +```python +app = Sanic(..., ctx={}) +``` + +```python +class MyContext: + ... + +app = Sanic(..., ctx=MyContext()) +``` +```` + +### カスタムリクエスト + +.. 列:: + +``` +It is sometimes helpful to have your own `Request` class, and tell Sanic to use that instead of the default. One example is if you wanted to modify the default `request.id` generator. + + + +.. note:: Important + + It is important to remember that you are passing the *class* not an instance of the class. +``` + +.. 列:: + +```` +```python +import time + +from sanic import Request, Sanic, text + +class NanoSecondRequest(Request): + @classmethod + def generate_id(*_): + return time.time_ns() + +app = Sanic(..., request_class=NanoSecondRequest) + +@app.get("/") +async def handler(request): + return text(str(request.id)) +``` +```` + +### カスタムエラーハンドラの設定 + +.. 列:: + +``` +詳細については format@@0(../best-practices/exceptions.md#custom-error-handling) を参照してください。 +``` + +.. 列:: + +```` +```python +from sanic.handlers import ErrorHandler + +class CustomErrorHandler(ErrorHandler): + def default(self, request, exception): + ''' handles errors that have no error handlers assigned ''' + # You custom error handling logic... + return super().default(request, exception) + +app = Sanic(..., error_handler=CustomErrorHandler()) +``` +```` + +### カスタムダンプ 関数 + +.. 列:: + +``` +JSONデータにオブジェクトをシリアライズするカスタム関数を提供することが必要な場合や望ましい場合もあります。 +``` + +.. 列:: + +```` +```python +import ujson + +dump = partial(ujson.dumps, escape_forward_slashes=False) +app = Sanic(__name__, dumps=dumps) +``` +```` + +.. 列:: + +``` +または、おそらく別のライブラリを使用するか、独自のライブラリを作成します。 +``` + +.. 列:: + +```` +```python +from orjson import dump + +app = Sanic("MyApp", dumps=dumps) +``` +```` + +### カスタム読み込み機能 + +.. 列:: + +``` +`dumps`と同様に、データをデシリアライズするためのカスタム関数を提供することもできます。 + +*v22.9*に追加されました +``` + +.. 列:: + +```` +```python +from orjson import loads + +app = Sanic("MyApp", loads=loads) +``` +```` + +### カスタムタイプされたアプリケーション + +デフォルトの Sanic アプリケーションインスタンスの正しい型アノテーションは v23.6 で始まります。 + +```python +sanic.app.Sanic[sanic.config.Config, types.SimpleNamespace] +``` + +2つの一般的な型を指します。 + +1. 最初は、構成オブジェクトのタイプです。 デフォルトでは、 :class:`sanic.config.Config` になっていますが、そのサブクラスにすることができます。 +2. 二つ目はアプリケーションコンテキストのタイプです。 デフォルトは [`SimpleNamespace()`](https://docs.python.org/3/library/types.html#types.SimpleNamespace) ですが、上記のように**任意のオブジェクト** にできます。 + +型がどのように変化するかの例を見てみましょう。 + +.. 列:: + +``` +:class:`sanic.config.Config` のカスタムサブクラスとカスタムコンテキストオブジェクトを渡す例を考えてみましょう。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic +from sanic.config import Config + +class CustomConfig(Config): + pass + +app = Sanic("test", config=CustomConfig()) +reveal_type(app) # N: Revealed type is "sanic.app.Sanic[main.CustomConfig, types.SimpleNamespace]" +``` +``` +sanic.app.Sanic[main.CustomConfig, types.SimpleNamespace] +``` +```` + +.. 列:: + +``` +同様に、カスタムコンテキストオブジェクトを渡す場合、型はそれを反映するように変更されます。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic + +class Foo: + pass + +app = Sanic("test", ctx=Foo()) +reveal_type(app) # N: Revealed type is "sanic.app.Sanic[sanic.config.Config, main.Foo]" +``` +``` +sanic.app.Sanic[sanic.config.Config, main.Foo] +``` +```` + +.. 列:: + +``` +もちろん、設定とコンテキストの両方をカスタムタイプに設定できます。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic +from sanic.config import Config + +class CustomConfig(Config): + pass + +class Foo: + pass + +app = Sanic("test", config=CustomConfig(), ctx=Foo()) +reveal_type(app) # N: Revealed type is "sanic.app.Sanic[main.CustomConfig, main.Foo]" +``` +``` +sanic.app.Sanic[main.CustomConfig, main.Foo] +``` +```` + +このパターンは、アプリケーションインスタンスにカスタム型エイリアスを作成し、リスナーやハンドラに注釈を付けるために使用する場合に特に便利です。 + +```python +# ./path/to/types.py +from sanic.app import Sanic +from sanic.config import Config +from myapp.context import MyContext +from typing import TypeAlias + +MyApp = TypeAlias("MyApp", Sanic[Config, MyContext]) +``` + +```python +# ./path/to/listeners.py +from myapp.types import MyApp + +def add_listeners(app: MyApp): + @app.before_server_start + async def before_server_start(app: MyApp): + # do something with your fully typed app instance + await app.ctx.db.connect() +``` + +```python +# ./path/to/server.py +from myapp.types import MyApp +from myapp.context import MyContext +from myapp.config import MyConfig +from myapp.listeners import add_listeners + +app = Sanic("myapp", config=MyConfig(), ctx=MyContext()) +add_listeners(app) +``` + +_V23.6_に追加されました + +### カスタムタイプのリクエスト + +Sanicでは、リクエストオブジェクトのタイプをカスタマイズすることもできます。 これは、リクエストオブジェクトにカスタムプロパティを追加する場合に便利です。 または型付けされたアプリケーションインスタンスのカスタムプロパティにアクセスできます。 + +サイニックリクエストインスタンスの正しいデフォルトタイプは次のとおりです: + +```python +sanic.request.Request[ + sanic.app.Sanic[sanic.config.Config, types.SimpleNamespace], + types.SimpleNamespace +] +``` + +2つの一般的な型を指します。 + +1. 最初はアプリケーションインスタンスのタイプです。 デフォルトは `sanic.app.Sanic[sanic.config.Config, types.SimpleNamespace]` ですが、そのサブクラスは任意です。 +2. 二つ目は、リクエストコンテキストのタイプです。 デフォルトは `types.SimpleNamespace` ですが、上記の [custom requests](#custom-requests) で表示されるように、 **任意のオブジェクト** にすることができます。 + +型がどのように変化するかの例を見てみましょう。 + +.. 列:: + +``` +カスタマイズされたアプリケーションインスタンスに型エイリアスがある上記の完全な例を展開します 同じタイプの注釈にアクセスできるようにカスタムリクエストタイプを作成することもできます + +もちろん、これを動作させるためにタイプエイリアスは必要ありません。 私たちは、表示されているコードの量を削減するためにそれらをここに示しているだけです。 +``` + +.. 列:: + +```` +```python +from sanic import Request +from myapp.types import MyApp +from types import SimpleNamespace + +def add_routes(app: MyApp): + @app.get("/") + async def handler(request: Request[MyApp, SimpleNamespace]): + # do something with your fully typed app instance + results = await request.app.ctx.db.query("SELECT * FROM foo") +``` +```` + +.. 列:: + +``` +カスタムコンテキストオブジェクトを生成するカスタムリクエストオブジェクトがあるかもしれません。 ここに示すように、注釈を入力してIDEでこれらのプロパティに適切にアクセスできます。 +``` + +.. 列:: + +```` +```python +from sanic import Request, Sanic +from sanic.config import Config + +class CustomConfig(Config): + pass + +class Foo: + pass + +class RequestContext: + foo: Foo + +class CustomRequest(Request[Sanic[CustomConfig, Foo], RequestContext]): + @staticmethod + def make_context() -> RequestContext: + ctx = RequestContext() + ctx.foo = Foo() + return ctx + +app = Sanic( + "test", config=CustomConfig(), ctx=Foo(), request_class=CustomRequest +) + +@app.get("/") +async def handler(request: CustomRequest): + # Full access to typed: + # - custom application configuration object + # - custom application context object + # - custom request context object + pass +``` +```` + +format@@0(./request#custom-request-context) セクションを参照してください。 + +_V23.6_に追加されました diff --git a/guide/content/ja/guide/basics/cookies.md b/guide/content/ja/guide/basics/cookies.md new file mode 100644 index 0000000000..f5a3bac2fe --- /dev/null +++ b/guide/content/ja/guide/basics/cookies.md @@ -0,0 +1,122 @@ +# Cookie + +## 読書中 + +.. 列:: + +``` +Cookie は `Request` オブジェクトの `cookies` 辞書からアクセスできます。 +``` + +.. 列:: + +```` +```python +@app.route("/cookie") +async def test(request): + test_cookie = request.cookies.get("test") + return text(f"Test cookie: {test_cookie}") +``` +```` + +.. tip:: FYI + +``` +💡 The `request.cookies` object is one of a few types that is a dictionary with each value being a `list`. This is because HTTP allows a single key to be reused to send multiple values. + +Most of the time you will want to use the `.get()` method to access the first element and not a `list`. If you do want a `list` of all items, you can use `.getlist()`. + +*Added in v23.3* +``` + +## 執筆中 + +.. 列:: + +``` +レスポンスを返すときは、`Response` オブジェクト: `response.cookies` にクッキーを設定できます。 このオブジェクトは `CookieJar` のインスタンスで、自動的にレスポンスヘッダーを書き込む特別な種類の辞書です。 +``` + +.. 列:: + +```` +```python +@app.route("/cookie") +async def test(request): + response = text("There's a cookie up in this response") + response.add_cookie( + "test", + "It worked!", + domain=".yummy-yummy-cookie.com", + httponly=True + ) + return response +``` +```` + +Response Cookieは辞書の値のように設定でき、次のパラメータを使用できます。 + +- `path: str` - このクッキーが適用される URL のサブセット。 デフォルトは `/` です。 +- `domain: str` - クッキーが有効なドメインを指定します。 明示的に指定されたドメインは常にドットで始まる必要があります。 +- `max_age: int` - クッキーが生き残るべき秒数。 +- `expires: datetime` - クッキーがクライアントのブラウザで期限切れになる時間。 通常、max-age を代わりに使用する方が良いです。 +- `secure: bool` - HTTPS でのみクッキーを送信するかどうかを指定します。 デフォルトは `True` です。 +- `httponly: bool` - クッキーをJavaScriptで読み取れないかどうかを指定します。 +- `samesite: str` - 利用可能な値: Lax, Strict, None。 デフォルトは `Lax` です。 +- `comment: str` - コメント (metadata) +- `host_prefix: bool` - Cookieに`__Host-`プレフィックスを追加するかどうか。 +- `secure_prefix: bool` - Cookieに`__Secure-`プレフィックスを追加するかどうか。 +- `partitioned: bool` - クッキーをパーティションとしてマークするかどうか。 + +これらの値の意味と使用状況をよりよく理解するためには、format@@0(https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookie)のformat@@1(https://developer.mozilla.org/docs/Web/HTTP/Cookie)を読むと便利かもしれません。 + +.. tip:: FYI + +``` +デフォルトでは、Sanicは`secure`フラグを`True`に設定し、HTTPS経由でのみクッキーが正常なデフォルトとして送信されるようにします。 これはローカル開発にとって影響を与えるべきではありません。なぜなら、HTTP を介した セキュアな Cookie は `localhost` に送られるべきだからです。 詳細については、[secure cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookie#restrict_access_to_cookies) の[secure cookies](https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie#Secure)をご覧ください。 +``` + +## 削除中 + +.. 列:: + +``` +クッキーは意味的または明示的に削除することができます。 +``` + +.. 列:: + +```` +```python +@app.route("/cookie") +async def test(request): + response = text("Time to eat some cookies muahaha") + + # This cookie will be set to expire in 0 seconds + response.delete_cookie("eat_me") + + # This cookie will self destruct in 5 seconds + response.add_cookie("fast_bake", "Be quick!", max_age=5) + + return response +``` + +*Don't forget to add `path` or `domain` if needed!* +```` + +## 食べる + +.. 列:: + +``` +Sanicはクッキーが好き +``` + +.. 列:: + +``` +.. attrs: + :class: is-size-1 has-text-centered + +🍪 +``` diff --git a/guide/content/ja/guide/basics/handlers.md b/guide/content/ja/guide/basics/handlers.md new file mode 100644 index 0000000000..3785e3ff60 --- /dev/null +++ b/guide/content/ja/guide/basics/handlers.md @@ -0,0 +1,217 @@ +# Handlers + +次に重要なビルディングブロックは _handlers_ です。 これらは"views"とも呼ばれます。 + +In Sanic, a handler is any callable that takes at least a :class:`sanic.request.Request` instance as an argument, and returns either an :class:`sanic.response.HTTPResponse` instance, or a coroutine that does the same. + +.. 列:: + +``` +Huh? 😕 + +It is a **function**; either synchronous or asynchronous. + +The job of the handler is to respond to an endpoint and do something. This is where the majority of your business logic will go. +``` + +.. 列:: + +```` +```python +def i_am_a_handler(request): + return HTTPResponse() + +async def i_am_ALSO_a_handler(request): + return HTTPResponse() +``` +```` + +さらに2つの重要な注意事項: + +1. あなたは :class:`sanic.response.HTTPresponse` を直接使用したくないでしょう。 format@@0(./response#methods) のいずれかを使う方が簡単です。 + + - `from sanic import json` + - `from sanic import html` + - `from sanic import redirect` + - _etc_ +2. format@@0(../advanced/streaming#response-streaming)で見るように、オブジェクトを返す必要はありません。 この下位レベルの API を使用する場合は、ハンドラ内からのレスポンスのフローを制御することができ、戻り値オブジェクトは使用されません。 + +.. tip:: Heads Up + +``` +ロジックのカプセル化についてもっと知りたい場合は、[class based views](../advanced/class-based-views.md) をチェックしてください。今後は関数ベースのビューだけで進めていきます。 +``` + +### シンプルな関数ベースのハンドラです + +ルートハンドラを作成する最も一般的な方法は、関数を飾ることです。 ルート定義の視覚的に簡単な識別を作成します。 format@@0(./routing.md) の詳細について学びます。 + +.. 列:: + +``` +Let's look at a practical example. + +- We use a convenience decorator on our app instance: `@app.get()` +- And a handy convenience method for generating out response object: `text()` + +Mission accomplished 💪 +``` + +.. 列:: + +```` +```python +from sanic import text + +@app.get("/foo") +async def foo_handler(request): + return text("I said foo!") +``` +```` + +--- + +## _async_についての単語。 + +.. 列:: + +``` +It is entirely possible to write handlers that are synchronous. + +In this example, we are using the _blocking_ `time.sleep()` to simulate 100ms of processing time. Perhaps this represents fetching data from a DB, or a 3rd-party website. + +Using four (4) worker processes and a common benchmarking tool: + +- **956** requests in 30.10s +- Or, about **31.76** requests/second +``` + +.. 列:: + +```` +```python +@app.get("/sync") +def sync_handler(request): + time.sleep(0.1) + return text("Done") +``` +```` + +.. 列:: + +``` +Just by changing to the asynchronous alternative `asyncio.sleep()`, we see an incredible change in performance. 🚀 + +Using the same four (4) worker processes: + +- **115,590** requests in 30.08s +- Or, about **3,843.17** requests/second + +.. attrs:: + :class: is-size-2 + + 🤯 +``` + +.. 列:: + +```` +```python +@app.get("/async") +async def async_handler(request): + await asyncio.sleep(0.1) + return text("Done") +``` +```` + +わかりました... これは途方もなく過大な結果です どんなベンチマークでも本質的に偏っています この例では、ウェブの世界での `async/await` の利点を紹介します。 結果は確かに異なります。 Sanicや他の非同期Pythonライブラリのようなツールは、物事をより速くする魔法の弾丸ではありません。 これらは _more efficient_ にします。 + +この例では、1つのリクエストがスリープ状態になっているため、非同期バージョンが非常に優れています。 別のものと別のものと別のものと別のものを始めることができます + +しかし、これはポイントである! Sanicは利用可能なリソースを取り出し、パフォーマンスを絞り出すため、高速です。 同時に多くのリクエストを処理することができます。つまり、1秒あたりのリクエストが多くなります。 + +.. tip:: よくある間違い! + +``` +Don't do this! You need to ping a website. What do you use? `pip install your-fav-request-library` 🙈 + +Instead, try using a client that is `async/await` capable. Your server will thank you. Avoid using blocking tools, and favor those that play well in the asynchronous ecosystem. If you need recommendations, check out [Awesome Sanic](https://github.com/mekicha/awesome-sanic). + +Sanic uses [httpx](https://www.python-httpx.org/) inside of its testing package (sanic-testing) 😉. +``` + +--- + +## 完全に注釈を付けられたハンドラです + +型アノテーションを使用している人のために... + +```python +from sanic.response import HTTPResponse, text +from sanic.request import Request + +@app.get("/typed") +async def typed_handler(request: Request) -> HTTPResponse: + return text("Done") +``` + +## ハンドラーに名前を付けています + +すべてのハンドラは自動的に名前が付けられます。 これは、デバッグやテンプレート内の URL を生成する際に便利です。 指定しない場合、使用される名前は関数の名前です。 + +.. 列:: + +``` +例えば、このハンドラは `foo_handler` という名前になります。 +``` + +.. 列:: + +```` +```python +# Handler name will be "foo_handler" +@app.get("/foo") +async def foo_handler(request): + return text("I said foo!") +``` +```` + +.. 列:: + +``` +しかし、デコレータに `name` 引数を渡すことで上書きできます。 +``` + +.. 列:: + +```` +```python +# Handler name will be "foo" +@app.get("/foo", name="foo") +async def foo_handler(request): + return text("I said foo!") +``` +```` + +.. 列:: + +``` +実際には、**注意** が名前を与えなければならないことがあります。 例えば、同じ関数に 2 つのデコレータを使用する場合、少なくとも 1 つの名前を指定する必要があります。 + +しないとエラーが発生し、アプリが起動しません。名前は **必ず**一意でなければなりません。 +``` + +.. 列:: + +```` +```python +# Two handlers, same function, +# different names: +# - "foo_arg" +# - "foo" +@app.get("/foo/", name="foo_arg") +@app.get("/foo") +async def foo(request, arg=None): + return text("I said foo!") +``` +```` diff --git a/guide/content/ja/guide/basics/headers.md b/guide/content/ja/guide/basics/headers.md new file mode 100644 index 0000000000..873194ab56 --- /dev/null +++ b/guide/content/ja/guide/basics/headers.md @@ -0,0 +1,249 @@ +# ヘッダー + +リクエストヘッダーとレスポンスヘッダーは `Request` オブジェクトと `HTTPResponse` オブジェクトでそれぞれ使用できます。 1つのキーが複数の値を持つことを可能にする[`multidict` package](https://multidict.io/en/stable/multidict.html#cimmultidict) を使用します。 + +.. tip:: FYI + +``` +ヘッダキーは解析時に *小文字* に変換されます。ヘッダの場合は大文字化は考慮されません。 +``` + +## リクエスト + +Sanic は、リクエストヘッダの正規化を開発者に提示する前に試みています。 一般的なユースケースに意味のある抽出物を作ることもできます + +.. 列:: + +``` +#### トークン + +`トークン `または `ベアラー `の形式の承認トークンは、リクエストオブジェクト`request.token`に抽出されます。 +``` + +.. 列:: + +```` +```python +@app.route("/") +async def handler(request): + return text(request.token) +``` + +```sh +curl localhost:8000 \ + -H "Authorization: Token ABCDEF12345679" +ABCDEF12345679 +``` + +```sh +curl localhost:8000 \ + -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c +``` +```` + +### プロキシヘッダー + +Sanicはプロキシヘッダーのための特別な扱いを持っています。 詳細は [プロキシ ヘッダー](/guide/advanced/proxy-headers.md) をご覧ください。 + +### ホストヘッダーと動的なURLの構築 + +.. 列:: + +``` +The *effective host* is available via `request.host`. This is not necessarily the same as the host header, as it prefers proxy-forwarded host and can be forced by the server name setting. + +Webapps should generally use this accessor so that they can function the same no matter how they are deployed. The actual host header, if needed, can be found via `request.headers` + +The effective host is also used in dynamic URL construction via `request.url_for`, which uses the request to determine the external address of a handler. + +.. tip:: Be wary of malicious clients + + These URLs can be manipulated by sending misleading host headers. `app.url_for` should be used instead if this is a concern. +``` + +.. 列:: + +```` +```python +app.config.SERVER_NAME = "https://example.com" + +@app.route("/hosts", name="foo") +async def handler(request): + return json( + { + "effective host": request.host, + "host header": request.headers.get("host"), + "forwarded host": request.forwarded.get("host"), + "you are here": request.url_for("foo"), + } + ) +``` + +```sh +curl localhost:8000/hosts +{ + "effective host": "example.com", + "host header": "localhost:8000", + "forwarded host": null, + "you are here": "https://example.com/hosts" +} +``` +```` + +### その他のヘッダー + +.. 列:: + +``` +すべてのリクエストヘッダは `request.headers` 上で利用可能で、辞書形式でアクセスできます。 大文字化はヘッダとは見なされず、大文字または小文字のいずれかのキーでアクセスできます。 +``` + +.. 列:: + +```` +```python +@app.route("/") +async def handler(request): + return json( + { + "foo_weakref": request.headers["foo"], + "foo_get": request.headers.get("Foo"), + "foo_getone": request.headers.getone("FOO"), + "foo_getall": request.headers.getall("fOo"), + "all": list(request.headers.items()), + } + ) +``` + +```sh +curl localhost:9999/headers -H "Foo: one" -H "FOO: two"|jq +{ + "foo_weakref": "one", + "foo_get": "one", + "foo_getone": "one", + "foo_getall": [ + "one", + "two" + ], + "all": [ + [ + "host", + "localhost:9999" + ], + [ + "user-agent", + "curl/7.76.1" + ], + [ + "accept", + "*/*" + ], + [ + "foo", + "one" + ], + [ + "foo", + "two" + ] + ] +} +``` +```` + +.. tip:: FYI + +``` +💡 request.headers オブジェクトは、それぞれの値がリストになる辞書である数少ない型の1つです。 これは、HTTP が 1 つのキーを再利用して複数の値を送信することを可能にするためです。 + +ほとんどの場合、.get() または . を使用します。 etone() メソッドは、リストではなく最初の要素にアクセスします。すべての要素のリストが必要な場合は、.getall() を使用できます。 +``` + +### 要求ID + +.. 列:: + +``` +`X-Request-ID` ヘッダーでリクエストを追跡するのは便利なことや必要なことがよくあります。`request.id` に簡単にアクセスできます。 +``` + +.. 列:: + +```` +```python +@app.route("/") +async def handler(request): + return text(request.id) +``` + +```sh +curl localhost:8000 \ + -H "X-Request-ID: ABCDEF12345679" +ABCDEF12345679 +``` +```` + +## 回答 + +Sanicは次のレスポンスヘッダーを自動的に設定します(適切な場合): + +- `content-length` +- `content-type` +- `connection` +- `transfer-encoding` + +ほとんどの状況では、これらのヘッダーを設定することを心配する必要はありません。 + +.. 列:: + +``` +設定したい他のヘッダーは、route (ルート)ハンドラか、レスポンスミドルウェアのどちらかで行うことができます。 +``` + +.. 列:: + +```` +```python +@app.route("/") +async def handler(request): + return text("Done.", headers={"content-language": "en-US"}) + +@app.middleware("response") +async def add_csp(request, response): + response.headers["content-security-policy"] = "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';base-uri 'self';form-action 'self'" +``` +```` + +.. 列:: + +``` +A common [middleware](middleware.md) you might want is to add a `X-Request-ID` header to every response. As stated above: `request.id` will provide the ID from the incoming request. But, even if no ID was supplied in the request headers, one will be automatically supplied for you. + +[See API docs for more details](https://sanic.readthedocs.io/en/latest/sanic/api_reference.html#sanic.request.Request.id) +``` + +.. 列:: + +```` +```python +@app.route("/") +async def handler(request): + return text(str(request.id)) + +@app.on_response +async def add_request_id_header(request, response): + response.headers["X-Request-ID"] = request.id +``` + +```sh +curl localhost:8000 -i +HTTP/1.1 200 OK +X-Request-ID: 805a958e-9906-4e7a-8fe0-cbe83590431b +content-length: 36 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +805a958e-9906-4e7a-8fe0-cbe83590431b +``` +```` diff --git a/guide/content/ja/guide/basics/listeners.md b/guide/content/ja/guide/basics/listeners.md new file mode 100644 index 0000000000..db4413323d --- /dev/null +++ b/guide/content/ja/guide/basics/listeners.md @@ -0,0 +1,326 @@ +# リスナー + +Sanicは、アプリケーションサーバーのライフサイクルにオペレーションを注入する8つの(8)機会を提供します。 これには [signals](../advanced/signals.md) は含まれません。 + +メインの Sanic プロセスで **のみ** を実行する (2) が2つあります (例: `sanic server.app` を呼び出すごとに1回です)。 + +- `main_process_start` +- `main_process_stop` + +自動リロードがオンになっている場合、リローダープロセスで **のみ** 動作する (2) もあります。 + +- `reload_process_start` +- `reload_process_stop` + +_v22.3_ に `reload_process_start` と `reload_process_stop` を追加しました + +サーバーが起動または終了すると、起動/分解コードを実行することができる4つの(4)があります。 + +- `before_server_start` +- `after_server_start` +- `before_server_stop` +- `after_server_stop` + +ワーカープロセスのライフサイクルは次のようになります。 + +.. mermaid:: + +``` +sequenceDiagram +autonumber +participant Process +participant Worker +participant Listener +participant Handler +Note over Process: sanic server.app +loop + Process->>Listener: @app.main_process_start + Listener->>Handler: Invoke event handler +end +Process->>Worker: Run workers +loop Start each worker + loop + Worker->>Listener: @app.before_server_start + Listener->>Handler: Invoke event handler + end + Note over Worker: Server status: started + loop + Worker->>Listener: @app.after_server_start + Listener->>Handler: Invoke event handler + end + Note over Worker: Server status: ready +end +Process->>Worker: Graceful shutdown +loop Stop each worker + loop + Worker->>Listener: @app.before_server_stop + Listener->>Handler: Invoke event handler + end + Note over Worker: Server status: stopped + loop + Worker->>Listener: @app.after_server_stop + Listener->>Handler: Invoke event handler + end + Note over Worker: Server status: closed +end +loop + Process->>Listener: @app.main_process_stop + Listener->>Handler: Invoke event handler +end +Note over Process: exit +``` + +Sanicプロセスの開始と停止を担当するプロセスの中で、このワーカープロセスの外で再ローダープロセスが稼働しています。 次の例を考えてみましょう: + +```python +@app.reload_process_start +async def reload_start(*_): + print(">>>>>> reload_start <<<<<<") + +@app.main_process_start +async def main_start(*_): + print(">>>>>> main_start <<<<<<") + +@app.before_server_start +async def before_start(*_): + print(">>>>>> before_start <<<<<<") +``` + +このアプリケーションが自動再読み込みをオンにして実行された場合、`reload_start` 関数は、リローダーのプロセスが開始されたときに呼び出されます。 メインプロセスが開始されると、 `main_start` 関数も呼び出されます。 **HOWEVER**、`before_start` 関数は、開始されるワーカープロセスごとに1回呼び出されます。 その後、ファイルが保存されワーカーが再起動されるたびに実行されます。 + +## リスナーをアタッチする + +.. 列:: + +``` +リスナーとして関数をセットアップするプロセスは、route (ルート)を宣言するプロセスと似ています。 + +現在実行されている `Sanic()` インスタンスはリスナーに挿入されます。 +``` + +.. 列:: + +```` +```python +async def setup_db(app): + app.ctx.db = await db_setup() + +app.register_listener(setup_db, "before_server_start") +``` +```` + +.. 列:: + +``` +`Sanic`アプリインスタンスには、利便性のデコレータもあります。 +``` + +.. 列:: + +```` +```python +@app.listener("before_server_start") +async def setup_db(app): + app.ctx.db = await db_setup() +``` +```` + +.. 列:: + +``` +v22.3 より前には、アプリケーションインスタンスとカレントイベントループの両方が関数に注入されました。 ただし、デフォルトではアプリケーションインスタンスのみが注入されます。 関数署名が両方を受け入れる場合は、ここで示すようにアプリケーションとループの両方が注入されます。 +``` + +.. 列:: + +```` +```python +@app.listener("before_server_start") +async def setup_db(app, loop): + app.ctx.db = await db_setup() +``` +```` + +.. 列:: + +``` +デコレータをさらに短くすることができます。オートコンプリート付きの IDE がある場合に便利です。 +``` + +.. 列:: + +```` +```python +@app.before_server_start +async def setup_db(app): + app.ctx.db = await db_setup() +``` +```` + +## 実行の順序 + +リスナーは起動時に宣言された順に実行され、分解中に宣言された順に逆順になります。 + +| | 段階 | ご注文 | +| --------------------- | ------- | ------------- | +| `main_process_start` | メインの起動 | regular 🙂 ⬇️ | +| `before_server_start` | ワーカーの起動 | regular 🙂 ⬇️ | +| `after_server_start` | ワーカーの起動 | regular 🙂 ⬇️ | +| `before_server_stop` | ワーカーの停止 | 🙃 ⬆️ | +| `after_server_stop` | ワーカーの停止 | 🙃 ⬆️ | +| `main_process_stop` | メインシャット | 🙃 ⬆️ | + +次の設定を考えると、2つのworker を実行した場合、コンソールでこれを確認する必要があります。 + +.. 列:: + +```` +```python +@app.listener("before_server_start") +async def listener_1(app, loop): + print("listener_1") + +@app.before_server_start +async def listener_2(app, loop): + print("listener_2") + +@app.listener("after_server_start") +async def listener_3(app, loop): + print("listener_3") + +@app.after_server_start +async def listener_4(app, loop): + print("listener_4") + +@app.listener("before_server_stop") +async def listener_5(app, loop): + print("listener_5") + +@app.before_server_stop +async def listener_6(app, loop): + print("listener_6") + +@app.listener("after_server_stop") +async def listener_7(app, loop): + print("listener_7") + +@app.after_server_stop +async def listener_8(app, loop): + print("listener_8") +``` +```` + +.. 列:: + +```` +```bash +[pid: 1000000] [INFO] Goin' Fast @ http://127.0.0.1:9999 +[pid: 1000000] [INFO] listener_0 +[pid: 1111111] [INFO] listener_1 +[pid: 1111111] [INFO] listener_2 +[pid: 1111111] [INFO] listener_3 +[pid: 1111111] [INFO] listener_4 +[pid: 1111111] [INFO] Starting worker [1111111] +[pid: 1222222] [INFO] listener_1 +[pid: 1222222] [INFO] listener_2 +[pid: 1222222] [INFO] listener_3 +[pid: 1222222] [INFO] listener_4 +[pid: 1222222] [INFO] Starting worker [1222222] +[pid: 1111111] [INFO] Stopping worker [1111111] +[pid: 1222222] [INFO] Stopping worker [1222222] +[pid: 1222222] [INFO] listener_6 +[pid: 1222222] [INFO] listener_5 +[pid: 1222222] [INFO] listener_8 +[pid: 1222222] [INFO] listener_7 +[pid: 1111111] [INFO] listener_6 +[pid: 1111111] [INFO] listener_5 +[pid: 1111111] [INFO] listener_8 +[pid: 1111111] [INFO] listener_7 +[pid: 1000000] [INFO] listener_9 +[pid: 1000000] [INFO] Server Stopped +``` +In the above example, notice how there are three processes running: + +- `pid: 1000000` - The *main* process +- `pid: 1111111` - Worker 1 +- `pid: 1222222` - Worker 2 + +*Just because our example groups all of one worker and then all of another, in reality since these are running on separate processes, the ordering between processes is not guaranteed. But, you can be sure that a single worker will **always** maintain its order.* +```` + +.. tip:: FYI + +``` +実際の結果は、 `before_server_start` ハンドラの最初のリスナーがデータベース接続を設定する場合です。 その後登録されたリスナーはその接続が生きていることに頼ることができます 開始時と停止時の両方。 +``` + +### 優先度 + +v23.12 では `priority` キーワード引数がリスナーに追加されました。 これにより、リスナーの実行順序を微調整できます。 デフォルトの優先度は `0` です。 優先度が高いリスナーが最初に実行されます。 同じ優先度を持つリスナーは、登録された順に実行されます。 さらに、 `app` インスタンスにアタッチされたリスナーは、 `Blueprint` インスタンスにアタッチされたリスナーの前に実行されます。 + +全体的に実行の順序を決定するためのルールは次のとおりです。 + +1. 降順の優先度 +2. Blueprint リスナーより前のアプリのリスナー +3. 登録注文 + +.. 列:: + +```` +一例として、以下のようなものを考えてみましょう。 これは + +```bash +third +bp_third +second +bp_second +first +fth +bp_first +``` +```` + +.. 列:: + +```` +```python +@app.before_server_start +async def first(app): + print("first") + +@app.listener("before_server_start", priority=2) +async def second(app): + print("second") + +@app.before_server_start(priority=3) +async def third(app): + print("third") + +@bp.before_server_start +async def bp_first(app): + print("bp_first") + +@bp.listener("before_server_start", priority=2) +async def bp_second(app): + print("bp_second") + +@bp.before_server_start(priority=3) +async def bp_third(app): + print("bp_third") + +@app.before_server_start +async def fourth(app): + print("fourth") + +app.blueprint(bp) +``` +```` + +## ASGI モード + +ASGI サーバーでアプリケーションを実行している場合は、次の変更を確認してください。 + +- `reload_process_start` と `reload_process_stop` は **無視されます** +- `main_process_start` と `main_process_stop` は **無視されます** +- `before_server_start` はできるだけ早く実行され、`after_server_start` の前に実行されますが、技術的にはその時点で既に実行されています +- `after_server_stop` はできるだけ遅く実行され、`before_server_stop` の後になりますが、技術的には、サーバーはその時点で動作しています。 diff --git a/guide/content/ja/guide/basics/middleware.md b/guide/content/ja/guide/basics/middleware.md new file mode 100644 index 0000000000..18687a1014 --- /dev/null +++ b/guide/content/ja/guide/basics/middleware.md @@ -0,0 +1,279 @@ +# ミドルウェア + +一方、リスナーはワーカープロセスのライフサイクルに機能を付加することができます。 ミドルウェアを使用すると、HTTPストリームのライフサイクルに機能を追加できます。 + +```python +@app.on_request +async def example(request): + print("I execute before the handler") +``` + +ミドルウェアは _before_ ハンドラが実行されるか、_after_ のどちらかで実行できます。 + +```python +@app.on_response +async def example(request, response): + print("I execute after the handler") +``` + +.. mermaid:: + +``` +sequenceDiagram +autonumber +participant Worker +participant Middleware +participant MiddlewareHandler +participant RouteHandler +Note over Worker: Incoming HTTP request +loop + Worker->>Middleware: @app.on_request + Middleware->>MiddlewareHandler: Invoke middleware handler + MiddlewareHandler-->>Worker: Return response (optional) +end +rect rgba(255, 13, 104, .1) +Worker->>RouteHandler: Invoke route handler +RouteHandler->>Worker: Return response +end +loop + Worker->>Middleware: @app.on_response + Middleware->>MiddlewareHandler: Invoke middleware handler + MiddlewareHandler-->>Worker: Return response (optional) +end +Note over Worker: Deliver response +``` + +## ミドルウェアをアタッチ中 + +.. 列:: + +``` +これはおそらく今ごろはよく見覚えがあるはずです。 ミドルウェアに`request`または`response`を実行させたいときに宣言するだけです。 +``` + +.. 列:: + +```` +```python +async def extract_user(request): + request.ctx.user = await extract_user_from_request(request) + +app.register_middleware(extract_user, "request") +``` +```` + +.. 列:: + +``` +繰り返しになりますが、`Sanic`アプリインスタンスには利便性デコレータもあります。 +``` + +.. 列:: + +```` +```python +@app.middleware("request") +async def extract_user(request): + request.ctx.user = await extract_user_from_request(request) +``` +```` + +.. 列:: + +``` +Response middleware は `request` と `response` の両方の引数を受け取ります。 +``` + +.. 列:: + +```` +```python +@app.middleware('response') +async def prevent_xss(request, response): + response.headers["x-xss-protection"] = "1; mode=block" +``` +```` + +.. 列:: + +``` +デコレータをさらに短くすることができます。オートコンプリート付きの IDE がある場合に便利です。 + +これが好ましい用途であり、今後使用するものです。 +``` + +.. 列:: + +```` +```python +@app.on_request +async def extract_user(request): + ... + +@app.on_response +async def prevent_xss(request, response): + ... +``` +```` + +## Modification + +ミドルウェアは与えられたリクエストやレスポンスのパラメータを変更することができます。 + +.. 列:: + +``` +#### 実行順序 + +1. リクエストミドルウェア: `add_key` +2. ルートハンドラ: `index` +3. レスポンスミドルウェア: `prevent_xss` +4. レスポンスミドルウェア: `custom_banner` +``` + +.. 列:: + +```` +```python +@app.on_request +async def add_key(request): + # Arbitrary data may be stored in request context: + request.ctx.foo = "bar" + +@app.on_response +async def custom_banner(request, response): + response.headers["Server"] = "Fake-Server" + +@app.on_response +async def prevent_xss(request, response): + response.headers["x-xss-protection"] = "1; mode=block" + +@app.get("/") +async def index(request): + return text(request.ctx.foo) + +``` +```` + +.. 列:: + +``` +`request.match_info`を変更することができます。例えばミドルウェアで`a-slug`をa_slug`に変換するのに役立つ機能です。 +``` + +.. 列:: + +```` +```python +@app.on_request +def convert_slug_to_underscore(request: Request): + request.match_info["slug"] = request.match_info["slug"].replace("-", "_") + +@app.get("/") +async def handler(request, slug): + return text(slug) +``` +``` +$ curl localhost:9999/foo-bar-baz +foo_bar_baz +``` +```` + +## 早期対応 + +.. 列:: + +``` +middleware が `HTTPResponse` オブジェクトを返す場合、リクエストは処理を停止し、レスポンスが返されます。 ルートハンドラに到達する前にリクエストにこれが発生した場合、ハンドラは **呼び出されません** 。 応答を返すと、さらにミドルウェアが実行されなくなります。 + +``` + +.. tip:: + +``` +`None` を返すと、ミドルウェアハンドラの実行が停止し、リクエストが通常通り処理できるようになります。 これは、ミドルウェアハンドラ内のリクエスト処理を避けるために早期の戻り値を使用する場合に便利です。 +``` + +.. 列:: + +```` +```python +@app.on_request +async def halt_request(request): + return text("I halted the request") + +@app.on_response +async def halt_response(request, response): + return text("I halted the response") +``` +```` + +## 実行の順序 + +リクエストミドルウェアは宣言された順序で実行されます。 レスポンスミドルウェアは**逆順**で実行されます。 + +以下の設定では、コンソールでこれを確認する必要があります。 + +.. 列:: + +```` +```python +@app.on_request +async def middleware_1(request): + print("middleware_1") + +@app.on_request +async def middleware_2(request): + print("middleware_2") + +@app.on_response +async def middleware_3(request, response): + print("middleware_3") + +@app.on_response +async def middleware_4(request, response): + print("middleware_4") + +@app.get("/handler") +async def handler(request): + print("~ handler ~") + return text("Done.") +``` +```` + +.. 列:: + +```` +```bash +middleware_1 +middleware_2 +~ handler ~ +middleware_4 +middleware_3 +[INFO][127.0.0.1:44788]: GET http://localhost:8000/handler 200 5 +``` +```` + +### ミドルウェアの優先度 + +.. 列:: + +``` +ミドルウェアの実行順序は、より高い優先度を割り当てることで変更できます。ミドルウェア定義の内部で発生します。 値が高いほど、他のミドルウェアから相対的に実行されます。ミドルウェアのデフォルトの優先度は `0` です。 +``` + +.. 列:: + +```` +```python +@app.on_request +async def low_priority(request): + ... + +@app.on_request(priority=99) +async def high_priority(request): + ... +``` +```` + +_v22.9_に追加されました diff --git a/guide/content/ja/guide/basics/request.md b/guide/content/ja/guide/basics/request.md new file mode 100644 index 0000000000..fa39536332 --- /dev/null +++ b/guide/content/ja/guide/basics/request.md @@ -0,0 +1,476 @@ +# リクエスト + +API ドキュメント: [sanic.request](/api/sanic.request) を参照してください。 + +:class:`sanic.request.Request`インスタンスには、パラメータで入手可能な役立つ情報がたくさん含まれています。 詳細については、format@@0(https://sanic.readthedocs.io/)を参照してください。 + +[handlers](./handlers) のセクションで見たとおり、ルートハンドラの最初の引数は通常、 :class:`sanic.request.Request` オブジェクトです。 Sanic は非同期フレームワークなので、ハンドラは[`asyncio.Task`](https://docs.python.org/3/library/asyncio-task.html#asyncio.Task)の中で実行され、イベントループによってスケジュールされます。 これは、ハンドラが分離されたコンテキストで実行され、リクエストオブジェクトはそのハンドラのタスクに固有であることを意味します。 + +.. 列:: + +``` +規約により、引数は `request` という名前になりますが、好きな名前を付けることができます。 引数の名前は重要ではありません。以下のハンドラの両方が有効です。 +``` + +.. 列:: + +```` +```python +@app.get("/foo") +async def typical_use_case(request): + return text("I said foo!") +``` + +```python +@app.get("/foo") +async def atypical_use_case(req): + return text("I said foo!") +``` +```` + +.. 列:: + +``` +リクエストオブジェクトに注釈を付けるのはとても簡単です。 + +``` + +.. 列:: + +```` +```python +from sanic.request import Request +from sanic.response import text + +@app.get("/typed") +async def typed_handler(request: Request): + return text("Done.") +``` +```` + +.. tip:: + +``` +最新のIDEを使用していると仮定すると、コード補完とドキュメントを支援するために型注釈を活用する必要があります。 これは特に、 **MANY** のプロパティとメソッドがあるので、 `request` オブジェクトを使用する場合に便利です。 + +利用可能なプロパティとメソッドの完全なリストは、[API ドキュメント](/api/sanic.request) を参照してください。 +``` + +## 本文 + +`Request`オブジェクトは、いくつかの方法でリクエストボディのコンテンツにアクセスできます。 + +### JSON + +.. 列:: + +``` +**Parameter**: `request.json` +**Description**: 解析されたJSONオブジェクト +``` + +.. 列:: + +```` +```bash +$ curl localhost:8000 -d '{"foo": "bar"}' +``` + +```python +>>> print(request.json) +{'foo': 'bar'} +``` +```` + +### Raw + +.. 列:: + +``` +**Parameter**: `request.body` +**Description**: リクエストボディからの生のバイト +``` + +.. 列:: + +```` +```bash +$ curl localhost:8000 -d '{"foo": "bar"}' +``` + +```python +>>> print(request.body) +b'{"foo": "bar"}' +``` +```` + +### フォーム + +.. 列:: + +``` +**Parameter**: `request.form` +**Description**: The form data + +.. tip:: FYI + + The `request.form` object is one of a few types that is a dictionary with each value being a list. This is because HTTP allows a single key to be reused to send multiple values. + + Most of the time you will want to use the `.get()` method to access the first element and not a list. If you do want a list of all items, you can use `.getlist()`. +``` + +.. 列:: + +```` +```bash +$ curl localhost:8000 -d 'foo=bar' +``` + +```python +>>> print(request.body) +b'foo=bar' + +>>> print(request.form) +{'foo': ['bar']} + +>>> print(request.form.get("foo")) +bar + +>>> print(request.form.getlist("foo")) +['bar'] +``` +```` + +### アップロードしました + +.. 列:: + +``` +**Parameter**: `request.files` +**Description**: The files uploaded to the server + +.. tip:: FYI + + The `request.files` object is one of a few types that is a dictionary with each value being a list. This is because HTTP allows a single key to be reused to send multiple values. + + Most of the time you will want to use the `.get()` method to access the first element and not a list. If you do want a list of all items, you can use `.getlist()`. +``` + +.. 列:: + +```` +```bash +$ curl -F 'my_file=@/path/to/TEST' http://localhost:8000 +``` + +```python +>>> print(request.body) +b'--------------------------cb566ad845ad02d3\r\nContent-Disposition: form-data; name="my_file"; filename="TEST"\r\nContent-Type: application/octet-stream\r\n\r\nhello\n\r\n--------------------------cb566ad845ad02d3--\r\n' + +>>> print(request.files) +{'my_file': [File(type='application/octet-stream', body=b'hello\n', name='TEST')]} + +>>> print(request.files.get("my_file")) +File(type='application/octet-stream', body=b'hello\n', name='TEST') + +>>> print(request.files.getlist("my_file")) +[File(type='application/octet-stream', body=b'hello\n', name='TEST')] +``` +```` + +## コンテキスト + +### コンテキストをリクエスト + +`request.ctx`オブジェクトは、リクエストに関して必要な情報を保存するためのプレイグラウンドです。 これはリクエストの期間のみ有効で、リクエストに固有のものです。 + +これはすべてのリクエストで共有される `app.ctx` オブジェクトで解釈できます。 それらを混乱させないように注意してください! + +デフォルトでは `request.ctx` オブジェクトは `SimpleNamespace` オブジェクトで、任意の属性を設定できます。 Sanicはこのオブジェクトを何にも使用しないので、名前の衝突を心配することなく自由に使用できます。 + +### 典型的な使用例 + +これは、認証されたユーザーの詳細などのアイテムを格納するためによく使用されます。 あとで [middleware](./middleware.md) に入りますが、ここに簡単な例があります。 + +```python +@app.on_request +async def run_before_handler(request): + request.ctx.user = await fetch_user_by_token(request.token) + +@app.route('/hi') +async def hi_my_name_is(request): + if not request.ctx.user: + return text("Hmm... I don't know you) + return text(f"Hi, my name is {request.ctx.user.name}") +``` + +ご覧のとおり、 `request. tx`オブジェクトは、複数のハンドラにアクセスするために必要な情報を格納するのに最適な場所です。 しかし、format@@0(./middleware) で学びます。 d)を使用して、別のミドルウェアから情報を保存することもできます。 + +### 接続の内容 + +.. 列:: + +``` +Often times your API will need to serve multiple concurrent (or consecutive) requests to the same client. This happens, for example, very often with progressive web apps that need to query multiple endpoints to get data. + +The HTTP protocol calls for an easing of overhead time caused by the connection with the use of [keep alive headers](../deployment/configuration.md#keep-alive-timeout). + +When multiple requests share a single connection, Sanic provides a context object to allow those requests to share state. +``` + +.. 列:: + +```` +```python +@app.on_request +async def increment_foo(request): + if not hasattr(request.conn_info.ctx, "foo"): + request.conn_info.ctx.foo = 0 + request.conn_info.ctx.foo += 1 + +@app.get("/") +async def count_foo(request): + return text(f"request.conn_info.ctx.foo={request.conn_info.ctx.foo}") +``` + +```bash +$ curl localhost:8000 localhost:8000 localhost:8000 +request.conn_info.ctx.foo=1 +request.conn_info.ctx.foo=2 +request.conn_info.ctx.foo=3 +``` +```` + +.. 警告:: + +``` +リクエスト間の情報を1つのHTTP接続で保存するのに便利な場所に見えます。 単一の接続上のすべてのリクエストが単一のエンドユーザーから来たと仮定しないでください。 これは、HTTP プロキシとロードバランサが複数の接続をサーバに複数回接続できるためです。 + +**単一のユーザーに関する情報を保存するには、これを使用しないでください** 。 `request.ctx` オブジェクトを使用してください。 +``` + +### カスタムリクエストオブジェクト + +[application customization](./app.md#custom-requests)で説明されているように、 :class:`sanic.request.Request` のサブクラスを作成して、リクエストオブジェクトに追加機能を追加できます。 これは、アプリケーションに固有の追加の属性やメソッドを追加する場合に便利です。 + +.. 列:: + +``` +たとえば、アプリケーションがユーザー ID を含むカスタム ヘッダーを送信するとします。 そのヘッダーを解析し、ユーザーIDを保存するカスタムリクエストオブジェクトを作成できます。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic, Request + +class CustomRequest(Request): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.user_id = self.headers.get("X-User-ID") + +app = Sanic("Example", request_class=CustomRequest) +``` +```` + +.. 列:: + +``` +ハンドラで `user_id` 属性にアクセスできるようになりました。 +``` + +.. 列:: + +```` +```python +@app.route("/") +async def handler(request: CustomRequest): + return text(f"User ID: {request.user_id}") +``` +```` + +### カスタムリクエストのコンテキスト + +デフォルトでは、リクエスト コンテキスト (`request.ctx`) は [`Simplenamespace`](https://docs.python.org/3/library/types.html#types.SimpleNamespace) オブジェクトで、任意の属性を設定できます。 これはアプリケーション全体でロジックを再利用するのに非常に役立ちます。 IDEで利用可能な属性がわからないため、開発経験では困難な場合があります。 + +これを支援するために、デフォルトの `SimpleNamespace` の代わりに使用されるカスタムリクエストコンテキストオブジェクトを作成できます。 これにより、型ヒントをコンテキストオブジェクトに追加し、それらをIDEで使用できるようにできます。 + +.. 列:: + +``` +:class:`sanic.request.Request`クラスをサブクラス化し、カスタムリクエストタイプを作成します。 次に、カスタムコンテキストオブジェクトのインスタンスを返す、 `make_context()` メソッドを追加する必要があります。 *注意: `make_context` メソッドは静的メソッドである必要があります。* +``` + +.. 列:: + +```` +```python +from sanic import Sanic, Request +from types import SimpleNamespace + +class CustomRequest(Request): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.ctx.user_id = self.headers.get("X-User-ID") + + @staticmethod + def make_context() -> CustomContext: + return CustomContext() + +@dataclass +class CustomContext: + user_id: str = None +``` +```` + +.. note:: + +``` +これはSanic poweruser 機能で、大きなコードベースでリクエストコンテキストオブジェクトを入力するのに非常に便利です。 それはもちろん必要ではありませんが、非常に役に立ちます。 +``` + +_V23.6_に追加されました + +## パラメータ + +.. 列:: + +``` +パスパラメータから抽出された値は、引数としてハンドラに注入され、より具体的にはキーワード引数として注入されます。 format@@0(./routing.md) には詳細が記載されています。 +``` + +.. 列:: + +```` +```python +@app.route('/tag/') +async def tag_handler(request, tag): + return text("Tag - {}".format(tag)) + +# or, explicitly as keyword arguments +@app.route('/tag/') +async def tag_handler(request, *, tag): + return text("Tag - {}".format(tag)) +``` +```` + +## 引数 + +`request`インスタンスには、クエリパラメータを取得するための2つの属性があります。 + +- `request.args` +- `request.query_args` + +これにより、リクエストパス(URL内の `?` の後の部分)からクエリパラメータにアクセスできます。 + +### 典型的な使用例 + +ほとんどの場合、`request.args` オブジェクトを使用してクエリパラメータにアクセスします。 これは解析されたクエリ文字列を辞書として使用します。 + +これははるかに一般的なパターンです。 + +.. 列:: + +``` +何かを検索するために使用する `q` パラメータを持つ `/search` エンドポイントがある例を考えてみましょう。 +``` + +.. 列:: + +```` +```python +@app.get("/search") +async def search(request): + query = request.args.get("q") + if not query: + return text("No query string provided") + return text(f"Searching for: {query}") +``` +```` + +### クエリ文字列の解析 + +場合によっては、クエリ文字列に生の文字列またはタプルのリストとしてアクセスしたい場合があります。 これには、 `request.query_string` と `request.query_args` 属性を使用できます。 + +また、HTTP は 1 つのキーに対して複数の値を許可することに注意する必要があります。 `request.args` は通常の辞書のように見えるかもしれませんが、実際には1つのキーに対して複数の値を与える特別な型です。 `request.args.getlist()`メソッドを使用することでアクセスできます。 + +- `request.query_string` - 生クエリ文字列 +- `request.query_args` - タプルのリストとして解析されたクエリー文字列。 +- `request.args` - _special_辞書として解析されたクエリ文字列。 + - `request.args.get()` - キーの最初の値を取得します (通常の辞書のように動作します) + - `request.args.getlist()` - キーの値をすべて取得します + +```sh +curl "http://localhost:8000?key1=val1&key2=val2&key1=val3" +``` + +```python +>>> print(request.args) +{'key1': ['val1', 'val3'], 'key2': ['val2']} + +>>> print(request.args.get("key1")) +val1 + +>>> print(request.args.getlist("key1")) +['val1', 'val3'] + +>>> print(request.query_args) +[('key1', 'val1'), ('key2', 'val2'), ('key1', 'val3')] + +>>> print(request.query_string) +key1=val1&key2=val2&key1=val3 + +``` + +.. tip:: FYI + +``` +`request.args` オブジェクトは、それぞれの値がリストになる辞書である数少ない型の1つです。 これは、HTTP が 1 つのキーを再利用して複数の値を送信することを可能にするためです。 + +ほとんどの場合、リストではなく最初の要素にアクセスするために `.get()` メソッドを使用します。 全てのアイテムのリストが必要な場合は、 `.getlist()` を使用します。 +``` + +## 現在のリクエスト取得 + +場合によっては、アプリケーション内の現在のリクエストにアクセスできない場所でアクセスする必要があることがあります。 典型的な例は、`logging`形式です。 `Request.get_current()`を使用して、現在のリクエストをフェッチすることができます (もしあれば)。 + +リクエストオブジェクトは、ハンドラを実行している単一の[`asyncio.Task`](https://docs.python.org/3/library/asyncio-task.html#asyncio.Task)に制限されていることを覚えておいてください。 そのタスクに参加していない場合、リクエストオブジェクトはありません。 + +```python +import logging + +from sanic import Request, Sanic, json +from sanic.exceptions import SanicException +from sanic.log import LOGGING_CONFIG_DEFAULTS + +LOGGING_FORMAT = ( + "%(asctime)s - (%(name)s)[%(levelname)s][%(host)s]: " + "%(request_id)s %(request)s %(message)s %(status)d %(byte)d" +) + +old_factory = logging.getLogRecordFactory() + +def record_factory(*args, **kwargs): + record = old_factory(*args, **kwargs) + record.request_id = "" + + try: + request = Request.get_current() + except SanicException: + ... + else: + record.request_id = str(request.id) + + return record + +logging.setLogRecordFactory(record_factory) + + +LOGGING_CONFIG_DEFAULTS["formatters"]["access"]["format"] = LOGGING_FORMAT +app = Sanic("Example", log_config=LOGGING_CONFIG_DEFAULTS) +``` + +この例では、すべてのアクセスログメッセージに `request.id` を追加しています。 + +_v22.6_に追加されました diff --git a/guide/content/ja/guide/basics/response.md b/guide/content/ja/guide/basics/response.md new file mode 100644 index 0000000000..d3216aa913 --- /dev/null +++ b/guide/content/ja/guide/basics/response.md @@ -0,0 +1,299 @@ +# 回答 + +すべての [handlers](./handlers.md) _通常_ レスポンスオブジェクトを返し、 [middleware](./middleware.md) は任意でレスポンスオブジェクトを返すことができます。 + +そのステートメントを明確にするには: + +- ハンドラがクライアントにバイトを送信するための独自のパターンを処理するストリーミングエンドポイントでない限り、 戻り値は :class:`sanic のインスタンスでなければなりません。 esponse.HTTPResponse` (format@@0(../advanced/streaming.md#response-streaming)を参照してください)。 **ほとんどの** ユースケースでは、レスポンスを返す必要があります。 +- ミドルウェアがレスポンスオブジェクトを返す場合は、ハンドラが何でも代わりに使用されます (詳細については、 [middleware](./middleware.md) を参照)。 + +基本的なハンドラーは以下のようになります。 :class:`sanic.response.HTTPResponse` オブジェクトは、クライアントに返されるステータス、本文、およびヘッダーを設定できます。 + +```python +from sanic import HTTPResponse, Sanic + +app = Sanic("TestApp") + +@app.route("") +def handler(_): + return HTTPResponse() +``` + +ただし、通常、以下で説明する便利な方法のいずれかを使用する方が簡単です。 + +## メソッド + +レスポンスオブジェクトを生成する最も簡単な方法は、コンビニエンス関数のいずれかを使用することです。 + +### テキスト + +.. 列:: + +``` +**デフォルト Content-Type**: `text/plain; charset=utf-8` +**Description**: プレーン テキストを返す +``` + +.. 列:: + +```` +```python +from sanic import text + +@app.route("/") +async def handler(request): + return text("Hi 😎) +``` +```` + +### HTML + +.. 列:: + +``` +**Default Content-Type**: `text/html; charset=utf-8` +**Description**: HTML ドキュメントを返す +``` + +.. 列:: + +```` +```python +from sanic import html + +@app.route("/") +async def handler(request): + return html('
Hi 😎
') +``` +```` + +### JSON + +.. 列:: + +``` +**デフォルト Content-Type**: `application/json` +**Description**: JSON ドキュメントを返す +``` + +.. 列:: + +```` +```python +from sanic import json + +@app.route("/") +async def handler(request): + return json({"foo": "bar"}) +``` +```` + +デフォルトでは、Sanic は [`ujson`](https://github.com/ultrajson/ultrajson) を JSON エンコーダとして出荷します。 `ujson` がインストールされていない場合、標準ライブラリ `json` に戻ります。 + +あなたが望むならば、これを変更するのは超簡単です。 + +```python +from sanic import json +from orjson import dump + +json({"foo": "bar"}, dumps=dumps) +``` + +また、アプリケーション全体で使用する実装を初期化時に宣言することもできます。 + +```python +from orjson import dump + +app = Sanic(..., dumps=dumps) +``` + +### ファイル + +.. 列:: + +``` +**Default Content-Type**: N/A +**Description**: ファイルを返す +``` + +.. 列:: + +```` +```python +from sanic import file + +@app.route("/") +async def handler(request): + return await file("/path/to/whatever.png") +``` +```` + +Sanicはファイルを調べ、MIMEタイプを推測し、コンテンツ型に適切な値を使用します。 必要に応じて、次のように説明できます。 + +```python +file("/path/to/whatever.png", mime_type="image/png") +``` + +ファイル名を上書きすることもできます: + +```python +file("/path/to/whatever.png", filename="super-awesome-increverble.png") +``` + +### ファイルストリーミング + +.. 列:: + +``` +**Default Content-Type**: N/A +**Description**: クライアントにファイルをストリーミングします。ビデオのような大きなファイルをストリーミングするときに便利です。 +``` + +.. 列:: + +```` +```python +from sanic.response import file_stream + +@app.route("/") +async def handler(request): + return await file_stream("/path/to/whatever.mp4") +``` +```` + +`file()`メソッドと同様に、`file_stream()`はファイルのMIMEタイプを決定しようとします。 + +### Raw + +.. 列:: + +``` +**Default Content-Type**: `application/octet-stream` +**Description**: body をエンコードせずに raw bytes を送る +``` + +.. 列:: + +```` +```python +from sanic import raw + +@app.route("/") +async def handler(request): + return raw(b"raw bytes") +``` +```` + +### リダイレクト + +.. 列:: + +``` +**デフォルトのContent-Type**: `text/html; charset=utf-8` +**Description**: クライアントを別のパスにリダイレクトするために `302` レスポンスを送信する +``` + +.. 列:: + +```` +```python +from sanic import redirect + +@app.route("/") +async def handler(request): + return redirect("/login") +``` +```` + +### なし + +.. 列:: + +``` +**Default Content-Type**: N/A +**Description**: [RFC 2616] (https://tools.ietf.org/search/rfc2616#section-7.2.1) で定義されている空のメッセージで応答するためのものです。 +``` + +.. 列:: + +```` +```python +from sanic import empty + +@app.route("/") +async def handler(request): + return empty() +``` + +デフォルトは `204` ステータスです。 +```` + +## 既定のステータス + +レスポンスのデフォルトの HTTP ステータスコードは `200` です。 変更が必要な場合は、responseメソッドで行うことができます。 + +```python +@app.post("/") +async def create_new(request): + new_thing = await do_create(request) + return json({"created": True, "id": new_thing.thing_id}, status=201) +``` + +## JSON データを返す + +v22.12 から始まります。`sanic.json` コンビニエンスメソッドを使用すると、 :class:`sanic.response.types.JSONResponse` という `HTTPResponse` のサブクラスが返されます。 このオブジェクト +には、一般的なJSONボディを変更するための便利なメソッドがいくつかあります。 + +```python +from sanic import json + +resp = json(...) +``` + +- `resp.set_body()` - JSONオブジェクトの本体を渡された値に設定します。 +- `resp.append()` - `list.append` のようにボディに値を追加します(ルートJSONが配列の場合のみ動作します) +- `resp.extend()` - `list.extend` のように値をボディに拡張します(ルートJSONが配列の場合のみ動作します) +- `resp.update()` - `dict.update` のような値で本文を更新します (ルートJSONがオブジェクトの場合のみ動作します) +- `resp.pop()` - `list.pop` や `dict.pop` のような値をポップします (ルートJSONが配列またはオブジェクトの場合にのみ動作します) + +.. 警告:: + +``` +生の Python オブジェクトは `JSONResponse` オブジェクトに `raw_body` として保存されます。 この値を新しい値で上書きしても安全ですが、変更しようとしないでください。 代わりに上記の方法を使用する必要があります。 +``` + +```python +resp = json({"foo": "bar"}) + +# This is OKAY +resp.raw_body = {"foo": "bar", "something": "else"} + +# This is better +resp.set_body({"foo": "bar", "something": "else"}) + +# This is also works well +resp.update({"something": "else"}) + +# This is NOT OKAY +resp.raw_body.update({"something": "else"}) +``` + +```python +# Or, even treat it like a list +resp = json(["foo", "bar"]) + +# This is OKAY +resp.raw_body = ["foo", "bar", "something", "else"] + +# This is better +resp.extend(["something", "else"]) + +# This is also works well +resp.append("something") +resp.append("else") + +# This is NOT OKAY +resp.raw_body.append("something") +``` + +_v22.9_に追加されました diff --git a/guide/content/ja/guide/basics/routing.md b/guide/content/ja/guide/basics/routing.md new file mode 100644 index 0000000000..6ee0fb5578 --- /dev/null +++ b/guide/content/ja/guide/basics/routing.md @@ -0,0 +1,927 @@ +# ルーティング + +.. 列:: + +``` +これまで、様々な形でこのデコレータをたくさん見てきました。 + +しかし、それは何ですか?そして、どのように使うのですか? +``` + +.. 列:: + +```` +```python +@app.route("/storeway") + ... + +@app.get("/to") + ... + +@app.post("/heaven") + ... +``` +```` + +## ルートの追加 + +.. 列:: + +``` +ハンドラをエンドポイントに接続する最も基本的な方法は、 `app.add_route()` です。 + +詳細は [API docs](https://sanic.readthedocs.io/en/stable/sanic/api_reference.html#sanic.app.url_for) を参照してください。 +``` + +.. 列:: + +```` +```python +async def handler(request): + return text("OK") + +app.add_route(handler, "/test") +``` +```` + +.. 列:: + +``` +デフォルトでは、ルートは HTTP `GET` 呼び出しとして使用できます。ハンドラーを1つまたは複数の HTTP メソッドに応答するように変更できます。 +``` + +.. 列:: + +```` +```python +app.add_route( + handler, + '/test', + methods=["POST", "PUT"], +) +``` +```` + +.. 列:: + +``` +デコレータ構文を使用すると、前の例はこれと同じです。 +``` + +.. 列:: + +```` +```python +@app.route('/test', methods=["POST", "PUT"]) +async def handler(request): + return text('OK') +``` +```` + +## HTTPメソッド + +それぞれの標準的な HTTP メソッドには、利便性のデコレータがあります。 + +### 取得 + +```python +@app.get('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET) + +### POST + +```python +@app.post('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST + +### PUT + +```python +@app.put('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) + +### PATCH + +```python +@app.patch('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) + +### 削除 + +```python +@app.delete('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE) + +### 頭 + +```python +@app.head('/test') +async def handler(request): + return empty() +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) + +### オプション + +```python +@app.options('/test') +async def handler(request): + return empty() +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS) + +.. 警告:: + +```` +By default, Sanic will **only** consume the incoming request body on non-safe HTTP methods: `POST`, `PUT`, `PATCH`, `DELETE`. If you want to receive data in the HTTP request on any other method, you will need to do one of the following two options: + +**Option #1 - Tell Sanic to consume the body using `ignore_body`** +```python +@app.request("/path", ignore_body=False) +async def handler(_): + ... +``` + +**Option #2 - Manually consume the body in the handler using `receive_body`** +```python +@app.get("/path") +async def handler(request: Request): + await request.receive_body() +``` +```` + +## パスパラメータ + +.. 列:: + +``` +SanicはパターンマッチングとURLパスからの値抽出を可能にします。これらのパラメータはルートハンドラのキーワード引数として注入されます。 +``` + +.. 列:: + +```` +```python +@app.get("/tag/") +async def tag_handler(request, tag): + return text("Tag - {}".format(tag)) +``` +```` + +.. 列:: + +``` +パラメータの型を宣言できます。これはマッチング時に強制され、変数をキャストします。 +``` + +.. 列:: + +```` +```python +@app.get("/foo/") +async def uuid_handler(request, foo_id: UUID): + return text("UUID - {}".format(foo_id)) +``` +```` + +.. 列:: + +``` +`str`、`int`、`UUID`のようないくつかの標準型では、Sanicは関数のシグネチャからパスパラメータ型を推測することができます。 つまり、pathパラメータ定義に型を含める必要があるわけではありません。 +``` + +.. 列:: + +```` +```python +@app.get("/foo/") # Notice there is no :uuid in the path parameter +async def uuid_handler(request, foo_id: UUID): + return text("UUID - {}".format(foo_id)) +``` +```` + +### サポートされているタイプ + +### `str` + +.. 列:: + +``` +**正規表現が適用されました**: `r"[^/]+"` +**キャストタイプ**: `str` +**一致例**: + +- `/path/to/Bob` +- `/path/to/Python%203` + +v22から始まります。 `str`は空の文字列では*マッチしません*。この動作については`strorempty`を参照してください。 +``` + +.. 列:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +### `strorempty` + +.. 列:: + +``` +**Regular expression applied**: `r"[^/]*"` +**Cast type**: `str` +**Example matches**: + +- `/path/to/Bob` +- `/path/to/Python%203` +- `/path/to/` + +Unlike the `str` path parameter type, `strorempty` can also match on an empty string path segment. + +*Added in v22.3* +``` + +.. 列:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +### `int` + +.. 列:: + +``` +**正規表現が適用されます**: `r"-?\d+"` +**キャストタイプ**: `int` +**一致例**: + +- `/path/to/10` +- `/path/to/-10` + +_float, hex, octal, etc_ +``` + +.. 列:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: int): + ... +``` +```` + +### `float` + +.. 列:: + +``` +**正規表現が適用されます**: `r"-?(?:\d+(?:\.\d*)?|\.\d+)"` +**キャストタイプ**: `float` +**マッチ例**: + +- `/path/to/10` +- `/path/to/-10` +- `/path/to/1.5` +``` + +.. 列:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: float): + ... +``` +```` + +### `alpha` + +.. 列:: + +``` +**正規表現が適用されました**: `r"[A-Za-z]+"` +**キャストタイプ**: `str` +**一致例**: + +- `/path/to/Bob` +- `/path/to/Python` + +_数字と一致しません。 またはスペースまたはその他の特殊文字_ +``` + +.. 列:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +### `slug` + +.. 列:: + +``` +**正規表現が適用されます**: `r"[a-z0-9]+(?:-[a-z0-9]+)*"` +**キャストタイプ**: `str` +**一致例**: + +- `/path/to/some-news-story` +- `/path/to/or-has-digits-123` + +*v21.6* に追加されました +``` + +.. 列:: + +```` +```python +@app.route("/path/to/") +async def handler(request, article: str): + ... +``` +```` + +### `path` + +.. 列:: + +``` +**正規表現が適用されます**: `r"[^/].*?"` +**キャストタイプ**: `str` +**一致例**: +- `/path/to/hello` +- `/path/to/hello.txt` +- `/path/to/hello/world.txt` +``` + +.. 列:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +.. 警告:: + +``` +これは `/` にマッチするためです。 `path`を使ってパターンをテストすれば、別の端点に向かってトラフィックを捕まえることができません。 さらに、このタイプの使い方に応じて、アプリケーションにパストラバーサルの脆弱性を作成する可能性があります。 これに対してエンドポイントを保護するのはあなたの仕事です。 でも必要な場合はコミュニティチャンネルでお気軽にお問い合わせください :) +``` + +### `ymd` + +.. 列:: + +``` +**Regular expression applied**: `r"^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))"` +**Cast type**: `datetime.date` +**Example matches**: + +- `/path/to/2021-03-28` +``` + +.. 列:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: datetime.date): + ... +``` +```` + +### `uuid` + +.. 列:: + +``` +**Regular expression applied**: `r"[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}"` +**Cast type**: `UUID` +**Example matches**: + +- `/path/to/123a123a-a12a-1a1a-a1a1-1a12a1a12345` +``` + +.. 列:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: UUID): + ... +``` +```` + +### ext + +.. 列:: + +``` +**正規表現が適用されました**: n/a +**キャストタイプ**: *varies* +**一致例**: +``` + +.. 列:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str, ext: str): + ... +``` +```` + +| 定義 | 例 | ファイル名 | 拡張 | +| ------------------------------------------------------------------------------------ | ----------------------------------------------------------- | -------- | ---------- | +| \ | page.txt | `"page"` | `"txt"` | +| \ | cat.jpg | `"cat"` | `"jpg"` | +| \ | cat.jpg | `"cat"` | `"jpg"` | +| | 123.txt | `123` | `"txt"` | +| | 123.svg | `123` | `"svg"` | +| | 3.14.tar.gz | `3.14` | `"tar.gz"` | + +ファイル拡張子は、特別なパラメータタイプ`ext`を使用して一致させることができます。 これは、ファイル名として他のタイプのパラメータを指定することができる特別な形式を使用します。 上の表に示されているように、1つまたは複数の特定の拡張子を指定します。 + +`path`パラメータ型は\*サポートしていません。 + +_v22.3_に追加されました + +### Regex + +.. 列:: + +``` +**Regular expression applied**: _whatever you insert_ +**Cast type**: `str` +**Example matches**: + +- `/path/to/2021-01-01` + +This gives you the freedom to define specific matching patterns for your use case. + +In the example shown, we are looking for a date that is in `YYYY-MM-DD` format. +``` + +.. 列:: + +```` +```python +@app.route(r"/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +### 正規表現の一致 + +多くの場合、複雑なルーティングと比較して、上記の例は単純すぎます。 我々は全く異なるルーティングマッチングパターンを使っています ここではRegexマッチングの高度な使い方を詳しく説明します + +ルートの一部をマッチさせたい場合もあります。 + +```text +/image/123456789.jpg +``` + +ファイルパターンを一致させたいが、数値部分だけをキャプチャしたい場合は、正規表現の楽しみ😄: + +```python +app.route(r"/image/\d+)\.jpg>") +``` + +さらに、これらはすべて許容されるべきです: + +```python +@app.get(r"/") # matching on the full pattern +@app.get(r"/") # defining a single matching group +@app.get(r"/[a-z]{3}).txt>") # defining a single named matching group +@app.get(r"/[a-z]{3}).(?:txt)>") # defining a single named matching group, with one or more non-matching groups +``` + +また、名前付きの一致グループを使用する場合は、セグメント ラベルと同じでなければなりません。 + +```python +@app.get(r"/\d+).jpg>") # OK +@app.get(r"/\d+).jpg>") # NOT OK +``` + +format@@0(https://docs.python.org/3/library/re.html) を参照してください。 + +## URLの生成 + +.. 列:: + +``` +Sanicはハンドラメソッド名`app.url_for()`に基づいてURLを生成するメソッドを提供しています。 これは、アプリケーション内のハードコーディングの url パスを避けたい場合に便利です。代わりに、ハンドラ名を参照するだけです。 +``` + +.. 列:: + +```` +```python +@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/') +async def post_handler(request, post_id): + ... +``` +```` + +.. 列:: + +``` +任意の数のキーワード引数を渡せます。 リクエストパラメータでないものは、クエリ文字列の一部として実装されます。 +``` + +.. 列:: + +```` +```python +assert app.url_for( + "post_handler", + post_id=5, + arg_one="one", + arg_two", +) == "/posts/5?arg_one=one&arg_two" +``` +```` + +.. 列:: + +``` +また、単一のクエリキーに複数の値を渡すこともサポートされています。 +``` + +.. 列:: + +```` +```python +assert app.url_for( + "post_handler", + post_id=5, + arg_one=["one", "two"], +) == "/posts/5?arg_one=one_one=two" +``` +```` + +### 特殊キーワード引数 + +詳細は API Docs を参照してください。 + +```python +app.url_for("post_handler", post_id=5, arg_one="one", _anchor="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) +# '//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) +# '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") +# 'http://another_server:8888/posts/5?arg_one=one&arg_one=two&arg_two=2#anchor' +``` + +### ルート名をカスタマイズ + +.. 列:: + +``` +route (ルート)を登録する際に `name` 引数を渡すことで、カスタムルート名を使用できます。 +``` + +.. 列:: + +```` +```python +@app.get("/get", name="get_handler") +def handler(request): + return text("OK") +``` +```` + +.. 列:: + +``` +このカスタム名を使用してURLを取得する +``` + +.. 列:: + +```` +```python +assert app.url_for("get_handler", foo="bar") == "/get?foo=bar" +``` +```` + +## Websockets routes + +.. 列:: + +``` +Websocket ルーティングは HTTP メソッドに似ています。 +``` + +.. 列:: + +```` +```python +async def handler(request, ws): + message = "Start" + while True: + await ws.send(message) + message = await ws.recv() + +app.add_websocket_route(handler, "/test") +``` +```` + +.. 列:: + +``` +それはまた、コンビニエンスデコレータを持っています。 +``` + +.. 列:: + +```` +```python +@app.websocket("/test") +async def handler(request, ws: + message = "Start" + while True: + await ws.send(message) + message = await ws.recv() +``` +```` + +動作の詳細については、[websockets section](/guide/advanced/websockets.md) をご覧ください。 + +## 厳密なスラッシュ + +.. 列:: + +``` +Sanic routes can be configured to strictly match on whether or not there is a trailing slash: `/`. This can be configured at a few levels and follows this order of precedence: + +1. Route +2. Blueprint +3. BlueprintGroup +4. Application +``` + +.. 列:: + +```` +```python +# provide default strict_slashes value for all routes +app = Sanic(__file__, strict_slashes=True) +``` + +```python +# overwrite strict_slashes value for specific route +@app.get("/get", strict_slashes=False) +def handler(request): + return text("OK") +``` + +```python +# it also works for blueprints +bp = Blueprint(__file__, strict_slashes=True) + +@bp.get("/bp/get", strict_slashes=False) +def handler(request): + return text("OK") +``` + +```python +bp1 = Blueprint(name="bp1", url_prefix="/bp1") +bp2 = Blueprint( + name="bp2", + url_prefix="/bp2", + strict_slashes=False, +) + +# This will enforce strict slashes check on the routes +# under bp1 but ignore bp2 as that has an explicitly +# set the strict slashes check to false +group = Blueprint.group([bp1, bp2], strict_slashes=True) +``` +```` + +## 静的ファイル + +.. 列:: + +``` +In order to serve static files from Sanic, use `app.static()`. + +The order of arguments is important: + +1. Route the files will be served from +2. Path to the files on the server + +See [API docs](https://sanic.readthedocs.io/en/stable/sanic/api/app.html#sanic.app.Sanic.static) for more details. +``` + +.. 列:: + +```` +```python +app.static("/static/", "/path/to/directory/") +``` +```` + +.. tip:: + +``` +一般的には、ディレクトリパスをスラッシュ(`/this/is/a/directory/`)で終わらせることをお勧めします。これにより、より明示的に曖昧さを削除します。 +``` + +.. 列:: + +``` +個々のファイルを提供することもできます。 +``` + +.. 列:: + +```` +```python +app.static("/", "/path/to/index.html") +``` +```` + +.. 列:: + +``` +エンドポイントに名前を付けることも時々役に立ちます +``` + +.. 列:: + +```` +```python +app.static( + "/user/uploads/", + "/path/to/uploads/", + name="uploads", +) +``` +```` + +.. 列:: + +``` +URL の取得はハンドラに似ていますが、ディレクトリ内に特定のファイルが必要な場合は、 `filename` 引数を追加することもできます。 +``` + +.. 列:: + +```` +```python +assert app.url_for( + "static", + name="static", + filename="file.txt", +) == "/static/file.txt" +``` +```python +assert app.url_for( + "static", + name="uploads", + filename="image.png", +) == "/user/uploads/image.png" + +``` +```` + +.. tip:: + +```` +複数の `static()`ルートを持つ場合は、手動で名前を付けることを提案されています。 これはバグを発見することが難しい可能性をほぼ確実に緩和します。 + +```python +app.static("/user/uploads/", "/path/to/uploads/", name="uploads") +app.static("/user/profile/", "/path/to/profile/", name="profile_pics") +``` +```` + +#### 自動インデックス作成 + +.. 列:: + +``` +indexページによって提供されるべき静的ファイルのディレクトリがある場合は、indexのファイル名を指定できます。 ディレクトリの URL に到達すると、インデックス ページが表示されます。 +``` + +.. 列:: + +```` +```python +app.static("/foo/", "/path/to/foo/", index="index="html") +``` +```` + +_V23.3_に追加されました + +#### ファイルブラウザー + +.. 列:: + +``` +静的ハンドラからディレクトリを提供する場合、Sanic は `directory_view=True` を使用して基本的なファイルブラウザーを表示するように設定することができます。 +``` + +.. 列:: + +```` +```python +app.static("/uploads/", "/path/to/dir", directory_view=True) +``` +```` + +ブラウザーにブラウザーが表示されるようになりました: + +![image](/assets/images/directory-view.png) + +_V23.3_に追加されました + +## ルートの説明 + +.. 列:: + +``` +route (ルート)が定義された場合、任意の数のキーワード引数を `ctx_` プレフィックスで追加できます。 これらの値はルート `ctx` オブジェクトに注入されます。 +``` + +.. 列:: + +```` +```python +@app.get("/1", ctx_label="something") +async def handler1(request): + ... + +@app.get("/2", ctx_label="something") +async def handler2(request): + ... + +@app.get("/99") +async def handler99(request): + ... + +@app.on_request +async def do_something(request): + if request.route.ctx.label == "something": + ... +``` +```` + +_v21.12_ に追加されました diff --git a/guide/content/ja/guide/basics/tasks.md b/guide/content/ja/guide/basics/tasks.md new file mode 100644 index 0000000000..68461f5642 --- /dev/null +++ b/guide/content/ja/guide/basics/tasks.md @@ -0,0 +1,166 @@ +--- +title: バックグラウンドタスク +--- + +# バックグラウンドタスク + +## タスクの作成 + +async Pythonで [tasks](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task) を利用するのはとても便利です。 Sanicは、現在の**実行中**ループにタスクを追加する便利な方法を提供します。 これは `asyncio.create_task` に似ています。 'App' ループが実行される前にタスクを追加する場合は、次のセクションを参照してください。 + +```python +async def notify_server_started_after_five_secondss(): + await asyncio.sleep(5) + print('Server successfully started!') + +app.add_task(notify_server_started_after_five_seconds()) +``` + +.. 列:: + +``` +Sanicは自動的にアプリを注入しようとし、タスクに引数として渡します。 +``` + +.. 列:: + +```` +```python +async def auto_inject(app): + await asyncio.sleep(5) + print(app.name) + +app.add_task(auto_inject) +``` +```` + +.. 列:: + +``` +もしくは、`app`引数を明示的に渡すこともできます。 +``` + +.. 列:: + +```` +```python +async def explicit_inject(app): + await asyncio.sleep(5) + print(app.name) + +app.add_task(explicit_inject(app)) +``` +```` + +## `app.run`の前にタスクを追加する + +`app.run`の前にバックグラウンドタスクを追加することができます。 アプリを実行する前にタスクを追加するには、コルーチンオブジェクトを渡さないことをお勧めします (すなわち。 `async`呼び出し可能を呼び出すことで作成されたものですが、代わりに呼び出し可能ファイルを渡すだけで、Sanicは**各ワーカー**にコルーチンオブジェクトを作成します。 注意: このように追加されたタスクは、 `before_server_start` ジョブとして実行されます。そのため、すべてのワーカーで実行されます (メインプロセスでは実行されません)。 これには一定の影響があります。詳細については、[このissue](https://github.com/sanic-org/sanic/issues/2139#issuecomment-868993668)の[このissue](https://github.com/sanic-org/sanic/issues/2139)をご覧ください。 + +メインプロセスで作業を追加するには、[`@app.main_process_start`](./listeners.md)に作業を追加することを検討してください。 注意: この作業が完了するまでワーカーは起動しません。 + +.. 列:: + +``` +`app.run`の前にタスクを追加する例 +``` + +.. 列:: + +```` +```python +async def slow_work(): + ... + +async def even_slower(num): + ... + +app = Sanic(...) +app.add_task(slow_work) # Note: we are passing the callable and not coroutine object ... +app.add_task(even_slower(10)) # ... or we can call the function and pass the coroutine. +app.run(...) +``` +```` + +## 名前付きタスク + +.. 列:: + +``` +タスクを作成するときは、`name`を指定することで、Sanicにそのタスクを追跡させることができます。 +``` + +.. 列:: + +```` +```python +app.add_task(slow_work, name="slow_task") +``` +```` + +.. 列:: + +``` +`get_task` を使用して、アプリケーションのどこからでもタスクインスタンスを取得できるようになりました。 +``` + +.. 列:: + +```` +```python +task = app.get_task("slow_task") +``` +```` + +.. 列:: + +``` +そのタスクをキャンセルする必要がある場合は、`cancel_task` でそれを行うことができます。`await` を確認してください。 +``` + +.. 列:: + +```` +```python +await app.cancel_task("slow_task") +``` +```` + +.. 列:: + +``` +登録されたすべてのタスクは `app.tasks` プロパティにあります。キャンセルされたタスクがいっぱいになるのを防ぐため、`app` を実行します。 完了またはキャンセルされたタスクをクリアする urge_tasks` 。 +``` + +.. 列:: + +```` +```python +app.purge_tasks() +``` +```` + +このパターンは `websockets` で特に役立ちます。 + +```python +async def receiver(ws): + while True: + message = await ws.recv() + if not message: + break + print(f"Received: {message}") + +@app.websocket("/feed") +async def feed(request, ws): + task_name = f"receiver:{request.id}" + request.app.add_task(receiver(ws), name=task_name) + try: + while True: + await request.app.event("my.custom.event") + await ws.send("A message") + finally: + # When the websocket closes, let's cleanup the task + await request.app.cancel_task(task_name) + request.app.purge_tasks() +``` + +_v21.12_ に追加されました diff --git a/guide/content/ja/guide/best-practices/blueprints.md b/guide/content/ja/guide/best-practices/blueprints.md new file mode 100644 index 0000000000..7f895bb7fb --- /dev/null +++ b/guide/content/ja/guide/best-practices/blueprints.md @@ -0,0 +1,521 @@ +# 建設計画 + +## 概要 + +設計図は、アプリケーション内のサブルーティングに使用できるオブジェクトです。 アプリケーションインスタンスにルートを追加する代わりに、blueprintsはルートを追加するための同様のメソッドを定義します。 柔軟でプラガブルな方法でアプリケーションに登録されます + +ブループリントは、アプリケーションのロジックをいくつかのグループまたは責任領域に分割することができる大規模なアプリケーションに特に便利です。 + +## 作成と登録 + +.. 列:: + +``` +まず、青写真を作成する必要があります。同じデコレータの多くを持つ`Sanic()`アプリインスタンスと非常によく似たAPIを持っています。 +``` + +.. 列:: + +```` +```python +# ./my_blueprint.py +from sanic.response import json +from sanic import Blueprint + +bp = Blueprint("my_blueprint") + +@bp.route("/") +async def bp_root(request): + return json({"my": "blueprint"}) +``` +```` + +.. 列:: + +``` +次に、アプリインスタンスに登録します。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic +from my_blueprint import bp + +app = Sanic(__name__) +app.blueprint(bp) +``` +```` + +ブループリントには、websocket()のデコレータとwebsocketを実装するための`add_websocket_route`メソッドもあります。 + +.. 列:: + +``` +v21.12 以降では、オブジェクトを追加する前後にブループリントを登録することができます。 以前は、登録時にブループリントに添付されたオブジェクトのみがアプリケーションインスタンスにロードされていました。 +``` + +.. 列:: + +```` +```python +app.blueprint(bp) + +@bp.route("/") +async def bp_root(request): + ... +``` +```` + +## コピー中 + +.. 列:: + +``` +ブループリントとそれに付随するすべてのものは、 `copy()`メソッドを使用して新しいインスタンスにコピーできます。 唯一必要な引数は新しい`name`を渡すことです。 ただし、これを使用して、古い設計図から任意の値を上書きすることもできます。 +``` + +.. 列:: + +```` +```python +v1 = Blueprint("Version1", version=1) + +@v1.route("/something") +def something(request): + pass + +v2 = v1.copy("Version2", version=2) + +app.blueprint(v1) +app.blueprint(v2) +``` + +``` +Available routes: +/v1/something +/v2/something + +``` +```` + +_v21.9_に追加されました + +## 建設計画グループ + +設計図は、リストやタプルの一部として登録することもできます。 登録者は再帰的に設計図の任意のサブシーケンスを循環し、それに応じて登録します。 ブループリント.groupメソッドは、このプロセスを簡素化するために提供されており、フロントエンドから見たものを模倣した「モック」バックエンドディレクトリ構造を可能にします。 これを考えてみましょう(かなり非対称な例) + +```text +api/ +├──content/ +│ ├──authors.py +│ ├──static.py +│ └──__init__.py +├──info.py +└──__init__.py +app.py +``` + +.. 列:: + +``` +#### 最初の建設計画 +``` + +.. 列:: + +```` +```python +# api/content/authors.py +from sanic import Blueprint + +author = Blueprint("content_authors", url_prefix="/authors") +``` +```` + +.. 列:: + +``` +#### 2番目の建設計画 +``` + +.. 列:: + +```` +```python +# api/content/static.py +from sanic import Blueprint + +static = Blueprint("content_static", url_prefix="/static") +``` +```` + +.. 列:: + +``` +#### ブループリントグループ +``` + +.. 列:: + +```` +```python +# api/content/__init__.py +from sanic import Blueprint +from .static import static +from .authors import authors + +content = Blueprint.group(static, authors, url_prefix="/content") +``` +```` + +.. 列:: + +``` +#### 3番目の建設計画 +``` + +.. 列:: + +```` +```python +# api/info.py +from sanic import Blueprint + +info = Blueprint("info", url_prefix="/info") +``` +```` + +.. 列:: + +``` +#### ブループリントグループ +``` + +.. 列:: + +```` +```python +# api/__init__.py +from sanic import Blueprint +from .content import content +from .info import info + +api = Blueprint.group(content, info, url_prefix="/api") +``` +```` + +.. 列:: + +``` +#### メインサーバー + +ブループリントがすべて登録されました +``` + +.. 列:: + +```` +```python +# app.py +from sanic import Sanic +from .api import api + +app = Sanic(__name__) +app.blueprint(api) +``` +```` + +### ブループリントグループのプレフィックスとコンポジション + +上のコードに示すように。 ブループリントのグループを作成するときは、`Blueprint に引数`url_prefix`を渡すことで、グループ内のすべてのブループリントのURLプレフィックスを拡張できます。 roup`メソッド。 これは、API のモックディレクトリ構造を作成するときに便利です。 + +さらに、`name_prefix`引数があり、設計図を再利用可能で構成可能にします。 これは、複数のグループに単一の設計図を適用するときに特に必要です。 これを行うことで、ブループリントは各グループごとに固有の名前で登録されます。 青写真を複数回登録しルートごとに固有の識別子で適切な名前を付けることができます + +.. 列:: + +``` +以下の例を考えてみましょう。 +- `TestApp.group-a_bp1.route1` +- `TestApp.group-a_bp2.route2` +- `TestApp.group-a_bp1.route1` +- `TestApp.group-bp2.route2` +``` + +.. 列:: + +```` +```python +bp1 = Blueprint("bp1", url_prefix="/bp1") +bp2 = Blueprint("bp2", url_prefix="/bp2") + +bp1.add_route(lambda _: ..., "/", name="route1") +bp2.add_route(lambda _: ..., "/", name="route2") + +group_a = Blueprint.group( + bp1, bp2, url_prefix="/group-a", name_prefix="group-a" +) +group_b = Blueprint.group( + bp1, bp2, url_prefix="/group-b", name_prefix="group-b" +) + +app = Sanic("TestApp") +app.blueprint(group_a) +app.blueprint(group_b) +``` +```` + +_v23.6_ にプレフィックスが追加されました + +## ミドルウェア + +.. 列:: + +``` +ブループリントはエンドポイントのみに特別に登録されているミドルウェアを持つこともできます。 +``` + +.. 列:: + +```` +```python +@bp.middleware +async def print_on_request(request): + print("I am a spy") + +@bp.middleware("request") +async def halt_request(request): + return text("I halted the request") + +@bp.middleware("response") +async def halt_response(request, response): + return text("I halted the response") +``` +```` + +.. 列:: + +``` +同様に、青写真グループを使用して、入れ子になった設計図のグループ全体にミドルウェアを適用することができます。 +``` + +.. 列:: + +```` +```python +bp1 = Blueprint("bp1", url_prefix="/bp1") +bp2 = Blueprint("bp2", url_prefix="/bp2") + +@bp1.middleware("request") +async def bp1_only_middleware(request): + print("applied on Blueprint : bp1 Only") + +@bp1.route("/") +async def bp1_route(request): + return text("bp1") + +@bp2.route("/") +async def bp2_route(request, param): + return text(param) + +group = Blueprint.group(bp1, bp2) + +@group.middleware("request") +async def group_middleware(request): + print("common middleware applied for both bp1 and bp2") + +# Register Blueprint group under the app +app.blueprint(group) +``` +```` + +## 例外 + +.. 列:: + +``` +他のformat@@0(./exception handling)と同様に、ブループリント固有のハンドラを定義できます。 +``` + +.. 列:: + +```` +```python +@bp.exception(NotFound) +def ignore_404s(request, exception): + return text("Yep, I find the page: {}".format(request.url)) +``` +```` + +## 静的ファイル + +.. 列:: + +``` +設計図は、独自の静的ハンドラを持つこともできます。 +``` + +.. 列:: + +```` +```python +bp = Blueprint("bp", url_prefix="/bp") +bp.static("/web/path", "/folder/to/serve") +bp.static("/web/path", "/folder/to/server", name="uploads") +``` +```` + +.. 列:: + +``` +これは `url_for()` を使用して取得できます。詳細は [routing](/guide/basics/routing.md) を参照してください。 +``` + +.. 列:: + +```` +```python +>>> print(app.url_for("static", name="bp.uploads", filename="file.txt")) +'/bp/web/path/file.txt' +``` +```` + +## リスナー + +.. 列:: + +``` +ブループリントは [listeners](/guide/basics/listeners.md) も実装できます。 +``` + +.. 列:: + +```` +```python +@bp.listener("before_server_start") +async def before_server_start(app, loop): + ... + +@bp.listener("after_server_stop") +async def after_server_stop(app, loop): + ... +``` +```` + +## Versioning + +[versioning section](/guide/advanced/version.md) で説明されているように、設計図は異なるバージョンの Web API を実装するために使用できます。 + +.. 列:: + +``` +`version`はルートの前に`/v1`または`/v2`などとなります。 +``` + +.. 列:: + +```` +```python +auth1 = ブループリント("auth", url_prefix="/auth", version=1) +auth2 = ブループリント("auth", url_prefix="/auth", version=2) +``` +```` + +.. 列:: + +``` +アプリにブループリントを登録すると、ルート`/v1/auth`と`/v2/auth`が個々のブループリントを示すようになります。 APIバージョンごとにサブサイトを作成することができます。 +``` + +.. 列:: + +```` +```python +from auth_blueprints import auth1, auth2 + +app = Sanic(__name__) +app.blueprint(auth1) +app.blueprint(auth2) +``` +```` + +.. 列:: + +``` +また、 `BlueprintGroup` エンティティの下で設計図をグループ化し、それらの複数を +同時にバージョン化することもできます。 +``` + +.. 列:: + +```` +```python +auth = Blueprint("auth", url_prefix="/auth") +metrics = Blueprint("metrics", url_prefix="/metrics") + +group = Blueprint.group(auth, metrics, version="v1") + +# This will provide APIs prefixed with the following URL path +# /v1/auth/ and /v1/metrics +``` +```` + +## 作成可能 + +`Blueprint` は複数のグループに登録することができ、`BlueprintGroup` 自体はそれぞれ登録して入れ子にすることができます。 これにより、無限の可能性をもたらす「設計図」構図が作成されます。 + +_V21.6_に追加されました + +.. 列:: + +``` +この例を見て、2つのハンドラが実際に5つの(5)の異なるルートとしてどのようにマウントされているかを見てください。 +``` + +.. 列:: + +```` +```python +app = Sanic(__name__) +blueprint_1 = Blueprint("blueprint_1", url_prefix="/bp1") +blueprint_2 = Blueprint("blueprint_2", url_prefix="/bp2") +group = Blueprint.group( + blueprint_1, + blueprint_2, + version=1, + version_prefix="/api/v", + url_prefix="/grouped", + strict_slashes=True, +) +primary = Blueprint.group(group, url_prefix="/primary") + +@blueprint_1.route("/") +def blueprint_1_default_route(request): + return text("BP1_OK") + +@blueprint_2.route("/") +def blueprint_2_default_route(request): + return text("BP2_OK") + +app.blueprint(group) +app.blueprint(primary) +app.blueprint(blueprint_1) + +# The mounted paths: +# /api/v1/grouped/bp1/ +# /api/v1/grouped/bp2/ +# /api/v1/primary/grouped/bp1 +# /api/v1/primary/grouped/bp2 +# /bp1 + +``` +```` + +## URLの生成 + +`url_for()` でURLを生成する場合、エンドポイント名は以下のようになります。 + +```text +{blueprint_name}.{handler_name} +``` diff --git a/guide/content/ja/guide/best-practices/decorators.md b/guide/content/ja/guide/best-practices/decorators.md new file mode 100644 index 0000000000..202ba251b6 --- /dev/null +++ b/guide/content/ja/guide/best-practices/decorators.md @@ -0,0 +1,195 @@ +# デコレーター + +一貫性のあるDRY Web APIを作成する最善の方法の1つは、デコレータを使用してハンドラから機能を削除することです。 再現できるようにするのです + +.. 列:: + +``` +したがって、その上に複数のデコレータを持つSanicビューハンドラを見ることは非常に一般的である。 +``` + +.. 列:: + +```` +```python +@app.get("/orders") +@authorized("view_order") +@validate_list_params() +@inject_user() +async def get_order_details(request, params, user): + ... +``` +```` + +## 例 + +ここにデコレータを作成するためのスターターテンプレートがあります。 + +この例では、ユーザーが特定のエンドポイントにアクセスする権限があることを確認しましょう。 ハンドラ関数をラップするデコレータを作成できます。 クライアントがリソースにアクセスする権限があるかどうかをリクエストをチェックし、適切なレスポンスを送信します。 + +```python +from functools import wraps +from sanic.response import json + +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 = await 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"}) +``` + +## テンプレート + +デコレータはSanicでアプリケーションを構築するための**基本**です。 これらはコードの移植性と保守性を高めます。 + +Pythonの禅を言い換えると、「[decorators] は素晴らしいアイデアです。 + +実装を容易にするために、ここでは、始めるためのコピー/貼り付け可能なコードの3つの例を紹介します。 + +.. 列:: + +``` +これらのインポート文を追加することを忘れないでください。 `@wraps`を使うと、関数のメタデータをそのまま保持することができます。format@@0(https://docs) ython.org/3/library/functtools.html#functools.wraps). また、ここでは`isawaitable`パターンを使用して、通常または非同期の関数でルートハンドラを使用できます。 +``` + +.. 列:: + +```` +```python +from inspect import isawaitable +from functtools import wraps +``` +```` + +### 引数あり + +.. 列:: + +```` +多くの場合、*常に*引数を必要とするデコレータが必要になります。そのため、実装された場合は常にそれを呼び出すことになります。 + +```python +@app.get("/") +@foobar(1, 2) +async def handler(request: Request): + return text("hi") +``` +```` + +.. 列:: + +```` +```python +def foobar(arg1, arg2): + def decorator(f): + @wraps(f) + async def decorated_function(request, *args, **kwargs): + + response = f(request, *args, **kwargs) + if isawaitable(response): + response = await response + + return response + + return decorated_function + + return decorator +``` +```` + +### 引数なし + +.. 列:: + +```` +Sometimes you want a decorator that will not take arguments. When this is the case, it is a nice convenience not to have to call it + +```python +@app.get("/") +@foobar +async def handler(request: Request): + return text("hi") +``` +```` + +.. 列:: + +```` +```python +def foobar(func): + def decorator(f): + @wraps(f) + async def decorated_function(request, *args, **kwargs): + + response = f(request, *args, **kwargs) + if isawaitable(response): + response = await response + + return response + + return decorated_function + + return decorator(func) +``` +```` + +### 引数の有無にかかわらず + +.. 列:: + +```` +If you want a decorator with the ability to be called or not, you can follow this pattern. Using keyword only arguments is not necessary, but might make implementation simpler. + +```python +@app.get("/") +@foobar(arg1=1, arg2=2) +async def handler(request: Request): + return text("hi") +``` + +```python +@app.get("/") +@foobar +async def handler(request: Request): + return text("hi") +``` +```` + +.. 列:: + +```` +```python +def foobar(maybe_func=None, *, arg1=None, arg2=None): + def decorator(f): + @wraps(f) + async def decorated_function(request, *args, **kwargs): + + response = f(request, *args, **kwargs) + if isawaitable(response): + response = await response + + return response + + return decorated_function + + return decorator(maybe_func) if maybe_func else decorator +``` +```` diff --git a/guide/content/ja/guide/best-practices/exceptions.md b/guide/content/ja/guide/best-practices/exceptions.md new file mode 100644 index 0000000000..b1d3c635fc --- /dev/null +++ b/guide/content/ja/guide/best-practices/exceptions.md @@ -0,0 +1,636 @@ +# 例外 + +## サニック例外の使用 + +場合によっては、ハンドラの実行を停止し、ステータスコード応答を返すようSanicに指示する必要があります。 これに対して「SanicException」を発生させることができ、Sanicはあなたのために残りを行います。 + +オプションの `status_code` 引数を渡すことができます。 デフォルトでは、SanicExceptionは内部サーバーエラー500レスポンスを返します。 + +```python +from sanic.exception import SanicException + +@app.route("/youshallnotpass") +async def no_no(request): + raise SanicException("Something went wrong .", status_code=501) +``` + +Sanicは多くの標準的な例外を提供します。 それぞれが自動的にレスポンス内に適切なHTTPステータスコードを発生させます。 詳細については、format@@0(https://sanic.readthedocs.io/en/latest/sanic/api_reference.html#module-sanic.exceptions) を参照してください。 + +.. 列:: + +``` +より一般的な例外は、あなた自身を実装すべきである。 + +- `BadRequest` (400) +- `Unauthorized` (401) +- `Forbidden` (403) +- `NotFound` (404) +- `ServerError` (500) +``` + +.. 列:: + +```` +```python +from sanic import exceptions + +@app.route("/login") +async def login(request): + user = await some_login_func(request) + if not user: + raise exceptions.NotFound( + f"Could not find user with username={request.json.username}" + ) +``` +```` + +## 例外のプロパティ + +Sanic のすべての例外は `SanicException` に由来します。 そのクラスには、開発者がアプリケーション全体で例外を一貫して報告するのに役立ついくつかのプロパティがあります。 + +- `message` +- `status_code` +- `quiet` +- `headers` +- `context` +- `extra` + +これらのプロパティはすべて、作成時に例外に渡すことができます。 最初の3つはクラス変数としても使えます + +.. 列:: + +``` +### `message` + +`message` プロパティは、Pythonの他の例外と同じように表示されるメッセージを明らかに制御します。 特に便利なのは、`message` プロパティをクラス定義に設定することで、アプリケーション全体で言語を簡単に標準化できます。 +``` + +.. 列:: + +```` +```python +class CustomError(SanicException): + message = "Something bad happened" + +raise CustomError +# or +raise CustomError("Override the default message with something else") +``` +```` + +.. 列:: + +``` +### `status_code` + +このプロパティは、例外が発生したときに応答コードを設定するために使用されます。 これは、通常、クライアントからの不正な情報に応答しているカスタム400シリーズの例外を作成する場合に特に便利です。 +``` + +.. 列:: + +```` +```python +class TeapotError(SanicException): + status_code = 418 + message = "Sorry, I cannot brew coffee" + +raise TeapotError +# or +raise TeapotError(status_code=400) +``` +```` + +.. 列:: + +``` +### `quiet` + +デフォルトでは、Sanic が `error_logger` に例外を出力します。 場合によっては、これは望ましくない場合があります。特に例外を使用して例外ハンドラでイベントをトリガーする場合は (format@@0を参照してください)。 exceptions.md#handling)) `quiet=True` を使ってログ出力を抑制することができます。 +``` + +.. 列:: + +```` +```python +class SilentError(SanicException): + message = "Something happened, but not shown in logs" + quiet = True + +raise SilentError +# or +raise InvalidUsage("blah blah", quiet=True) +``` +```` + +.. 列:: + +``` +デバッグ中に`quiet=True`プロパティをグローバルに無視したい場合があります。 `NOISY_EXCEPTIONS` + +*バージョン21.12*に追加された、このプロパティに関係なく、Sanicにすべての例外を強制的にログアウトさせることができます +``` + +.. 列:: + +```` +```python +app.config.NOISY_EXCEPTIONS = True +``` +```` + +.. 列:: + +``` +### `headers` + +`SanicException` を使ってレスポンスを作成するのはとても強力です。 これは、 `status_code` を制御するだけでなく、 reponse ヘッダを例外から直接制御することもできるからです。 +``` + +.. 列:: + +```` +```python +class MyException(SanicException): + headers = { + "X-Foo": "bar" + } + +raise MyException +# or +raise InvalidUsage("blah blah", headers={ + "X-Foo": "bar" +}) +``` +```` + +.. 列:: + +``` +### `extra` + +See [contextual exceptions](./exceptions.md#contextual-exceptions) + +*Added in v21.12* +``` + +.. 列:: + +```` +```python +raise SanicException(..., extra={"name": "Adam"}) +``` +```` + +.. 列:: + +``` +### `context` + +See [contextual exceptions](./exceptions.md#contextual-exceptions) + +*Added in v21.12* +``` + +.. 列:: + +```` +```python +raise SanicException(..., context={"foo": "bar"}) +``` +```` + +## 取扱い方法 + +Sanicはエラーページをレンダリングすることで自動的に例外を処理するため、多くの場合、自分で処理する必要はありません。 ただし、例外が発生したときに何をすべきかをもっと制御したい場合は、ハンドラを自分で実装することができます。 + +Sanicはこのためのデコレータを提供します。これはSanic標準の例外だけでなく、アプリケーションが投げるかもしれない**any**例外にも適用されます。 + +.. 列:: + +``` +ハンドラを追加する最も簡単な方法は、 `@app.exception()` を使用して1つ以上の例外を渡すことです。 +``` + +.. 列:: + +```` +```python +from sanic.exceptionimport NotFound + +@app.exception(NotFound, SomeCustomException) +async def ignore_404s(request, exception): + return text("Yep, I find the page: {}".format(request.url)) +``` +```` + +.. 列:: + +``` +`Exception` をキャッチすることで、キャッチオールハンドラを作成することもできます。 +``` + +.. 列:: + +```` +```python +@app.exception(Exception) +async def catch_anything(request, exception): + ... +``` +```` + +.. 列:: + +``` +`app.error_handler.add()`を使ってエラーハンドラを追加することもできます。 +``` + +.. 列:: + +```` +```python +async def server_error_handler(request, exception): + return text("Oops, server error", status=500) + +app.error_handler.add(Exception, server_error_handler) +``` +```` + +## ビルトインエラー処理 + +Sanic は、HTML、JSON、およびテキストの3つのフォーマットを除外して出荷します。 以下の例は format@@0(#fallback-handler) セクションにあります。 + +.. 列:: + +``` +`error_format` キーワード引数でどの形式を使用するかを _per route_ で制御できます。 + +*v21.9* で追加しました +``` + +.. 列:: + +```` +```python +@app.request("/", error_format="text") +async def handler(request): + ... +``` +```` + +## カスタムエラーの処理 + +場合によっては、デフォルトで提供されるエラー処理機能を追加したい場合があります。 その場合、Sanic のデフォルトエラーハンドラを次のようにサブクラス化できます。 + +```python +from sanic.handlers import ErrorHandler + +class CustomErrorHandler(ErrorHandler): + def default(self, request: Request, exception: Exception) -> HTTPResponse: + ''' handles errors that have no error handlers assigned ''' + # You custom error handling logic... + status_code = getattr(exception, "status_code", 500) + return json({ + "error": str(exception), + "foo": "bar" + }, status=status_code) + +app.error_handler = CustomErrorHandler() +``` + +## Fallback handler + +Sanic には 3 つのフォールバック例外ハンドラが付属しています: + +1. HTML +2. テキスト +3. JSON + +これらのハンドラは、アプリケーションが format@@0(/guide/deployment/development.md) かどうかによって詳細のレベルが異なります。 + +デフォルトでは、Sanicは「自動」モードになります。 つまり、受信リクエストと潜在的なマッチングハンドラを使用して、適切な応答形式を選択するということです。 例えば、ブラウザでは常にHTMLエラーページを提供する必要があります。 curl を使用すると、JSON またはプレーンテキストが表示されることがあります。 + +### HTML + +```python +app.config.FALLBACK_ERROR_FORMAT = "html" +``` + +.. 列:: + +```` +```python +app.config.DEBUG = True +``` + +![Error](/assets/images/error-display-html-debug.png) +```` + +.. 列:: + +```` +```python +app.config.DEBUG = False +``` + +![Error](/assets/images/error-display-html-prod.png) +```` + +### テキスト + +```python +app.config.FALLBACK_ERROR_FORMAT = "text" +``` + +.. 列:: + +```` +```python +app.config.DEBUG = True +``` + +```sh +curl localhost:8000/exc -i +HTTP/1.1 500 Internal Server Error +content-length: 620 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +⚠️ 500 — Internal Server Error +============================== +That time when that thing broke that other thing? That happened. + +ServerError: That time when that thing broke that other thing? That happened. while handling path /exc +Traceback of TestApp (most recent call last): + + ServerError: That time when that thing broke that other thing? That happened. + File /path/to/sanic/app.py, line 979, in handle_request + response = await response + + File /path/to/server.py, line 16, in handler + do_something(cause_error=True) + + File /path/to/something.py, line 9, in do_something + raise ServerError( +``` +```` + +.. 列:: + +```` +```python +app.config.DEBUG = False +``` + +```sh +curl localhost:8000/exc -i +HTTP/1.1 500 Internal Server Error +content-length: 134 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +⚠️ 500 — Internal Server Error +============================== +That time when that thing broke that other thing? That happened. +``` +```` + +### JSON + +```python +app.config.FALLBACK_ERROR_FORMAT = "json" +``` + +.. 列:: + +```` +```python +app.config.DEBUG = True +``` + +```sh +curl localhost:8000/exc -i +HTTP/1.1 500 Internal Server Error +content-length: 572 +connection: keep-alive +content-type: application/jso + +{ + "description": "Internal Server Error", + "status": 500, + "message": "That time when that thing broke that other thing? That happened.", + "path": "/exc", + "args": {}, + "exceptions": [ + { + "type": "ServerError", + "exception": "That time when that thing broke that other thing? That happened.", + "frames": [ + { + "file": "/path/to/sanic/app.py", + "line": 979, + "name": "handle_request", + "src": "response = await response" + }, + { + "file": "/path/to/server.py", + "line": 16, + "name": "handler", + "src": "do_something(cause_error=True)" + }, + { + "file": "/path/to/something.py", + "line": 9, + "name": "do_something", + "src": "raise ServerError(" + } + ] + } + ] +} +``` +```` + +.. 列:: + +```` +```python +app.config.DEBUG = False +``` + +```sh +curl localhost:8000/exc -i +HTTP/1.1 500 Internal Server Error +content-length: 129 +connection: keep-alive +content-type: application/json + +{ + "description": "Internal Server Error", + "status": 500, + "message": "That time when that thing broke that other thing? That happened." +} + +``` +```` + +### 自動 + +Sanicはまた、どのフォールバックオプションを使用するかを推測するためのオプションも提供します。 + +```python +app.config.FALLBACK_ERROR_FORMAT = "auto" +``` + +## 文脈の例外 + +アプリケーション全体で例外を一貫して発生させる機能を簡素化するデフォルトの例外メッセージ。 + +```python +class TeapotError(SanicException): + status_code = 418 + message = "Sorry, I cannot brew coffee" + +raise TeapotError +``` + +しかし、これには二つのことが欠けています: + +1. 動的で予測可能なメッセージ形式 +2. エラーメッセージにコンテキストを追加する機能 (詳細は後で) + +_v21.12_ に追加されました + +Sanicの例外のいずれかを使用すると、実行時に追加の詳細を提供する2つのオプションがあります。 + +```python +raise TeapotError(extra={"foo": "bar"}, context={"foo": "bar"}) +``` + +違いは何であり、いつそれぞれを使用することに決めるべきか。 + +- `extra` - オブジェクト自体はプロダクションクライアントに **決して**送られません\*\* 。 内部使用のみを目的としています。 それは何のために使用することができますか? + - 1分後に表示されるように、動的なエラーメッセージを生成します + - ロガーにランタイムの詳細を提供する + - デバッグ情報(開発モードの場合はレンダリングされます) +- `context` - このオブジェクトは常にプロダクションクライアントに送信されます。 これは一般的に、何が起こったのかのコンテキストについての追加の詳細を送信するために使用されます。 それは何のために使用することができますか? + - `BadRequest`検証問題に代替値を提供します + - お客様がサポートチケットを開くために役立つ詳細を記載して回答する + - 現在ログインしているユーザー情報のような状態情報を表示します + +### `extra`を使った動的で予測可能なメッセージ + +サニック例外は、`extra`キーワード引数を使って発生した例外インスタンスに追加情報を提供することができます。 + +```python +class TeapotError(SanicException): + status_code = 418 + + @property + def message(self): + return f"Sorry {self.extra['name']}, I cannot make you coffee" + +raise TeapotError(extra={"name": "Adam"}) +``` + +新機能では、例外インスタンスに `extra` メタを渡すことができます。 これは、上記の例のように、動的データをメッセージテキストに渡すのに特に役立ちます。 この`extra` infoオブジェクトは `PRODUCTION` モードでは抑制されますが、 `DEVELOPMENT` モードでは表示されます。 + +.. 列:: + +``` +**開発版** + +![image](~@assets/images/error-extra-debug.png) +``` + +.. 列:: + +``` +**製品** + +![image](~@assets/images/error-extra-prod.png) +``` + +### エラーメッセージに追加の `コンテキスト` + +サニック例外は、何が起こったかについてユーザに意図した情報を渡すための引数`context`でも発生します。 これは、マイクロサービスやエラーメッセージを JSON 形式で渡すことを意図した API を作成する場合に特に便利です。 このユースケースでは、パース可能なエラーメッセージ以外にも、クライアントに詳細を返すコンテキストを持たせたいと考えています。 + +```python +raise TeapotError(context={"foo": "bar"}) +``` + +この情報は **私たちが常にエラーで渡したい** です(利用可能な場合)。 次のようにすべきです: + +.. 列:: + +```` +**PRODUCTION** + +```json +{ + "description": "I'm a teapot", + "status": 418, + "message": "Sorry Adam, I cannot make you coffee", + "context": { + "foo": "bar" + } +} +``` +```` + +.. 列:: + +```` +**DEVELOPMENT** + +```json +{ + "description": "I'm a teapot", + "status": 418, + "message": "Sorry Adam, I cannot make you coffee", + "context": { + "foo": "bar" + }, + "path": "/", + "args": {}, + "exceptions": [ + { + "type": "TeapotError", + "exception": "Sorry Adam, I cannot make you coffee", + "frames": [ + { + "file": "handle_request", + "line": 83, + "name": "handle_request", + "src": "" + }, + { + "file": "/tmp/p.py", + "line": 17, + "name": "handler", + "src": "raise TeapotError(" + } + ] + } + ] +} +``` +```` + +## エラー報告 + +Sanic には [signal](../advanced/signals.md#built-in-signals) があり、例外報告プロセスにフックすることができます。 これは、Sentry や Rollbar のようなサードパーティのサービスに例外情報を送信したい場合に便利です。 これは、以下のようにエラー報告ハンドラを付けることで便利に実現できます。 + +```python +@app.report_exception +async def catch_any_exception(app: Sanic, exception: Exception): +print("Caught exception:) +``` + +.. note:: + +``` +このハンドラはバックグラウンドタスクにディスパッチされ、任意のレスポンスデータを操作するために使用される**IS NOT** です。 ログまたはレポートの目的でのみ使用することを意図しています。 クライアントにエラー・レスポンスを返す能力には影響を与えるべきではありません +``` + +_V23.6_に追加されました diff --git a/guide/content/ja/guide/best-practices/logging.md b/guide/content/ja/guide/best-practices/logging.md new file mode 100644 index 0000000000..052be98a3f --- /dev/null +++ b/guide/content/ja/guide/best-practices/logging.md @@ -0,0 +1,218 @@ +# ログ + +Sanicはformat@@0(https://docs.python.org/3/howto/logging.html)に基づいてリクエストの異なるタイプのログ(アクセスログ、エラーログ)を行うことができます。 新しい設定を作成したい場合は、Pythonロギングに関する基本的な知識を持っている必要があります。 + +しかし、Sanicは、箱の中からいくつかの賢明なロギングデフォルトで出荷されます。 デバッグモードであるかどうかに応じてログをフォーマットする `AutoFormatter` を使用します。 これを後で強制する方法をお見せします。 + +## クイックスタート + +まずは、ローカル開発におけるロギングの様子を見てみましょう。 このために、Sanicが提供するデフォルトのロギング設定を使用し、開発モードでSanicを実行するようにします。 + +.. 列:: + +``` +デフォルト設定を使用した簡単な例は次のようになります。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic +from sanic.log import logger +from sanic.response import text + +app = Sanic('logging_example') + +@app.route('/') +async def test(request): + logger.info('Here is your log') + return text('Hello World!') +``` +```` + +.. 列:: + +``` +開発ログを確認しようとしているので、Sanicを必ず開発モードで実行します。 +``` + +.. 列:: + +```` +```sh +sanic path.to.server:app --dev +``` +```` + +サーバーが実行されると、このようなログが表示されます。 + +![Sanic Logging Start](/assets/images/logging-debug-start.png) + +サーバーにリクエストを送信すると、ログメッセージが出力されます。 + +![Sanic Logging Access](/assets/images/logging-debug-access.png) + +注意すべきいくつかの重要なポイント: + +- **production** モードのデフォルトのログレベルは `INFO` です。 +- **debug** モードのデフォルトのログレベルは `DEBUG` です。 +- **debug** モードの場合、ログメッセージにタイムスタンプはありません(アクセスログを除く)。 +- Sanicはターミナルがそれをサポートしている場合、ログを色付けしようとします。 Docker で docker-compose を実行している場合は、`docker-compose.yml` ファイルに `tty: true` を設定して色を確認する必要があります。 + +## Sanicのロガー + +サニック号には5つのロガーが搭載されています。 + +| **Logger Name** | **ユースケース** | +| ------------------ | ------------------------ | +| `sanic.root` | 内部メッセージのログに使用されます。 | +| `sanic.error` | エラーログを記録するために使用されます。 | +| `sanic.access` | アクセスログを記録するために使用されます。 | +| `sanic.server` | サーバログのログに使用します。 | +| `sanic.websockets` | ウェブソケットのログを記録するために使用します。 | + +.. 列:: + +``` +これらのロガーを自分で使いたい場合は、 `sanic.log` からインポートできます。 +``` + +.. 列:: + +```` +```python +from sanic.log import logger, error_logger, access_logger, server_logger, websockets_logger + +logger.info('This is a root logger message') +``` +```` + +.. 警告:: + +``` +ルートロガーとエラーロガーを自由に使用してください。 ただし、アクセスロガー、サーバーロガー、またはwebsocketsロガーを直接使用したくない場合があります。 これらはSanicによって内部で使用され、特定の方法でログインするように設定されています。 これらのロガーのログの方法を変更したい場合は、ログの設定を変更する必要があります。 +``` + +## デフォルトのログ設定 + +Sanic は、独自のロギング設定を提供しない場合に使用されるデフォルトのロギング設定を持っています。 この設定は `sanic.log.LOGGING_CONFIG_DEFAULTS` に保存されます。 + +```python +{ + 'version': 1, + 'disable_existing_loggers': False, + 'loggers': { + 'sanic.root': {'level': 'INFO', 'handlers': ['console']}, + 'sanic.error': { + 'level': 'INFO', + 'handlers': ['error_console'], + 'propagate': True, + 'qualname': 'sanic.error' + }, + 'sanic.access': { + 'level': 'INFO', + 'handlers': ['access_console'], + 'propagate': True, + 'qualname': 'sanic.access' + }, + 'sanic.server': { + 'level': 'INFO', + 'handlers': ['console'], + 'propagate': True, + 'qualname': 'sanic.server' + }, + 'sanic.websockets': { + 'level': 'INFO', + 'handlers': ['console'], + 'propagate': True, + 'qualname': 'sanic.websockets' + } + }, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'formatter': 'generic', + 'stream': sys.stdout + }, + 'error_console': { + 'class': 'logging.StreamHandler', + 'formatter': 'generic', + 'stream': sys.stderr + }, + 'access_console': { + 'class': 'logging.StreamHandler', + 'formatter': 'access', + 'stream': sys.stdout + } + }, + 'formatters': { + 'generic': {'class': 'sanic.logging.formatter.AutoFormatter'}, + 'access': {'class': 'sanic.logging.formatter.AutoAccessFormatter'} + } +} +``` + +## サイニックロガーの変更 + +.. 列:: + +``` +独自のロギング設定を使用するには、`logging.config.dictConfig` を使用するか、Sanic アプリを初期化する際に `log_config` を使用します。 +``` + +.. 列:: + +```` +```python +app = Sanic('logging_example', log_config=LOGGING_CONFIG) + +if __name__ == "__main__": + app.run(access_log=False) +``` +```` + +.. 列:: + +``` +しかし、ロギングを完全に制御したくない場合は、例えばフォーマッタを変更してください。 ここでは、デフォルトのロギング設定をインポートし、常に`ProdFormatter`を使用するように強制する部分(例えば)のみを変更します。 +``` + +.. 列:: + +```` +```python +from sanic.log import LOGGING_CONFIG_DEFAULTS + +LOGGING_CONFIG_DEFAULTS['formatters']['generic']['class'] = 'sanic.logging.formatter.ProdFormatter' +LOGGING_CONFIG_DEFAULTS['formatters']['access']['class'] = 'sanic.logging.formatter.ProdAccessFormatter' + +app = Sanic('logging_example', log_config=LOGGING_CONFIG_DEFAULTS) +``` +```` + +.. tip:: FYI + +``` +Logging in Python is a relatively cheap operation. However, if you are serving a high number of requests and performance is a concern, all of that time logging out access logs adds up and becomes quite expensive. + +This is a good opportunity to place Sanic behind a proxy (like nginx) and to do your access logging there. You will see a *significant* increase in overall performance by disabling the `access_log`. + +For optimal production performance, it is advised to run Sanic with `debug` and `access_log` disabled: `app.run(debug=False, access_log=False)` +``` + +## アクセスロガーの追加パラメータ + +Sanicはアクセスロガーに追加のパラメータを提供します。 + +| ログのコンテキストパラメータ | パラメータの値 | Datatype | +| -------------- | ------------------------------------ | -------- | +| `host` | `request.ip` | `str` | +| `request` | `request.method + " " + request.url` | `str` | +| `status` | `response` | `int` | +| `byte` | `len(response.body)` | `int` | +| `duration` | | `float` | + +## レガシーログ + +Sanic 24.3では多くのロギング変更が導入されました。 主な変更点は、ロギングフォーマットに関連していました。 従来のログ形式を使用する場合は、`sanic.logging.formatter.LegacyFormatter.LegacyFormatter` と `sanic.logging.formatter.LegacyAccessFormatter` 形式を使用します。 diff --git a/guide/content/ja/guide/best-practices/testing.md b/guide/content/ja/guide/best-practices/testing.md new file mode 100644 index 0000000000..fbc56f3f63 --- /dev/null +++ b/guide/content/ja/guide/best-practices/testing.md @@ -0,0 +1,3 @@ +# テスト + +参照: [sanic-testing](../../plugins/sanic-testing/getting-started.md) diff --git a/guide/content/ja/guide/deployment/caddy.md b/guide/content/ja/guide/deployment/caddy.md new file mode 100644 index 0000000000..d1406f75b0 --- /dev/null +++ b/guide/content/ja/guide/deployment/caddy.md @@ -0,0 +1,74 @@ +# キャディデプロイメント + +## はじめに + +CaddyはHTTP/3までサポートする最先端のWebサーバーとプロキシです。 そのシンプルさは、最小限の設定と、Let's Encrypt からドメイン用の TLS 証明書を自動的に調達するための構築機能にあります。 この設定では、127.0.0でローカルで動作するようにSanicアプリケーションを設定します。 :8001, Caddy がドメインの example.com の公開サーバーの役割を果たしています。 + +Windows、Linux、Macでお気に入りのパッケージメニューからCaddyをインストールできます。 パッケージ名は `caddy` です。 + +## プロキシされたサニックアプリ + +```python +from sanic import Sanic +from sanic.response import text + +app = Sanic("proxied_example") + +@app.get("/") +def index(request): + # This should display external (public) addresses: + return text( + f"{request.remote_addr} connected to {request.url_for('index')}\n" + f"Forwarded: {request.forwarded}\n" + ) +``` + +このアプリケーションを実行するには、`proxied_example.py` として保存し、sanic コマンドラインインターフェイスを以下のように使用します。 + +```bash +SANIC_PROXIES_COUNT=1 sanic proxied_example --port 8001 +``` + +SANIC_PROXIES_COUNT環境変数を設定すると、SanicはCaddyから送信されたX-Forward-\*ヘッダを信頼するように命令します。 クライアントのIPアドレスやその他の情報を正しく識別できるようにします。 + +## キャディはシンプルです + +他の Web サーバーが動作していない場合は、Caddy CLI を実行できます(Linux では `sudo` が必要です)。 + +```bash +caddy リバース・プロキシ --from example.com --to :8001 +``` + +これはあなたのドメインの証明書、http-to-https リダイレクト、プロキシヘッダ、ストリーミング、WebSocketを含む完全なサーバーです。 Sanicアプリケーションは、HTTPバージョン1、2、3で指定されたドメインで利用可能になるはずです。 H3通信を有効にするには、ファイアウォールでUDP/443 を開いてください。 + +すべて完了しましたか? + +すぐに、複数のサーバーが必要になります。または、設定ファイルが入ってくる詳細を制御する必要があります。 上記のコマンドは `Caddyfile` と同等で、インストールの良い開始点として機能します。 + +``` +example.com { + reverse_proxy localhost:8001 +} +``` + +Linuxディストリビューションによっては、`/etc/caddy/Caddy/Caddy/Caddyfile` から設定を読み込むようにインストールされているものもあります。これは `import /etc/caddy/conf.d/*` です。 そうでない場合は、 `caddy run` をシステムサービスとして手動で実行し、適切な設定ファイルを指す必要があります。 もしくは、永続的な設定変更には、 `caddy run --resume` を使用して Caddy API モードを使用してください。 Caddyfile の読み込みはすべての設定を置き換えるため、 `caddy-api` は従来の方法では設定できません。 + +## 高度な構成 + +時には、静的なファイルとハンドラをサイトルートで混在させ、よりクリーンな URL を得る必要があるかもしれません。 Sanicでは、\`app.static("/", "static", index="index.html")を使用します。 ただし、パフォーマンスを向上させるために、静的ファイルをCaddyにオフロードすることができます。 + +``` +app.example.com { + # Look for static files first, proxy to Sanic if not found + route { + file_server { + root /srv/sanicexample/static + precompress br # brotli your large scripts and styles + pass_thru + } + reverse_proxy unix//tmp/sanic.socket # sanic --unix /tmp/sanic.socket + } +} +``` + +詳細については、format@@0(https://caddyserver.com/docs/)を参照してください。 diff --git a/guide/content/ja/guide/deployment/docker.md b/guide/content/ja/guide/deployment/docker.md new file mode 100644 index 0000000000..c115fa5753 --- /dev/null +++ b/guide/content/ja/guide/deployment/docker.md @@ -0,0 +1,199 @@ +# Docker Deployment + +## はじめに + +長い間、環境はデプロイにとって困難な問題でした。 プロジェクトに矛盾する構成がある場合は、それらの解決に多くの時間を費やす必要があります。 幸いなことに、仮想化は私たちに良い解決策を提供します。 Dockerはその一つです。 Dockerを知らない場合は、format@@0(https://www.docker.com/)をご覧ください。 + +## ビルド画像 + +簡単なプロジェクトから始めましょう 例として、Sanicプロジェクトを使用します。 プロジェクトのパスは `/path/to/SanicDocker` であるとします。 + +.. 列:: + +``` +ディレクトリ構造は以下のようになります: +``` + +.. 列:: + +```` +```text +# /path/to/SanicDocker +SanicDocker +├── requirements.txt +├── dockerfile +├── server.py +``` +```` + +.. 列:: + +``` +`server.py`のコードは次のようになります。 +``` + +.. 列:: + +```` +```python +app = Sanic("MySanicApp") + +@app.get('/') +async def hello(request): + return text("OK!") + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=8000) +``` +```` + +.. note:: + +``` +ホストは 127.0.0.1 ではありません。docker container, 127.0.0. は、コンテナのデフォルトのネットワークインターフェイスです。コンテナだけが他のコンテナと通信できます。 詳細はformat@@0(https://docs.docker.com/engine/reference/commandline/network/)をご覧ください。 +``` + +コードの準備ができました。`Dockerfile`を書きましょう。 + +```Dockerfile + +FROM sanicframework/sanic:3.8-latest + +WORKDIR /sanic + +COPY . . + +RUN pip install -r requirements.txt + +EXPOSE 8000 + +CMD ["python", "server.py"] +``` + +次のコマンドを実行してイメージをビルドします。 + +```shell +docker build -t my-sanic-image . +``` + +## コンテナを起動 + +.. 列:: + +``` +イメージ構築後、コンテナは `my-sanic-image` を使用します。 +``` + +.. 列:: + +```` +```shell +docker run --name mysanic -p 8000:8000 -d my-sanic-image +``` +```` + +.. 列:: + +``` +`http://localhost:8000`で結果を確認できます。 +``` + +.. 列:: + +```` +```text +OK! +``` +```` + +## docker-compose を使用 + +プロジェクトが複数のサービスで構成されている場合は、 [docker-compose](https://docs.docker.com/compose/) を使用して管理できます。 + +例えば、`my-sanic-image` と `nginx` をデプロイし、nginx アクセスの sanic サーバーを使用して実現します。 + +.. 列:: + +``` +まず、nginxの設定ファイルを準備する必要があります。`mysanic.conf`という名前のファイルを作成します。 +``` + +.. 列:: + +```` +```nginx +server { + listen 80; + listen [::]:80; + location / { + proxy_pass http://mysanic:8000/; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection upgrade; + proxy_set_header Accept-Encoding gzip; + } +} +``` +```` + +.. 列:: + +``` +次に、`docker-compose.yml` ファイルを準備する必要があります。内容は以下のとおりです: +``` + +.. 列:: + +```` +```yaml +version: "3" + +services: + mysanic: + image: my-sanic-image + ports: + - "8000:8000" + restart: always + + mynginx: + image: nginx:1.13.6-alpine + ports: + - "80:80" + depends_on: + - mysanic + volumes: + - ./mysanic.conf:/etc/nginx/conf.d/mysanic.conf + restart: always + +networks: + default: + driver: bridge +``` +```` + +.. 列:: + +``` +その後、次のように開始できます。 +``` + +.. 列:: + +```` +```shell +docker-compose up -d +``` +```` + +.. 列:: + +``` +`http://localhost:80`で結果を確認できます。 +``` + +.. 列:: + +```` +```text +OK! +``` +```` diff --git a/guide/content/ja/guide/deployment/kubernetes.md b/guide/content/ja/guide/deployment/kubernetes.md new file mode 100644 index 0000000000..8d6340de1c --- /dev/null +++ b/guide/content/ja/guide/deployment/kubernetes.md @@ -0,0 +1 @@ +# Kubernetes diff --git a/guide/content/ja/guide/deployment/nginx.md b/guide/content/ja/guide/deployment/nginx.md new file mode 100644 index 0000000000..7c7b26e5ae --- /dev/null +++ b/guide/content/ja/guide/deployment/nginx.md @@ -0,0 +1,172 @@ +# Nginx デプロイ + +## はじめに + +Sanicはインターネット上で直接実行することができますが、Nginxなどのプロキシ +サーバーをその前に使用すると便利です。 This is particularly useful for running +multiple virtual hosts on the same IP, serving NodeJS or other services beside +a single Sanic app, and it also allows for efficient serving of static files. +TLS と HTTP/2 は、このようなプロキシでも容易に実装されています。 + +Sanicアプリは127.0.0でローカルでのみ動作するように設定しています。 :8001, 一方、 +Nginx のインストールは、ドメインの example.com 上のパブリック インターネット +にサービスを提供する責任があります。 スタティックファイルは最大 +パフォーマンスのためにNginxによって提供されます。 + +## プロキシされたサニックアプリ + +```python +from sanic import Sanic +from sanic.response import text + +app = Sanic("proxied_example") + +@app.get("/") +def index(request): + # This should display external (public) addresses: + return text( + f"{request.remote_addr} connected to {request.url_for('index')}\n" + f"Forwarded: {request.forwarded}\n" + ) +``` + +これはシステムサービスになりますので、コードを +`/srv/sanicservice/proxied_example.py`に保存してください。 + +テストのために、ファイルを保存したフォルダにある `sanic` CLI を使用して、アプリをターミナルで実行します。 + +```bash +SANIC_FORWARDED_SECRET=_hostname sanic proxied_example --port 8001 +``` + +Sanic config `FORWARDED_SECRET`を用意して、リモートアドレスから +取得するプロキシを特定します。 ローカルホスト名の前にある `_` に注意してください。 +This gives basic protection against users spoofing these headers and faking +their IP addresses and more. + +## SSL 証明書 + +Certbotをインストールし、すべてのドメインの証明書を取得してください。 これは、指定されたドメイン名を制御することを確認するために、しばらくの間、ポート80上で独自のWebサーバーを起動します。 + +```bash +certbot -d example.com -d www.example.com +``` + +## Nginx 設定 + +高速な透過プロキシを許可するには、非常に多くの構成が必要です しかし、ほとんどの場合、 +は修正する必要はありません。 + +.. tip:: メモ + +``` +Separate upstream section, rather than simply adding the IP after `proxy_pass` +as in most tutorials, is needed for HTTP keep-alive. We also enable streaming, +WebSockets and Nginx serving static files. +``` + +The following config goes inside the `http` section of `nginx.conf` or if your +system uses multiple config files, `/etc/nginx/sites-available/default` or +your own files (be sure to symlink them to `sites-enabled`): + +```nginx +# Files managed by Certbot +ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; +ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; + +# Sanic service +upstream example.com { + keepalive 100; + server 127.0.0.1:8001; + #server unix:/tmp//sanic.sock; +} + +server { + server_name example.com; + listen 443 ssl http2 default_server; + listen [::]:443 ssl http2 default_server; + # Serve static files if found, otherwise proxy to Sanic + location / { + root /srv/sanicexample/static; + try_files $uri @sanic; + } + location @sanic { + proxy_pass http://$server_name; + # Allow fast streaming HTTP/1.1 pipes (keep-alive, unbuffered) + proxy_http_version 1.1; + proxy_request_buffering off; + proxy_buffering off; + proxy_set_header forwarded 'by=\"_$hostname\";$for_addr;proto=$scheme;host=\"$http_host\"'; + # Allow websockets and keep-alive (avoid connection: close) + proxy_set_header connection "upgrade"; + proxy_set_header upgrade $http_upgrade; + } +} + +# Redirect WWW to no-WWW +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name ~^www\.(.*)$; + return 308 $scheme://$1$request_uri; +} + +# Redirect all HTTP to HTTPS with no-WWW +server { + listen 80 default_server; + listen [::]:80 default_server; + server_name ~^(?:www\.)?(.*)$; + return 308 https://$1$request_uri; +} + +# Forwarded for= client IP address formatting +map $remote_addr $for_addr { + ~^[0-9.]+$ "for=$remote_addr"; # IPv4 client address + ~^[0-9A-Fa-f:.]+$ "for=\"[$remote_addr]\""; # IPv6 bracketed and quoted + default "for=unknown"; # Unix socket +} +``` + +変更を有効にするためにNginxを起動または再起動します。 例えば、 + +```bash +systemctl restart nginx +``` + +`https://example.com`でアプリを接続できます。 Any 404 +errors and such will be handled by Sanic's error pages, and whenever a static +file is present at a given path, it will be served by Nginx. + +## サービスとして実行中 + +この部分は `systemd` に基づいたLinuxディストリビューション用です。 Create a unit file +`/etc/systemd/system/sanicexample.service` + +``` +[Unit] +Description=Sanic Example + +[Service] +DynamicUser=Yes +WorkingDirectory=/srv/sanicservice +Environment=SANIC_PROXY_SECRET=_hostname +ExecStart=sanic proxied_example --port 8001 --fast +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +次に、サービスファイルを再読み込みし、サービスを開始し、起動時に有効にします: + +```bash +systemctl daemon-reload +systemctl start sanicexample +systemctl enable sanicexample +``` + +.. tip:: メモ + +``` +簡潔さのために、別のユーザアカウントとPython仮想環境のセットアップをスキップしたり、アプリケーションをPythonモジュールとしてインストールしたりしました。 Sanicにも簡単に適用できる他のトピックについても良いチュートリアルがあります。 DynamicUser設定は強力なサンドボックスを作成します。これは、アプリケーションがファイルにデータを保存できないことを意味します。 その代わりに、`User=sanicexample` を設定することを考えてみましょう。 +``` diff --git a/guide/content/ja/guide/getting-started.md b/guide/content/ja/guide/getting-started.md new file mode 100644 index 0000000000..bcf3ca4992 --- /dev/null +++ b/guide/content/ja/guide/getting-started.md @@ -0,0 +1,106 @@ +# はじめに + +始める前に、Python 3.9 以上を実行していることを確認してください。 現在、SanicはPythonバージョン3.9 – 3.13で動作しています。 + +## インストール + +```sh +pip install sanic +``` + +## こんにちは、世界のアプリケーション + +.. 列:: + +``` +If you have ever used one of the many decorator based frameworks, this probably looks somewhat familiar to you. + + + +.. note:: + + If you are coming from Flask or another framework, there are a few important things to point out. Remember, Sanic aims for performance, flexibility, and ease of use. These guiding principles have tangible impact on the API and how it works. +``` + +.. 列:: + +```` +```python +from sanic import Sanic +from sanic.response import text + +app = Sanic("MyHelloWorldApp") + +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` +```` + +### ご注意ください + +- 各リクエストハンドラーは、sync (`def hello_world`) または async (`async def hello_world`) のいずれかになります。 明確な理由がない限り、常に `async` を使用します。 +- `request` オブジェクトは常にハンドラの最初の引数です。 他のフレームワークは、インポートされるコンテキスト変数にこれを渡します。 `async` ワールドで うまくいかないし、明らかにするのははるかに簡単です(クリーナーやパフォーマンスの向上は言うまでもありません)。 +- 応答タイプを**使用する必要があります**。 他の多くのフレームワークでは、`return "Hello, world."` または: `return {"foo": "bar"}` のような戻り値を持つことができます。 しかしこの暗黙の呼び出しを行うためには、チェーン内のどこかで、あなたが何を意味するのかを判断するために貴重な時間を費やす必要があります。 したがって、これを犠牲にして、Sanicは明示的な呼び出しを要求することにしました。 + +### 実行中 + +.. 列:: + +``` +上記のファイルを `server.py` として保存して起動しましょう。 +``` + +.. 列:: + +```` +```sh +sanic server +``` +```` + +.. note:: + +``` +この**別の**重要な区別。他のフレームワークには開発用サーバーが組み込まれており、開発用のものであることを明示的に示しています。 + +**パッケージ化されたサーバーは準備ができています。** +``` + +## サニックエクステンション + +Sanicは、意図的にクリーンで不要なフィーチャーリストを目指しています。 プロジェクトは、特定の方法でアプリケーションを構築する必要はありませんし、特定の開発パターンを処方しないようにしようとします。 コミュニティによって構築され、維持されているいくつかのサードパーティーのプラグインがあります, それ以外の場合、コアリポジトリの要件を満たしていない追加の機能を追加します. + +しかし、**API 開発者を助けるために**ために、Sanic Organization は [Sanic Extensions](../plugins/sanic-ext/getting-started.md) と呼ばれる公式プラグインを管理し、以下を含むあらゆる種類のグッズを提供します。 + +- **OpenAPI** Redoc や Swagger 付きドキュメント +- **CORS** 保護 +- ルートハンドラへの**依存性注入** +- クエリの引数と本文入力**バリデーション** +- `HEAD`、`OPTIONS`、および `TRACE`エンドポイントを自動的に作成します +- 事前定義されたエンドポイント固有のレスポンスシリアライザー + +設定するにはSanicと一緒にインストールする方法が推奨されますが、独自にパッケージをインストールすることもできます。 + +.. 列:: + +```` +```sh +pip install sanic[ext] +``` +```` + +.. 列:: + +```` +```sh +pip install sanic sanic-ext +``` +```` + +同じ環境であれば、Sanicは自動的にSanic Extensionをv21.12から設定します。 2つの追加アプリケーションプロパティにもアクセスできます。 + +- `app.extend()` - Sanic Extensionsの設定に使用 +- `app.ext` - アプリケーションに追加された `Extend` インスタンス + +プラグインの使用方法と操作方法については、format@@0(../plugins/sanic-ext/getting-started.md) を参照してください。 diff --git a/guide/content/ja/guide/how-to/README.md b/guide/content/ja/guide/how-to/README.md new file mode 100644 index 0000000000..8e66fc0483 --- /dev/null +++ b/guide/content/ja/guide/how-to/README.md @@ -0,0 +1 @@ +# How to ... diff --git a/guide/content/ja/guide/how-to/authentication.md b/guide/content/ja/guide/how-to/authentication.md new file mode 100644 index 0000000000..5c33b060d7 --- /dev/null +++ b/guide/content/ja/guide/how-to/authentication.md @@ -0,0 +1,116 @@ +# 認証 + +> 認証と認証を制御するにはどうすればよいですか? + +これは、いくつかのスニペットに詰め込むために_非常に複雑な対象です。 しかし、これはこの問題に取り組む方法についてのアイデアを提供する必要があります。 この例では [JWTs](https://jwt.io/)を使用しますが、この概念はセッションや他のスキームにも同様に適用されるはずです。 + +## `server.py` + +```python +from sanic import Sanic, text + +from auth import protected +from login import login + +app = Sanic("AuthApp") +app.config.SECRET = "KEEP_IT_SECRET_KEEP_IT_SAFE" +app.blueprint(login) + +@app.get("/secret") +@protected +async def secret(request): + return text("To go fast, you must be fast.") +``` + +## `login.py` + +```python +import jwt +from sanic import Blueprint, text + +login = Blueprint("login", url_prefix="/login") + +@login.post("/") +async def do_login(request): + token = jwt.encode({}, request.app.config.SECRET) + return text(token) +``` + +## `auth.py` + +```python +from functools import wraps + +import jwt +from sanic import text + +def check_token(request): + if not request.token: + return False + + try: + jwt.decode( + request.token, request.app.config.SECRET, algorithms=["HS256"] + ) + except jwt.exceptions.InvalidTokenError: + return False + else: + return True + +def protected(wrapped): + def decorator(f): + @wraps(f) + async def decorated_function(request, *args, **kwargs): + is_authenticated = check_token(request) + + if is_authenticated: + response = await f(request, *args, **kwargs) + return response + else: + return text("You are unauthorized.", 401) + + return decorated_function + + return decorator(wrapped) +``` + +このデコレータパターンは format@@0(/ja/guide/best-practices/decorators.md) から取得されます。 + +--- + +```bash +$ curl localhost:9999/secret -i +HTTP/1.1 401 Unauthorized +content-length: 21 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +You are unauthorized. + + +$ curl localhost:9999/login -X POST +eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e30.rjxS7ztIGt5tpiRWS8BGLUqjQFca4QOetHcZTi061DE + + +$ curl localhost:9999/secret -i -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e30.rjxS7ztIGt5tpiRWS8BGLUqjQFca4QOetHcZTi061DE" +HTTP/1.1 200 OK +content-length: 29 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +To go fast, you must be fast. + + +$ curl localhost:9999/secret -i -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e30.BAD" +HTTP/1.1 401 Unauthorized +content-length: 21 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +You are unauthorized. +``` + +また、コミュニティからいくつかのリソースをチェックアウトします。 + +- Awesome Sanic - [Authorization](https://github.com/mekicha/awesome-sanic/blob/master/README.md#authentication) & [Session](https://github.com/mekicha/awesome-sanic/blob/master/README.md#session) +- [EuroPython 2020 - Web APIでのアクセス制御の克服](https://www.youtube.com/watch?v=Uqgoj43ky6A) diff --git a/guide/content/ja/guide/how-to/autodiscovery.md b/guide/content/ja/guide/how-to/autodiscovery.md new file mode 100644 index 0000000000..2b4b55029b --- /dev/null +++ b/guide/content/ja/guide/how-to/autodiscovery.md @@ -0,0 +1,197 @@ +--- +title: 自動検出 +--- + +# 設計図、ミドルウェア、リスナーの自動発見 + +> アプリケーションを構築するために使用しているコンポーネントを自動検出するにはどうすればよいですか? + +誰かがアプリケーションを構築する際に直面する最初の問題の1つは、プロジェクトを構成する方法\*です。 Sanicはルートハンドラ、ミドルウェア、リスナーを登録するためにデコレータを活用しています。 そして、設計図を作成した後、アプリケーションにマウントする必要があります。 + +可能な解決策は、**everything** がインポートされ、Sanic インスタンスに適用される単一のファイルです。 もう一つは、グローバル変数として Sanic インスタンスを渡すことです。 これらの解決策の両方に欠点があります。 + +代替案は自己発見である。 アプリケーションをモジュール(既にインポートされている、または文字列)に向けて、すべてのものを配線します。 + +## `server.py` + +```python +from sanic import Sanic +from sanic.response import empty + +import blueprints +from utility import autodiscover + +app = Sanic("auto", register=True) +autodiscover( + app, + blueprints, + "parent.child", + "listeners.something", + recursive=True, +) + +app.route("/")(lambda _: empty()) +``` + +```bash +[2021-03-02 21:37:02 +0200] [880451] [INFO] Goin' Fast @ http://127.0.0.1:9999 +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something @ nested +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something @ level1 +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something @ level3 +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something inside __init__.py +[2021-03-02 21:37:02 +0200] [880451] [INFO] Starting worker [880451] +``` + +## `utility.py` + +```python + +from glob import glob +from importlib import import_module, util +from inspect import getmembers +from pathlib import Path +from types import ModuleType +from typing import Union + +from sanic.blueprints import Blueprint + +def autodiscover( + app, *module_names: Union[str, ModuleType], recursive: bool = False +): + mod = app.__module__ + blueprints = set() + _imported = set() + + def _find_bps(module): + nonlocal blueprints + + for _, member in getmembers(module): + if isinstance(member, Blueprint): + blueprints.add(member) + + for module in module_names: + if isinstance(module, str): + module = import_module(module, mod) + _imported.add(module.__file__) + _find_bps(module) + + if recursive: + base = Path(module.__file__).parent + for path in glob(f"{base}/**/*.py", recursive=True): + if path not in _imported: + name = "module" + if "__init__" in path: + *_, name, __ = path.split("/") + spec = util.spec_from_file_location(name, path) + specmod = util.module_from_spec(spec) + _imported.add(path) + spec.loader.exec_module(specmod) + _find_bps(specmod) + + for bp in blueprints: + app.blueprint(bp) +``` + +## `blueprints/level1.py` + +```python +from sanic import Blueprint +from sanic.log import logger + +level1 = Blueprint("level1") + +@level1.after_server_start +def print_something(app, loop): + logger.debug("something @ level1") +``` + +## `blueprints/one/two/level3.py` + +```python +from sanic import Blueprint +from sanic.log import logger + +level3 = Blueprint("level3") + +@level3.after_server_start +def print_something(app, loop): + logger.debug("something @ level3") +``` + +## `listeners/something.py` + +```python +from sanic import Sanic +from sanic.log import logger + +app = Sanic.get_app("auto") + +@app.after_server_start +def print_something(app, loop): + logger.debug("something") +``` + +## `parent/child/__init__.py` + +```python +from sanic import Blueprint +from sanic.log import logger + +bp = Blueprint("__init__") + +@bp.after_server_start +def print_something(app, loop): + logger.debug("something inside __init__.py") +``` + +## `parent/child/nested.py` + +```python +from sanic import Blueprint +from sanic.log import logger + +nested = Blueprint("nested") + +@nested.after_server_start +def print_something(app, loop): + logger.debug("something @ nested") +``` + +--- + +```text +here is the dir tree +generate with 'find . -type d -name "__pycache__" -exec rm -rf {} +; tree' + +. # run 'sanic sever -d' here +├── blueprints +│ ├── __init__.py # you need add this file, just empty +│ ├── level1.py +│ └── one +│ └── two +│ └── level3.py +├── listeners +│ └── something.py +├── parent +│ └── child +│ ├── __init__.py +│ └── nested.py +├── server.py +└── utility.py +``` + +```sh +source ./.venv/bin/activate # activate the python venv which sanic is installed in +sanic sever -d # run this in the directory including server.py +``` + +```text +you will see "something ***" like this: + +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something inside __init__.py +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something @ level3 +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something @ level1 +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something @ nested +``` diff --git a/guide/content/ja/guide/how-to/cors.md b/guide/content/ja/guide/how-to/cors.md new file mode 100644 index 0000000000..ce31516505 --- /dev/null +++ b/guide/content/ja/guide/how-to/cors.md @@ -0,0 +1,138 @@ +--- +title: CORS +--- + +# クロスオリジンリソース共有 (CORS) + +> CORS のアプリケーションを設定するにはどうすればよいですか? + +.. note:: + +``` +🏆 最善の解決策は[Sanic Extensions](../../plugins/sanic-ext/http/cors.md)を使用することです。 + +ただし、独自のバージョンをビルドしたい場合は、この限られた例を出発点として使用することができます。 +``` + +### `server.py` + +```python +from sanic import Sanic, text + +from cors import add_cors_headers +from options import setup_options + +app = Sanic("app") + +@app.route("/", methods=["GET", "POST"]) +async def do_stuff(request): + return text("...") + +# Add OPTIONS handlers to any route that is missing it +app.register_listener(setup_options, "before_server_start") + +# Fill in CORS headers +app.register_middleware(add_cors_headers, "response") +``` + +## `cors.py` + +```python +from typing import Iterable + +def _add_cors_headers(response, methods: Iterable[str]) -> None: + allow_methods = list(set(methods)) + if "OPTIONS" not in allow_methods: + allow_methods.append("OPTIONS") + headers = { + "Access-Control-Allow-Methods": ",".join(allow_methods), + "Access-Control-Allow-Origin": "mydomain.com", + "Access-Control-Allow-Credentials": "true", + "Access-Control-Allow-Headers": ( + "origin, content-type, accept, " + "authorization, x-xsrf-token, x-request-id" + ), + } + response.headers.extend(headers) + +def add_cors_headers(request, response): + if request.method != "OPTIONS": + methods = [method for method in request.route.methods] + _add_cors_headers(response, methods) +``` + +## `options.py` + +```python +from collections import defaultdict +from typing import Dict, FrozenSet + +from sanic import Sanic, response +from sanic.router import Route + +from cors import _add_cors_headers + +def _compile_routes_needing_options( + routes: Dict[str, Route] +) -> Dict[str, FrozenSet]: + needs_options = defaultdict(list) + # This is 21.12 and later. You will need to change this for older versions. + for route in routes.values(): + if "OPTIONS" not in route.methods: + needs_options[route.uri].extend(route.methods) + + return { + uri: frozenset(methods) for uri, methods in dict(needs_options).items() + } + +def _options_wrapper(handler, methods): + def wrapped_handler(request, *args, **kwargs): + nonlocal methods + return handler(request, methods) + + return wrapped_handler + +async def options_handler(request, methods) -> response.HTTPResponse: + resp = response.empty() + _add_cors_headers(resp, methods) + return resp + +def setup_options(app: Sanic, _): + app.router.reset() + needs_options = _compile_routes_needing_options(app.router.routes_all) + for uri, methods in needs_options.items(): + app.add_route( + _options_wrapper(options_handler, methods), + uri, + methods=["OPTIONS"], + ) + app.router.finalize() +``` + +--- + +``` +$ curl localhost:9999/ -i +HTTP/1.1 200 OK +Access-Control-Allow-Methods: OPTIONS,POST,GET +Access-Control-Allow-Origin: mydomain.com +Access-Control-Allow-Credentials: true +Access-Control-Allow-Headers: origin, content-type, accept, authorization, x-xsrf-token, x-request-id +content-length: 3 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +... + +$ curl localhost:9999/ -i -X OPTIONS +HTTP/1.1 204 No Content +Access-Control-Allow-Methods: GET,POST,OPTIONS +Access-Control-Allow-Origin: mydomain.com +Access-Control-Allow-Credentials: true +Access-Control-Allow-Headers: origin, content-type, accept, authorization, x-xsrf-token, x-request-id +connection: keep-alive +``` + +また、コミュニティからいくつかのリソースをチェックアウトします。 + +- format@@0(https://github.com/mekicha/awesome-sanic/blob/master/README.md#frontend) diff --git a/guide/content/ja/guide/how-to/csrf.md b/guide/content/ja/guide/how-to/csrf.md new file mode 100644 index 0000000000..7f982ff12f --- /dev/null +++ b/guide/content/ja/guide/how-to/csrf.md @@ -0,0 +1 @@ +csrf diff --git a/guide/content/ja/guide/how-to/db.md b/guide/content/ja/guide/how-to/db.md new file mode 100644 index 0000000000..476e159da3 --- /dev/null +++ b/guide/content/ja/guide/how-to/db.md @@ -0,0 +1 @@ +データソースへの接続 diff --git a/guide/content/ja/guide/how-to/decorators.md b/guide/content/ja/guide/how-to/decorators.md new file mode 100644 index 0000000000..7d03dd7182 --- /dev/null +++ b/guide/content/ja/guide/how-to/decorators.md @@ -0,0 +1 @@ +デコレーター diff --git a/guide/content/ja/guide/how-to/mounting.md b/guide/content/ja/guide/how-to/mounting.md new file mode 100644 index 0000000000..e484b27f92 --- /dev/null +++ b/guide/content/ja/guide/how-to/mounting.md @@ -0,0 +1,53 @@ +# アプリケーションのマウント + +> アプリケーションを root の上にあるパスにマウントするにはどうすればよいですか? + +```python +# server.py +from sanic import Sanic, text + +app = Sanic("app") +app.config.SERVER_NAME = "example.com/api" + +@app.route("/foo") +def handler(request): + url = app.url_for("handler", _external=True) + return text(f"URL: {url}") +``` + +```yaml +# docker-compose.yml +version: "3.7" +services: + app: + image: nginx:alpine + ports: + - 80:80 + volumes: + - type: bind + source: ./conf + target: /etc/nginx/conf.d/default.conf +``` + +```nginx +# conf +server { + listen 80; + + # Computed data service + location /api/ { + proxy_pass http://:9999/; + proxy_set_header Host example.com; + } +} +``` + +```bash +$ docker-compose up -d +$ sanic server.app --port=9999 --host=0.0.0 +``` + +```bash +$ curl localhost/api/foo +URL: http://example.com/api/foo +``` diff --git a/guide/content/ja/guide/how-to/orm.md b/guide/content/ja/guide/how-to/orm.md new file mode 100644 index 0000000000..d453c21178 --- /dev/null +++ b/guide/content/ja/guide/how-to/orm.md @@ -0,0 +1,467 @@ +# ORM + +> どのように私はSanicでSQLAlchemyを使用しますか? + +すべてのORMツールはSanicで使用できますが、非同期ORMツールはSanicパフォーマンスに影響を与えます。 +サポートする Orm パッケージがいくつかあります + +現在、Python の `async`/`await` キーワードをサポートするORMがたくさんあります。 いくつかの可能な選択肢には: + +- [Mayim](https://ahopkins.github.io/mayim/) +- [SQLAlchemy 1.4](https://docs.sqlalchemy.org/ja/14/changelog/changelog_14.html) +- [tortoise-orm](https://github.com/tortoise/turtoise-orm) + +Sanicアプリケーションへの統合はかなり簡単です: + +## Mayim + +Mayimはformat@@0(https://ahopkins.github.io/mayim/guide/extensions.html#sanic)と一緒に出荷します。 確かに拡張機能なしで Mayim を実行することは可能ですが、すべての format@@0(https://sanic )を処理するために推奨されています。 ev/ja/guide/basics/listeners.html) and [dependency injections](https://sanic.dev/en/plugins/sanic-ext/injection.html) + +.. 列:: + +``` +### 依存関係 + +まず、必要な依存関係をインストールする必要があります。DBドライバに必要なインストールについては、[Mayim docs](https://ahopkins.github.io/mayim/guide/install.html#postgres)を参照してください。 +``` + +.. 列:: + +```` +```shell +pip install sanic-ext +pip install mayim[postgres] +``` +```` + +.. 列:: + +``` +### Define ORM Model + +Mayim allows you to use whatever you want for models. Whether it is [dataclasses](https://docs.python.org/3/library/dataclasses.html), [pydantic](https://pydantic-docs.helpmanual.io/), [attrs](https://www.attrs.org/en/stable/), or even just plain `dict` objects. Since it works very nicely [out of the box with Pydantic](https://ahopkins.github.io/mayim/guide/pydantic.html), that is what we will use here. +``` + +.. 列:: + +```` +```python +# ./models.py +from pydantic import BaseModel + +class City(BaseModel): + id: int + name: str + district: str + population: int + +class Country(BaseModel): + code: str + name: str + continent: str + region: str + capital: City +``` +```` + +.. 列:: + +``` +### SQLを定義する + +使い慣れていない場合、Mayimは一方向のSQL-firstであるという点で他のORMとは異なります。 これは、インラインまたは別の `.sql` ファイルで独自のクエリを定義することを意味します。 +``` + +.. 列:: + +```` +```sql +-- ./queries/select_all_countries.sql +SELECT country.code, + country.name, + country.continent, + country.region, + ( + SELECT row_to_json(q) + FROM ( + SELECT city.id, + city.name, + city.district, + city.population + ) q + ) capital +FROM country + JOIN city ON country.capital = city.id +ORDER BY country.name ASC +LIMIT $limit OFFSET $offset; +``` +```` + +.. 列:: + +``` +### Sanic App と Async Engine + +アプリインスタンスを作成し、任意の実行者に `SanicMayimExtension` を追加する必要があります。 +``` + +.. 列:: + +```` +```python +# ./server.py +from sanic import Sanic, Request, json +from sanic_ext import Extend +from mayim.executor import PostgresExecutor +from mayim.extensions import SanicMayimExtension +from models import Country + +class CountryExecutor(PostgresExecutor): + async def select_all_countries( + self, limit: int = 4, offset: int = 0 + ) -> list[Country]: + ... + +app = Sanic("Test") +Extend.register( + SanicMayimExtension( + executors=[CountryExecutor], + dsn="postgres://...", + ) +) +``` +```` + +.. 列:: + +``` +### ルート登録 + +Sanic用のMayimの拡張機能を使っているので、ルートハンドラに自動的に`CountryExecutor`を注入しています。 これにより、タイプ注釈付きの開発が容易になります。 +``` + +.. 列:: + +```` +```python +@app.get("/") +async def handler(request, executor: CountryExecutor): + countles = await executor.select_all_countries() + return json({"countries": [country.dict() for country in co +``` +```` + +.. 列:: + +``` +### リクエストを送信する +``` + +.. 列:: + +```` +```sh +curl 'http://127.0.0.1:8000' +{"countries":[{"code":"AFG","name":"Afghanistan","continent":"Asia","region":"Southern and Central Asia","capital":{"id":1,"name":"Kabul","district":"Kabol","population":1780000}},{"code":"ALB","name":"Albania","continent":"Europe","region":"Southern Europe","capital":{"id":34,"name":"Tirana","district":"Tirana","population":270000}},{"code":"DZA","name":"Algeria","continent":"Africa","region":"Northern Africa","capital":{"id":35,"name":"Alger","district":"Alger","population":2168000}},{"code":"ASM","name":"American Samoa","continent":"Oceania","region":"Polynesia","capital":{"id":54,"name":"Fagatogo","district":"Tutuila","population":2323}}]} +``` +```` + +## SQLAlchemy + +[SQLAlchemy 1.4](https://docs.sqlalchemy.org/en/14/changelog/changelog_14.html) が `asyncio` のネイティブサポートを追加したため、Sanic は SQLAlchemy でうまく動作します。 この機能はまだ SQLAlchemy プロジェクトで _beta_ とみなされていることに注意してください。 + +.. 列:: + +``` +### 依存関係 + +まず、必要な依存関係をインストールする必要があります。 以前はインストールされていた依存関係は `sqlalcemy` と `pymysql` でしたが、現在は `sqlalcemy` と `aiomysql` が必要です。 +``` + +.. 列:: + +```` +```shell +pip install -U sqlalchemy +pip install -U aiomysql +``` +```` + +.. 列:: + +``` +### Define ORM Model + +ORM model creation remains the same. +``` + +.. 列:: + +```` +```python +# ./models.py +from sqlalchemy import INTEGER, Column, ForeignKey, String +from sqlalchemy.orm import declarative_base, relationship + +Base = declarative_base() + +class BaseModel(Base): + __abstract__ = True + id = Column(INTEGER(), primary_key=True) + +class Person(BaseModel): + __tablename__ = "person" + name = Column(String()) + cars = relationship("Car") + + def to_dict(self): + return {"name": self.name, "cars": [{"brand": car.brand} for car in self.cars]} + +class Car(BaseModel): + __tablename__ = "car" + + brand = Column(String()) + user_id = Column(ForeignKey("person.id")) + user = relationship("Person", back_populates="cars") +``` +```` + +.. 列:: + +``` +### Sanic AppとAsync Engine + +ここではデータベースとしてmysqlを使用し、PostgreSQL/SQLiteを選択することもできます。 ドライバを`aiomysql`から`asyncpg`/`aiosqlite`に変更することに注意してください。 +``` + +.. 列:: + +```` +```python +# ./server.py +from sanic import Sanic +from sqlalchemy.ext.asyncio import create_async_engine + +app = Sanic("my_app") + +bind = create_async_engine("mysql+aiomysql://root:root@localhost/test", echo=True) +``` +```` + +.. 列:: + +``` +### Register Middlewares + +The request middleware creates an usable `AsyncSession` object and set it to `request.ctx` and `_base_model_session_ctx`. + +Thread-safe variable `_base_model_session_ctx` helps you to use the session object instead of fetching it from `request.ctx`. +``` + +.. 列:: + +```` +```python +# ./server.py +from contextvars import ContextVar + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.orm import sessionmaker + +_sessionmaker = sessionmaker(bind, AsyncSession, expire_on_commit=False) + +_base_model_session_ctx = ContextVar("session") + +@app.middleware("request") +async def inject_session(request): + request.ctx.session = _sessionmaker() + request.ctx.session_ctx_token = _base_model_session_ctx.set(request.ctx.session) + +@app.middleware("response") +async def close_session(request, response): + if hasattr(request.ctx, "session_ctx_token"): + _base_model_session_ctx.reset(request.ctx.session_ctx_token) + await request.ctx.session.close() +``` +```` + +.. 列:: + +``` +### Register Routes + +によると、 sqlalcemy の公式ドキュメント、 `session. uery`は2.0でレガシーとなり、ORMオブジェクトに問い合わせる2.0の方法は`select`を使用しています。 +``` + +.. 列:: + +```` +```python +# ./server.py +from sqlalchemy import select +from sqlalchemy.orm import selectinload +from sanic.response import json + +from models import Car, Person + +@app.post("/user") +async def create_user(request): + session = request.ctx.session + async with session.begin(): + car = Car(brand="Tesla") + person = Person(name="foo", cars=[car]) + session.add_all([person]) + return json(person.to_dict()) + +@app.get("/user/") +async def get_user(request, pk): + session = request.ctx.session + async with session.begin(): + stmt = select(Person).where(Person.id == pk).options(selectinload(Person.cars)) + result = await session.execute(stmt) + person = result.scalar() + + if not person: + return json({}) + + return json(person.to_dict()) +``` +```` + +.. 列:: + +``` +### リクエストを送信する +``` + +.. 列:: + +```` +```sh +curl --location --request POST 'http://127.0.0.1:8000/user' +{"name":"foo","cars":[{"brand":"Tesla"}]} +```sh + +```sh +curl --location ---request GET 'http://127.0.0.1:8000/user/1' +{"name":"foo","cars":[{"brand":"Tesla"}]} +``` +```` + +## Tortoise-ORM + +.. 列:: + +``` +### 依存関係 + +tortoise-ormの依存関係はとてもシンプルで、インストールするだけで済みます。 +``` + +.. 列:: + +```` +```shell +pip install -U tortoise-orm +``` +```` + +.. 列:: + +``` +### Define ORM Model + +Djangoに慣れていれば、この部分はよく知っているはずです。 +``` + +.. 列:: + +```` +```python +# ./models.py +from tortoise import Model, fields + +class Users(Model): + id = fields.IntField(pk=True) + name = fields.CharField(50) + + def __str__(self): + return f"I am {self.name}" +``` +```` + +.. 列:: + +``` +### Create Sanic App and Async Engine + +Tortoise-ormは登録インターフェースのセットを提供しています。 利用者にとって便利で、簡単にデータベース接続を作成することができます。 +``` + +.. 列:: + +```` +```python +# ./main.py + +from models import Users +from tortoise.contrib.sanic import register_tortoise + +app = Sanic(__name__) + +register_tortoise( + app, db_url="mysql://root:root@localhost/test", modules={"models": ["models"]}, generate_schemas=True +) + +``` +```` + +.. 列:: + +``` +### ルート登録 +``` + +.. 列:: + +```` +```python +# ./main.py + +from models import Users +from sanic import Sanic, response + +@app.route("/user") +async def list_all(request): + users = await Users.all() + return response.json({"users": [str(user) for user in users]}) + +@app.route("/user/") +async def get_user(request, pk): + user = await Users.query(pk=pk) + return response.json({"user": str(user)}) + +if __name__ == "__main__": + app.run(port=5000) +``` +```` + +.. 列:: + +``` +### リクエストを送信する +``` + +.. 列:: + +```` +```sh +curl --location --request POST 'http://127.0.0.1:8000/user' +{"users":["I am foo", "I am bar"]} +``` + +```sh +curl --location --request GET 'http://127.0.0.1:8000/user/1' +{"user": "I am foo"} +``` +```` diff --git a/guide/content/ja/guide/how-to/serialization.md b/guide/content/ja/guide/how-to/serialization.md new file mode 100644 index 0000000000..c4d5522a8b --- /dev/null +++ b/guide/content/ja/guide/how-to/serialization.md @@ -0,0 +1 @@ +# シリアル化 diff --git a/guide/content/ja/guide/how-to/server-sent-events.md b/guide/content/ja/guide/how-to/server-sent-events.md new file mode 100644 index 0000000000..7daf86745e --- /dev/null +++ b/guide/content/ja/guide/how-to/server-sent-events.md @@ -0,0 +1 @@ +sse diff --git a/guide/content/ja/guide/how-to/static-redirects.md b/guide/content/ja/guide/how-to/static-redirects.md new file mode 100644 index 0000000000..911784438c --- /dev/null +++ b/guide/content/ja/guide/how-to/static-redirects.md @@ -0,0 +1,112 @@ +# 「静的」リダイレクト + +> 静的リダイレクトを設定するには? + +## `app.py` + +```python +### SETUP ### +import typing +import sanic, sanic.response + +# Create the Sanic app +app = sanic.Sanic(__name__) + +# This dictionary represents your "static" +# redirects. For example, these values +# could be pulled from a configuration file. +REDIRECTS = { + '/':'/hello_world', # Redirect '/' to '/hello_world' + '/hello_world':'/hello_world.html' # Redirect '/hello_world' to 'hello_world.html' +} + +# This function will return another function +# that will return the configured value +# regardless of the arguments passed to it. +def get_static_function(value:typing.Any) -> typing.Callable[..., typing.Any]: + return lambda *_, **__: value + +### ROUTING ### +# Iterate through the redirects +for src, dest in REDIRECTS.items(): + # Create the redirect response object + response:sanic.HTTPResponse = sanic.response.redirect(dest) + + # Create the handler function. Typically, + # only a sanic.Request object is passed + # to the function. This object will be + # ignored. + handler = get_static_function(response) + + # Route the src path to the handler + app.route(src)(handler) + +# Route some file and client resources +app.static('/files/', 'files') +app.static('/', 'client') + +### RUN ### +if __name__ == '__main__': + app.run( + '127.0.0.1', + 10000 + ) +``` + +## `client/hello_world.html` + +```html + + + + + + + Hello World + + + +
+ Hello world! +
+ + +``` + +## `client/hello_world.css` + +```css +#hello_world { + width: 1000px; + margin-left: auto; + margin-right: auto; + margin-top: 100px; + + padding: 100px; + color: aqua; + text-align: center; + font-size: 100px; + font-family: monospace; + + background-color: rgba(0, 0, 0, 0.75); + + border-radius: 10px; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.75); +} + +body { + background-image: url("/files/grottoes.jpg"); + background-repeat: no-repeat; + background-size: cover; +} +``` + +## `files/grottoes.jpg` + +![lake](/assets/images/grottoes.jpg) + +--- + +また、コミュニティからいくつかのリソースをチェックアウトします。 + +- [Static Routing Example](https://github.com/Perzan/sanic-static-routing-example) diff --git a/guide/content/ja/guide/how-to/table-of-contents.md b/guide/content/ja/guide/how-to/table-of-contents.md new file mode 100644 index 0000000000..c530aa4584 --- /dev/null +++ b/guide/content/ja/guide/how-to/table-of-contents.md @@ -0,0 +1,17 @@ +--- +title: 目次 +--- + +# 目次 + +一般的な質問やユーザーケースに答えるための完全な作業例をまとめました。 ほとんどの部分では、例は可能な限り最小限ですが、完全で実行可能なソリューションである必要があります。 + +| ページ | どうすればいいですか... | +| :------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------ | +| format@@0(./mounting.md) | ... アプリケーションをルートの上にあるパスにマウントしますか? | +| [Authentication](./authentication.md) | ... 認証と承認を制御する? | +| [Autodiscovery](./autodiscovery.md) | ... アプリケーションの構築に使用しているコンポーネントを自動的に発見しますか? | +| [CORS](./cors.md) | ... CORS のアプリケーションを設定しますか? | +| [ORM](./orm) | ... SanicとORMを使用しますか? | +| format@@0(./static-redirects.md) | ... 静的リダイレクトの設定 | +| [TLS/SSL/HTTPS](./tls.md) | ... HTTPS経由でSanicを実行しますか?
... HTTPをHTTPSにリダイレクトしますか? | diff --git a/guide/content/ja/guide/how-to/task-queue.md b/guide/content/ja/guide/how-to/task-queue.md new file mode 100644 index 0000000000..6f8d12219f --- /dev/null +++ b/guide/content/ja/guide/how-to/task-queue.md @@ -0,0 +1 @@ +タスクキュー diff --git a/guide/content/ja/guide/how-to/tls.md b/guide/content/ja/guide/how-to/tls.md new file mode 100644 index 0000000000..17bc327aee --- /dev/null +++ b/guide/content/ja/guide/how-to/tls.md @@ -0,0 +1,199 @@ +--- +title: TLS/SSL/HTTPS +--- + +# TLS/SSL/HTTPS + +> HTTPS経由でSanicを実行するにはどうすればよいですか? + +まだ TLS 証明書を持っていない場合は、format@@0(./tls.md#get-certificates-for-your-domain-names) を参照してください。 + +## 単一ドメインと単一証明書 + +.. 列:: + +``` +Sanicに指定されたフォルダに`fullchain.pem`と`privkey.pem`という名前の証明書ファイルを自動的にロードさせます: +``` + +.. 列:: + +```` +```sh +sudo sanic myserver:app -H :: -p 443 \ + --tls /etc/letsencrypt/live/example.com/ +``` +```python +app.run("::", 443, ssl="/etc/letsencrypt/live/example.com/") +``` +```` + +.. 列:: + +``` +または、certとキーファイル名を辞書として個別に渡すこともできます: + +キーが暗号化されている場合は、パスワードを除くすべてのフィールドが `request` に追加されます。 on_info.cert` +``` + +.. 列:: + +```` +```python +ssl = { + "cert": "/path/to/fullchain.pem", + "key": "/path/to/privkey.pem", + "password": "for encrypted privkey file", # Optional +} +app.run(host="0.0.0.0", port=8443, ssl=ssl) +``` +```` + +.. 列:: + +``` +代わりに、[`ssl.SSLContext`](https://docs.python.org/3/library/ssl.html)が渡されます。どの暗号アルゴリズムが許可されているかなど、詳細を完全に制御する必要がある場合。 デフォルトでは、Sanicはセキュアなアルゴリズムのみを許可し、これは非常に古いデバイスからのアクセスを制限する可能性があります。 +``` + +.. 列:: + +```` +```python +import ssl + +context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) +context.load_cert_chain("certs/fullchain.pem", "certs/privkey.pem") + +app.run(host="0.0.0.0", port=8443, ssl=context) +``` +```` + +## 別の証明書を持つ複数のドメイン + +.. 列:: + +``` +複数の証明書のリストを提供することができます。その場合、Sanic はユーザーが接続しているホスト名に一致するものを選択します。 これは TLS ハンドシェイクの早い段階で発生し、Sanic はまだクライアントにパケットを送信していない。 + +クライアントがSNI(サーバー名表示)を送信していない場合 リストの最初の証明書は、クライアントブラウザ上で名前が一致しないため、TLS エラーで失敗する可能性がありますが使用されます。 このフォールバックを防ぎ、既知のホスト名なしでクライアントの接続を即座に切断するには、リストの最初のエントリとして `None` を追加します。 `--tls-strict-host` はCLIオプションと同等です。 +``` + +.. 列:: + +```` +```python +ssl = ["certs/example.com/", "certs/bigcorp.test/"] +app.run(host="0.0.0.0", port=8443, ssl=ssl) +``` +```sh +sanic myserver:app + --tls certs/example.com/ + --tls certs/bigcorp.test/ + --tls-strict-host +``` +```` + +.. tip:: + +``` +証明書を公開したくない場合は、1つの証明書の前に `None` を使用することもできます。 適切なDNS名ではなく、IPアドレスに接続している人への真のホスト名またはサイトコンテンツ。 +``` + +.. 列:: + +``` +辞書はリストで使用できます。 これにより、証明書が一致するドメインを指定することができますが、証明書自体に存在する名前は、ここから制御することはできません。 names が指定されていない場合、証明書自体からの名前が使用されます。 + +メインドメイン **example.com** とサブドメインのみ **bigcorp.test** への接続を許可するには: +``` + +.. 列:: + +```` +```python +ssl = [ + None, # No fallback if names do not match! + { + "cert": "certs/example.com/fullchain.pem", + "key": "certs/example.com/privkey.pem", + "names": ["example.com", "*.bigcorp.test"], + } +] +app.run(host="0.0.0.0", port=8443, ssl=ssl) +``` +```` + +## `request.conn_info` フィールドを介してハンドラ内の TLS 情報にアクセスする + +- `.ssl` - 接続セキュア (bool) +- `.cert` - 現在アクティブな証明書の情報とdict フィールド (dict) +- `.server_name` - クライアントによって送信されたSNI(str、空かもしれない) + +すべての `conn_info` フィールドは接続ごとに存在し、時間の経過とともに多くのリクエストがあることに注意してください。 プロキシがサーバの前で使用されている場合、同じパイプでのこれらのリクエストは異なるユーザからのものでもあります。 + +## HTTPをHTTPSにリダイレクトし、証明書要求は引き続きHTTP経由で行われます + +HTTPS が実行されている通常のサーバーに加えて、`http_redir.py`をリダイレクトする別のサーバーを実行します。 + +```python +from sanic import Sanic, exceptions, response + +app = Sanic("http_redir") + +# Serve ACME/certbot files without HTTPS, for certificate renewals +app.static("/.well-known", "/var/www/.well-known", resource_type="dir") + +@app.exception(exceptions.NotFound, exceptions.MethodNotSupported) +def redirect_everything_else(request, exception): + server, path = request.server_name, request.path + if server and path.startswith("/"): + return response.redirect(f"https://{server}{path}", status=308) + return response.text("Bad Request. Please use HTTPS!", status=400) +``` + +HTTPS サーバーとは別の systemd ユニットとしてセットアップするのが最善です。 HTTPSサーバーはまだ実行できませんが、最初に証明書をリクエストする際にHTTPを実行する必要がある場合があります。 IPv4とIPv6の起動: + +``` +sanic http_redir:app -H 0.0.0.0 -p 80 +sanic http_redir:app -H :: -p 80 +``` + +または、メインアプリケーションからHTTPリダイレクトアプリケーションを実行することもできます。 + +```python +# app == Your main application +# redirect == Your http_redir application +@app.before_server_start +async def start(app, _): + app.ctx.redirect = await redirect.create_server( + port=80, return_asyncio_server=True + ) + app.add_task(runner(redirect, app.ctx.redirect)) + +@app.before_server_stop +async def stop(app, _): + await app.ctx.redirect.close() + +async def runner(app, app_server): + app.state.is_running = True + try: + app.signalize() + app.finalize() + app.state.is_started = True + await app_server.serve_forever() + finally: + app.state.is_running = False + app.state.is_stopping = True +``` + +## ドメイン名の証明書を取得する + +format@@0(https://letsencrypt.org/)から無料で証明書を取得できます。 パッケージマネージャーから [certbot](https://certbot.eff.org/) をインストールし、証明書を要求します。 + +```sh +sudo certbot certonly --key-type ecdsa --preferred-chain "ISRG Root X1" -d example.com -d www.example.com +``` + +複数のドメイン名は `-d` 引数によって追加されることができます。すべてが `/etc/letsencrypt/live/example` に保存される単一の証明書に格納されます。 ここにリストされている**最初のドメイン**に従って、om/\`。 + +キータイプと優先チェーンオプションは、最小サイズの証明書ファイルを取得するために必要です。 サーバーをできるだけ速く動作させるために不可欠です。 このチェーンには、Let's Encryptがすべての主要なブラウザで新しいECチェーンを信頼するまで、1つのRSA証明書が含まれます。 diff --git a/guide/content/ja/guide/how-to/validation.md b/guide/content/ja/guide/how-to/validation.md new file mode 100644 index 0000000000..ab377e8183 --- /dev/null +++ b/guide/content/ja/guide/how-to/validation.md @@ -0,0 +1 @@ +検証 diff --git a/guide/content/ja/guide/how-to/websocket-feed.md b/guide/content/ja/guide/how-to/websocket-feed.md new file mode 100644 index 0000000000..8a6494a96b --- /dev/null +++ b/guide/content/ja/guide/how-to/websocket-feed.md @@ -0,0 +1 @@ +websocket フィード diff --git a/guide/content/ja/guide/introduction.md b/guide/content/ja/guide/introduction.md new file mode 100644 index 0000000000..e99c4581ee --- /dev/null +++ b/guide/content/ja/guide/introduction.md @@ -0,0 +1,71 @@ +# はじめに + +Sanic は Python 3.9+ のウェブサーバーであり、高速化のために書かれたウェブフレームワークです。 Python 3.5 で追加された async/await 構文を使用することができます。これにより、コードをノンブロッキングでスピーディーにすることができます。 + +.. attrs:: +:class: 紹介テーブル + +``` +| | | +|--|--| +| Build | [![Tests](https://github.com/sanic-org/sanic/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/sanic-org/sanic/actions/workflows/tests.yml) | +| Docs | [![User Guide](https://img.shields.io/badge/user%20guide-sanic-ff0068)](https://sanicframework.org/) [![Documentation](https://readthedocs.org/projects/sanic/badge/?version=latest)](http://sanic.readthedocs.io/en/latest/?badge=latest) | +| Package | [![PyPI](https://img.shields.io/pypi/v/sanic.svg)](https://pypi.python.org/pypi/sanic/) [![PyPI version](https://img.shields.io/pypi/pyversions/sanic.svg)](https://pypi.python.org/pypi/sanic/) [![Wheel](https://img.shields.io/pypi/wheel/sanic.svg)](https://pypi.python.org/pypi/sanic) [![Supported implementations](https://img.shields.io/pypi/implementation/sanic.svg)](https://pypi.python.org/pypi/sanic) [![Code style black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) | +| Support | [![Forums](https://img.shields.io/badge/forums-community-ff0068.svg)](https://community.sanicframework.org/) [![Discord](https://img.shields.io/discord/812221182594121728?logo=discord)](https://discord.gg/FARQzAEMAA) [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/mekicha/awesome-sanic) | +| Stats | [![Monthly Downloads](https://img.shields.io/pypi/dm/sanic.svg)](https://pepy.tech/project/sanic) [![Weekly Downloads](https://img.shields.io/pypi/dw/sanic.svg)](https://pepy.tech/project/sanic) [![Conda downloads](https://img.shields.io/conda/dn/conda-forge/sanic.svg)](https://anaconda.org/conda-forge/sanic) | +``` + +## それは何ですか? + +まず最初に、水に飛び込む前に、Sanicは他のフレームワークとは異なることを知っておくべきです。 + +最初の文には大きな間違いがあります。なぜなら、Sanic は **framework** と **web server** の両方であるからです。 展開セクションでは、これについてもう少し詳しく説明します。 + +しかし、Sanicには、プロダクショングレードのWebアプリケーションを作成、デプロイ、およびスケーリングするために必要なものがすべて揃っています。 🚀 + +## 目標 + +> 構築しやすい高性能な HTTP サーバーを立ち上げて実行する簡単な方法を提供します。 拡大し最終的には拡大しました + +## 特徴 + +.. 列:: + +``` +### Core + +- Built in, **_fast_** web server +- Production ready +- Highly scalable +- ASGI compliant +- Simple and intuitive API design +- By the community, for the community +``` + +.. 列:: + +``` +### Sanic Extensions [[learn more](../plugins/sanic-ext/getting-started.md)] + +- CORS protection +- Template rendering with Jinja +- Dependency injection into route handlers +- OpenAPI documentation with Redoc and/or Swagger +- Predefined, endpoint-specific response serializers +- Request query arguments and body input validation +- Auto create `HEAD`, `OPTIONS`, and `TRACE` endpoints +``` + +## スポンサー情報 + +format@@0(https://opencollective.com/sanic-org) をご覧ください。 + +## コミュニティに参加する + +ディスカッションの主なチャンネルは、format@@0(https://community.sanicframework.org/)にあります。 ライブディスカッションやチャットのための[Discord Server](https://discord.gg/FARQzAEMAA)もあります。 + +Stackoverflow `[sanic]` タグはプロジェクトメンテナーによってformat@@1(https://stackoverflow.com/questions/tagged/sanic)です。 + +## 貢献 + +私たちは常に新しい貢献をしています。 We have [marked issues good for anyone looking to get started](https://github.com/sanic-org/sanic/issues?q=is%3Aopen+is%3Aissue+label%3Abeginner), and welcome [questions/answers/discussion on the forums](https://community.sanicframework.org/). format@@0(https://github.com/sanic-org/sanic/blob/master/CONTRIBUTING.rst)をご覧ください。 diff --git a/guide/content/ja/guide/running/app-loader.md b/guide/content/ja/guide/running/app-loader.md new file mode 100644 index 0000000000..ab5e0578fa --- /dev/null +++ b/guide/content/ja/guide/running/app-loader.md @@ -0,0 +1,98 @@ +--- +title: 動的アプリケーション +--- + +# 動的アプリケーション + +SanicはCLIで動作するように最適化されています。 まだ読んでいない場合は、format@@0(./running.md#sanic-server)を読んで、オプションに慣れるようにしてください。 + +.. 列:: + +``` +これにはグローバルスコープオブジェクトとして実行することが含まれます... +``` + +.. 列:: + +```` +```sh +sanic path.to.server:app +``` +```python +# server.py +app = Sanic("TestApp") + +@app.get("/") +async def handler(request: Request): + return json({"foo": "bar"}) +``` +```` + +.. 列:: + +``` +...または、`Sanic`アプリケーションオブジェクトを作成するファクトリ関数。 +``` + +.. 列:: + +```` +```sh +sanic path.to.server:create_app --factory +``` +```python +# server.py +def create_app(): + app = Sanic("TestApp") + + @app.get("/") + async def handler(request: Request): + return json({"foo": "bar"}) + + return app +``` +```` + +**時々、これは十分ではありません... 🤔** + +[v22.9](../release-notes/v22.9.md) で導入されたSanicは、さまざまな[worker processes](./manager.md#how-sanic-server-starts-processes)でアプリケーションを作成する `AppLoader` オブジェクトを持っています。 アプリケーションでより動的なスタートアップ体験を作成する必要がある場合は、これを利用できます。 + +.. 列:: + +``` +`AppLoader`は、`Sanic`インスタンスを返す呼び出し可能なものを渡すことができます。その`AppLoader`は、APIを実行する低レベルのアプリケーションで使用できます。 +``` + +.. 列:: + +```` +```python +import sys +from functools import partial + +from sanic import Request, Sanic, json +from sanic.worker.loader import AppLoader + +def attach_endpoints(app: Sanic): + @app.get("/") + async def handler(request: Request): + return json({"app_name": request.app.name}) + +def create_app(app_name: str) -> Sanic: + app = Sanic(app_name) + attach_endpoints(app) + return app + +if __name__ == "__main__": + app_name = sys.argv[-1] + loader = AppLoader(factory=partial(create_app, app_name)) + app = loader.load() + app.prepare(port=9999, dev=True) + Sanic.serve(primary=app, app_loader=loader) +``` +```sh +python path/to/server.py MyTestAppName +``` +```` + +上記の例では、 `AppLoader` は `factory` で作成され、プロセス間で同じアプリケーションのコピーを作成できます。 これを行う場合は、上記の`Sanic.serve`パターンを使用して、作成した`AppLoader`が置き換えられないようにしてください。 diff --git a/guide/content/ja/guide/running/configuration.md b/guide/content/ja/guide/running/configuration.md new file mode 100644 index 0000000000..42ab3df109 --- /dev/null +++ b/guide/content/ja/guide/running/configuration.md @@ -0,0 +1,328 @@ +# 設定 + +## 基本 + +.. 列:: + +``` +Sanic はアプリケーションオブジェクトの config 属性に設定を保持します。 構成オブジェクトは、ドット表記または辞書のように変更できるオブジェクトにすぎません。 +``` + +.. 列:: + +```` +```python +app = Sanic("myapp") +app.config.DB_NAME = "appdb" +app.config["DB_USER"] = "appuser" +``` +```` + +.. 列:: + +``` +`update()`メソッドは、通常の辞書と同様に使用することもできます。 +``` + +.. 列:: + +```` +```python +db_settings = { + 'DB_HOST': 'localhost', + 'DB_NAME': 'appdb', + 'DB_USER': 'appuser' +} +app.config.update(db_settings) +``` +```` + +.. note:: + +``` +Sanicでは標準的な方法で、設定値を**大文字**で指定します。 確かに、大文字と小文字の名前を混ぜ始めると、奇妙な動作が発生することがあります。 +``` + +## 読み込み中 + +### 環境変数 + +.. 列:: + +``` +SANIC_`プレフィックスで定義された環境変数は、Sanicの設定に適用されます。 例えば、`SANIC_REQUEST_TIMEOUT` を設定すると、アプリケーションが自動的にロードされ、 `REQUEST_TIMEOUT` 設定変数に与えられます。 +``` + +.. 列:: + +```` +```bash +$ export SANIC_REQUEST_TIMEOUT=10 +``` +```python +>>> print(app.config.REQUEST_TIMEOUT) +10 +``` +```` + +.. 列:: + +``` +Sanicが起動時に期待しているプレフィックスを変更できます。 +``` + +.. 列:: + +```` +```bash +$ export MYAPP_REQUEST_TIMEOUT=10 +``` +```python +>>> app = Sanic(__name__, env_prefix='MYAPP_') +>>> print(app.config.REQUEST_TIMEOUT) +10 +``` +```` + +.. 列:: + +``` +環境変数の読み込みを完全に無効にすることもできます。 +``` + +.. 列:: + +```` +```python +app = Sanic(__name__, load_env=False) +``` +```` + +### Sanic.update_config の使用 + +`Sanic`インスタンスは、config: `app.update_config`をロードするための_very_汎用的なメソッドを持っています。 ファイル、辞書、クラス、またはその他のあらゆる種類のオブジェクトにパスを与えることができます。 + +#### ファイルから + +.. 列:: + +``` +たとえば、次のような `my_config.py` ファイルがあるとします。 +``` + +.. 列:: + +```` +```python +# my_config.py +A = 1 +B = 2 +``` +```` + +.. 列:: + +``` +`app.update_config`にパスを渡すことで、これを設定値としてロードできます。 +``` + +.. 列:: + +```` +```python +>>> app.update_config("/path/to/my_config.py") +>>> print(app.config.A) +1 +``` +```` + +.. 列:: + +``` +このパスは、bash スタイルの環境変数も受け付けます。 +``` + +.. 列:: + +```` +```bash +$ export my_path="/path/to" +``` +```python +app.update_config("${my_path}/my_config.py") +``` +```` + +.. note:: + +``` +ただ、`${environment_variable}`形式で環境変数を指定する必要があり、`$environment_variable`は展開されていないことを覚えておいてください(「プレーン」テキストとして扱われます)。 +``` + +#### 辞書から + +.. 列:: + +``` +`app.update_config` メソッドはプレーン辞書でも動作します。 +``` + +.. 列:: + +```` +```python +app.update_config({"A": 1, "B": 2}) +``` +```` + +#### クラスまたはオブジェクトから + +.. 列:: + +``` +独自の設定クラスを定義し、`app.update_config`に渡すことができます。 +``` + +.. 列:: + +```` +```python +class MyConfig: + A = 1 + B = 2 + +app.update_config(MyConfig) +``` +```` + +.. 列:: + +``` +インスタンス化することもできます +``` + +.. 列:: + +```` +```python +app.update_config(MyConfig()) +``` +```` + +### 鋳造タイプ + +環境変数から読み込むとき、Sanicは期待されるPython型に値をキャストしようとします。 これは特に以下に該当します: + +- `int` +- `float` +- `bool` + +`bool`に関しては、以下の_大文字小文字を区別しません。 + +- **`True`**: `y`, `yes`, `yep`, `yep`, `yup`, `true`, `on`, `enable`, `enabled`, `1` +- **`False`**: `n`, `no`, `f`, `false`, `off`, `disabled`, `0` + +値をキャストできない場合、デフォルトは `str` になります。 + +.. 列:: + +``` +さらに、Sanicは追加の型コンバータを使用して追加の型をキャストするように設定できます。 値を返したり、`ValueError` を発生させたりする任意の呼び出し可能である必要があります。 + +*v21.12* に追加されました +``` + +.. 列:: + +```` +```python +app = Sanic(..., config=Config(converters=[UUID])) +``` +```` + +## 組み込み値 + +| **変数** | **デフォルト** | **説明** | +| ------------------------------------------------------------ | -------------------- | -------------------------------------------------------------------------------------------- | +| ACCESS_LOG | True | アクセスログを無効または有効にする | +| 拡張されました。 | True | [Sanic Extensions](../../plugins/sanic-ext/getting-started.md) が既存の仮想環境にある場合にロードされるかどうかを制御する | +| AUTO_RELOAD | True | ファイルが変更されたときにアプリケーションが自動的にリロードされるかどうかを制御します | +| number@@0 自動登録 | True | 存在しない信号で `app.event()` メソッドを使用して `True` を使用すると自動的に作成され、例外は発生しません。 | +| FORMAT | html | 例外がキャッチされて処理されていない場合のエラー応答のフォーマット | +| 前に進みました。 | X-Forwarded-For | クライアントとプロキシIPを含むHTTPヘッダー「X-Forwarded-For」の名前 | +| FORWARDED_SECRET | なし | 特定のプロキシ サーバーを安全に識別するために使用します (下記参照) | +| タイムアウト | 15.0 | アイドル状態でない接続を強制終了するまで待機する時間 (秒) | +| 検査 | False | インスペクタを有効にするかどうか | +| INSPECTOR_HOST | localhost | インスペクタのホスト | +| インポートします。 | 6457 | インスペクターのポート | +| キー | - | インスペクタの TLS キー | +| INSPECTOR_TLS_CERT | - | インスペクタの TLS 証明書 | +| INSPECTOR_API_KEY | - | インスペクタの API キー | +| ALIVE_TIMEOUT | 120 | TCPコネクションを保持する期間 (秒) | +| 保持します。 | True | Falseのときは生き残りを無効にします | +| MOTD | True | 起動時にMOTD (メッセージ) を表示するかどうか | +| MOTD_表示 | {} | MOTD に追加の任意のデータを表示するキー/値のペア | +| NOISY_EXCEPTIONS | False | すべての `quiet` 例外をログに記録する | +| PROXIES_COUNT | なし | アプリの前にあるプロキシサーバーの数(例:nginx、下記参照) | +| REAL_IP_HEADER | なし | 実際のクライアントIPを含むHTTPヘッダーの名前 | +| 登録 | True | アプリレジストリを有効にするかどうか | +| リクエストのBUFF_サイズ | 65536 | リクエストを一時停止する前のリクエストバッファサイズ。デフォルトは64KBです。 | +| リクエストID | X-リクエストID | リクエスト/相関IDを含む "X-Request-ID" HTTP ヘッダーの名前 | +| 最大サイズ | 100000000 | リクエストの大きさ(バイト)は100メガバイトです | +| 要求される最大ヘッダのサイズ | 8192 | リクエストヘッダがどれくらい大きいか(バイト)、デフォルトは8192バイト | +| 要求時間 | 60 | リクエスト到着までにかかる時間 (秒) | +| タイムアウト | 60 | 応答の処理にかかる時間 (秒) | +| UVLoopを使用します。 | True | `uvloop` を使用するループポリシーをオーバーライドします。 `app.run`でのみサポートされています。 | +| 最大サイズ | 2^20 | 受信メッセージの最大サイズ (バイト) | +| Ping_INTERVAL | 20 | Pingフレームは、ping_interval 秒ごとに送信されます。 | +| ping_timeout | 20 | ping_timeout 秒後に Pong が受信されなかった場合、接続は切断されます | + +.. tip:: FYI + +``` +- The `USE_UVLOOP` value will be ignored if running with Gunicorn. Defaults to `False` on non-supported platforms (Windows). +- The `WEBSOCKET_` values will be ignored if in ASGI mode. +- v21.12 added: `AUTO_EXTEND`, `MOTD`, `MOTD_DISPLAY`, `NOISY_EXCEPTIONS` +- v22.9 added: `INSPECTOR` +- v22.12 added: `INSPECTOR_HOST`, `INSPECTOR_PORT`, `INSPECTOR_TLS_KEY`, `INSPECTOR_TLS_CERT`, `INSPECTOR_API_KEY` +``` + +## タイムアウト + +### 要求時間 + +リクエストタイムアウトは、新しいオープンTCP接続が +Sanicバックエンドサーバに渡された瞬間の時間を測定します。 そして全体の HTTP リクエストが受信された瞬間です。 If the time taken exceeds the +`REQUEST_TIMEOUT` value (in seconds), this is considered a Client Error so Sanic generates an `HTTP 408` response +and sends that to the client. クライアントが非常に大きなリクエストペイロード +を日常的に渡したり、リクエストを非常にゆっくりアップロードした場合、このパラメータの値を高く設定します。 + +### タイムアウト + +応答タイムアウトは、Sanic サーバーが HTTP リクエストを Sanic App に渡す瞬間の時間を測定します。 クライアントにHTTPレスポンスが送信されます。 経過時間が `RESPONSE_TIMEOUT` 値を超えた場合 (秒数) これはサーバーエラーと見なされるため、Sanic は `HTTP 503` レスポンスを生成してクライアントに送信します。 アプリケーションが、応答の +生成を遅らせる長時間のプロセスを持つ可能性がある場合、このパラメータの値を大きく設定します。 + +### ALIVE_TIMEOUT + +#### Keep Aliveとは何ですか? そして、Keep Alive Timeoutの価値は何ですか? + +`Keep-Alive`は、`HTTP 1.1`で導入されたHTTP機能です。 HTTP リクエストを送信するとき クライアント(通常はウェブブラウザアプリケーション)は、レスポンスを送信した後にTCPコネクションを閉じないように、httpサーバー(SANC)を示す「Keep-Alive」ヘッダを設定できます。 これにより、クライアントは既存の TCP コネクションを再利用して、後続の HTTP リクエストを送信することができます。 クライアントとサーバーの両方で、より効率的なネットワークトラフィックを確保します。 + +デフォルトでは、`KEEP_ALIVE`設定変数は、Sanicでは`True`に設定されています。 アプリケーションでこの機能を必要としない場合 応答が送信された直後にすべてのクライアント接続が閉じられるようにするには、 `False` に設定します。 リクエストの `Keep-Alive` ヘッダーに関係なく。 + +サーバーがTCP接続を開いている時間は、サーバー自身によって決まります。 Sanic では、その値は `KEEP_ALIVE_TIMEOUT` 値を使用して設定されます。 デフォルトでは、**120秒**に設定されています。 これは、クライアントが `Keep-Alive` ヘッダーを送信した場合、サーバーは応答を送信した後 120 秒間、TCP コネクションを保持します。 クライアントはその時間内に別の HTTP リクエストを送信するために接続を再利用できます。 + +参考: + +- Apache サーバのデフォルトの keepalving timeout = 5 秒 +- Nginx サーバーのデフォルトの keepalving タイムアウト = 75 秒 +- Nginx パフォーマンスチューニングガイドラインでは、keepliving = 15 seconds +- キャディサーバーのデフォルトキープサバイバルタイムアウト = 120 秒 +- IE (5-9) クライアントハードキープアライブ制限 = 60秒 +- Firefox クライアントのハードキープサバイバル制限 = 115 秒 +- Opera 11 クライアントのハードキープサバイバル制限 = 120 秒 +- Chrome 13+クライアントキープサバイバル制限 > 300秒以上 + +## プロキシ設定 + +format@@0(/guide/advanced/proxy-headers.md) を参照してください。 diff --git a/guide/content/ja/guide/running/development.md b/guide/content/ja/guide/running/development.md new file mode 100644 index 0000000000..5b83a7935e --- /dev/null +++ b/guide/content/ja/guide/running/development.md @@ -0,0 +1,308 @@ +# 開発 + +最初に言及すべきことは、Sanicに統合されているWebサーバーは開発サーバーだけではないということです。 + +_デバッグモード_で有効にしない限り、プロダクションはすぐにすぐに準備ができています。 + +## デバッグモード + +デバッグモードを設定すると、Sanicは出力をより冗長にし、複数のランタイム最適化を無効にします。 + +```python +# server.py +from sanic import Sanic +from sanic.response import json + +app = Sanic(__name__) + +@app.route("/") +async def hello_world(request): + return json({"hello": "world"}) +``` + +```sh +sanic server:app --host=0.0.0 --port=1234 --debug +``` + +.. 危険:: + +``` +Sanicのデバッグモードはサーバーのパフォーマンスを低下させ、本番環境向けではありません。 + +**本番環境でデバッグモードを有効にしないでください** 。 +``` + +## 自動リローダー + +.. 列:: + +``` +Sanicは自動リロードを有効または無効にする方法を提供します。 それを有効にする最も簡単な方法は、CLIの`--reload`引数を使用して自動リロードを有効にすることです。 Pythonファイルが変更されるたびに、リローダーはアプリケーションを自動的に再起動します。 これは開発中に非常に便利です。 + +.. Note:: + + このリローダーはSanicの[worker manager]()を使用している場合にのみ利用できます。 `--single-process` を使用して無効にした場合、再ローダーは利用できません。 +``` + +.. 列:: + +```` +```sh +sanic path.to:app --reload +``` +簡体字プロパティ +```sh +sanic path.to:app -r +``` +```` + +.. 列:: + +``` +ファイル保存時に自動的にリロードしたい追加のディレクトリがある場合(例: HTML テンプレートのディレクトリ) `--reload-dir` を使用して追加できます。 +``` + +.. 列:: + +```` +```sh +sanic path.to:app --reload --reload-dir=/path/to/templates +``` +Or multiple directories, shown here using the shorthand properties +```sh +sanic path.to:app -r -R /path/to/one -R /path/to/two +``` +```` + +## 開発REPL + +Sanic CLI には、アプリケーションとやり取りするために使用できる REPL (別名「read-eval-print loop」)が付属しています。 これはデバッグやテストに役立ちます。 REPL は引数なしで `python` を実行すると得られる対話型シェルです。 + +.. 列:: + +``` +Sanic CLI に `--repl` 引数を渡すことで、REPL を開始できます。 +``` + +.. 列:: + +```` +```sh +sanic path.to.server:app --repl +``` +```` + +.. 列:: + +``` +もしくは、 `--dev`を実行すると、Sanicは自動的にREPLを開始します。 ただし、この場合、REPLを実際に開始する前に「ENTER」キーを押すように求められる場合があります。 +``` + +.. 列:: + +```` +```sh +sanic path.to.server:app --dev +``` +```` + +![](/assets/images/repl.png) + +上のスクリーンショットで見られるように、REPL は自動的にいくつかの変数をグローバル名前空間に追加します。 これらは次のとおりです。 + +- `app` - Sanic applicationインスタンス。 これは、`sanic` CLI に渡されるのと同じインスタンスです。 +- `sanic` - `sanic`モジュール。 `import sanic` を実行したときにインポートされたモジュールと同じです。 +- `do` - モック`Request`オブジェクトを作成し、アプリケーションに渡す関数。 これは、REPLからアプリケーションをテストする場合に便利です。 +- `client` - アプリケーションへのリクエストを行うように設定された `httpx.Client` のインスタンス。 これは、REPLからアプリケーションをテストする場合に便利です。 **注意:** これはあなたの環境で `httpx` がインストールされている場合にのみ利用できます。 + +### Async/Await サポート + +.. 列:: + +``` +REPL は `async`/`await` 構文をサポートしています。つまり、REPL で `await` を使用して非同期操作が完了するまで待つことができます。 これは非同期コードのテストに便利です。 +``` + +.. 列:: + +```` +```python +>>> await app.ctx.db.fetchval("SELECT 1") +1 +``` +```` + +### 変数 `app` + +`app` 変数は、REPL が起動されたときに存在するため、アプリのインスタンスであることに注意してください。 これは、CLI コマンドを実行するときにロードされるインスタンスです。 つまり、ソースコードに加えられ、ワーカーにリロードされた変更は、 `app` 変数には反映されません。 リロードされたアプリケーション・インスタンスとやり取りしたい場合は、REPLを再起動する必要があります。 + +しかし、アドホックのテストとデバッグのためにREPLの元のアプリケーション・インスタンスにアクセスできることも非常に便利です。 + +### `client` 変数 + +[httpx](https://www.python-httpx.org/) があなたの環境にインストールされている場合、`client` 変数はREPLで利用可能になります。 これは実行中のアプリケーションへのリクエストを行うように設定された `httpx.Client` のインスタンスです。 + +.. 列:: + +``` +これを使用するには、クライアントの HTTP メソッドのいずれかを呼び出してください。詳細については、[httpx documentation](https://www.python-httpx.org/api/#client) を参照してください。 +``` + +.. 列:: + +```` +```python +>>> client.get("/") + +``` +```` + +### `do` 関数 + +上で説明したように、REPLが開始された時と同様に、`app` インスタンスが存在し、REPL内で変更されました。 サーバーを再ロードさせるインスタンスへの変更は、 `app` 変数には反映されません。 `do`関数が入ってくるところです。 + +新しいルートを追加するために REPL 内のアプリケーションを変更したとします。 + +```python +>>> @app.get("/new-route") +... async def new_route(request): +... return sanic.json({"hello": "world"}) +... + >>format@@4 +``` + +`do` 関数を使ってリクエストをモックし、アプリケーションに実際の HTTP リクエストのように渡すことができます。 これにより、REPLを再起動せずに新しいルートをテストすることができます。 + +```python +>>> await do("/new-route") +Result(request=, response=) +``` + +`do` 関数は、アプリケーションから返された `Request` と `Response` オブジェクトを含む `Result` オブジェクトを返します。 `NamedTuple`ですので、名前で値にアクセスできます: + +```python +>>> result = await do("/new-route") +>>> result.request + +>>response + +``` + +または、タプルを分割することによって: + +```python +>>> リクエスト、応答 = await do("//new-route") +>>> リクエスト + +>>応答 + +``` + +### `do` vs `client` はいつ使えばいいですか? + +.. 列:: + +``` +**Use `do` when ...** + +- You want to test a route that does not exist in the running application +- You want to test a route that has been modified in the REPL +- You make a change to your application inside the REPL +``` + +.. 列:: + +``` +**Use `client` when ...** + +- You want to test a route that already exists in the running application +- You want to test a route that has been modified in your source code +- You want to send an actual HTTP request to your application +``` + +_v23.12_ に追加されました + +## 開発モードを完了 + +.. 列:: + +``` +デバッグモードにしたい場合は、自動リロードを実行している**と**、`dev=True`を渡すことができます。 これは**debug + auto reload + REPL** と同等です。 + +*v22.3* に追加されました +``` + +.. 列:: + +```` +```sh +sanic path.to:app --dev +``` +簡体字プロパティ +```sh +sanic path.to:app -d + ``` も使用できます。 +```` + +v23.12 の `--dev` フラグに追加すると、REPLを開始することができます。 詳細は、format@@0(./development.md#development-repl) セクションを参照してください。 + +v23.12 では、`--dev` フラグは `--debug --reload -repl` とほぼ同じです。 `--dev` を使用すると、明示的に `--repl` フラグを渡すと、REPL を開始する必要があります。 +v23.12 より前の `--dev` フラグは `--debug --reload` によく似ています。 + +.. 列:: + +``` +`--dev`フラグを使用してREPLを無効にしたい場合は、`--no-repl`を渡すことができます。 +``` + +.. 列:: + +```` +```sh +sanic path.to:app --dev --no-repl +``` +```` + +## 自動 TLS 証明書 + +`DEBUG`モードで実行している場合、Sanicに、localhostの一時的なTLS証明書の設定を処理するよう依頼できます。 これは、`https://`でローカル開発環境にアクセスしたい場合に役立ちます。 + +この機能は [mkcert](https://github.com/FiloSottile/mkcert) または [trustme](https://github.com/python-trio/trustme) のいずれかで提供されています。 どちらも良い選択ですが、いくつかの違いがあります。 `trustme`はPythonライブラリで、`pip`を使って環境にインストールできます。 これにより、envrionment の処理が簡単になりますが、HTTP/3 サーバーの実行時は互換性がありません。 `mkcert` はインストールプロセスにより複雑なものかもしれませんが、ローカルの CA をインストールして使いやすくすることができます。 + +.. 列:: + +``` +`config.LOCAL_CERT_CREATOR` を設定することで、使用するプラットフォームを選択できます。`"auto"`に設定すると、可能な場合は `mkcert` を優先して、どちらかのオプションを選択します。 +``` + +.. 列:: + +```` +```python +app.config.LOCAL_CERT_CREATOR = "auto" +app.config.LOCAL_CERT_CREATOR = "mkcert" +app.config.LOCAL_CERT_CREATOR = "trustme" +``` +```` + +.. 列:: + +``` +Sanic サーバーの実行時間で自動的な TLS を有効にできます。 +``` + +.. 列:: + +```` +```sh +sanic path.to.server:app --auto-tls --debug +``` +```` + +.. 警告:: + +``` +ローカルホストの TLS 証明書 (`mkcert` と `trustme` の両方によって生成された証明書) は、本番環境には適していません。 *real* TLS 証明書の取得方法がわからない場合は、[How to...](../how-to/tls.md) セクションをチェックしてください。 +``` + +_v22.6_に追加されました diff --git a/guide/content/ja/guide/running/inspector.md b/guide/content/ja/guide/running/inspector.md new file mode 100644 index 0000000000..a7e383b479 --- /dev/null +++ b/guide/content/ja/guide/running/inspector.md @@ -0,0 +1,245 @@ +# Inspector + +Sanic Inspector は、Sanic Server の機能です。 Sanic を組み込みの format@@0(./manager.md) で実行している場合は _のみ_ 利用できます。 + +これは、アプリケーションの実行中のインスタンスと対話できるように、アプリケーションのバックグラウンドで_オプション_で動作するHTTPアプリケーションです。 + +.. tip:: INFO + +``` +インスペクターは v22.9 で限定的な容量で導入されましたが、このページのドキュメントでは、v22.12 以上を使用していることを前提としています。 +``` + +## はじめに + +インスペクターはデフォルトで無効になっています。 それを有効にするには、2つのオプションがあります。 + +.. 列:: + +``` +アプリケーションインスタンスを作成するときにフラグを設定します。 +``` + +.. 列:: + +```` +```python +app = Sanic("TestApp", inspector=True) +``` +```` + +.. 列:: + +``` +または、設定値を設定します。 +``` + +.. 列:: + +```` +```python +app = Sanic("TestApp") +app.config.INSPECTOR = True +``` +```` + +.. 警告:: + +``` +設定値を使用している場合は、メインワーカープロセスが開始される前に *必ず* 設定を行わなければなりません。 これは、環境変数であるか、上記のようにアプリケーションインスタンスを作成した直後に設定する必要があることを意味します。 +``` + +## インスペクターの使用 + +インスペクターが実行されると、CLI 経由または直接 Web API に HTTP 経由でアクセスできるようになります。 + +.. 列:: + +```` +**CLI** +```sh +sanic inspect +``` +```` + +.. 列:: + +```` +**Via HTTP** +```sh +curl http://localhost:6457 +``` +```` + +.. note:: + +``` +覚えておいてください、インスペクターはSanicアプリケーション上で実行されていません。それは分離されたアプリケーションであり、分離されたソケット上で公開されています。 +``` + +## 内蔵コマンド + +インスペクターには、次の組み込みコマンドが付属しています。 + +| CLIコマンド | HTTP アクション | 説明 | +| ------------------ | ------------------------------ | ----------------------------- | +| `inspect` | `GET /` | 実行中のアプリケーションに関する基本的な詳細を表示します。 | +| `inspect reload` | `POST /reload` | すべてのサーバワーカーのリロードをトリガーします。 | +| `inspect shutdown` | `POST /shutdown` | すべてのプロセスをシャットダウンします。 | +| `inspect scale N` | `POST /scale`
`{"レプリカ": N}` | ワーカーの数を調整します。 `N`がレプリカの対象数です。 | + +## カスタムコマンド + +インスペクターは簡単に拡張でき、カスタムコマンド(およびエンドポイント)を追加できます。 + +.. 列:: + +``` +`Inspector` クラスをサブクラス化し、任意のメソッドを作成します。 メソッド名の前にアンダースコア(`_`)がない限り、 そして、このメソッドの名前は、インスペクタの新しいサブコマンドになります。 +``` + +.. 列:: + +```` +```python +from sanic import json +from sanic.worker.inspector import Inspector + +class MyInspector(Inspector): + async def something(self, *args, **kwargs): + print(args) + print(kwargs) + +app = Sanic("TestApp", inspector_class=MyInspector, inspector=True) +``` +```` + +これは一般的なパターンでカスタムメソッドを公開します。 + +- CLI: `健全な検査 ` +- HTTP: `POST /` + +新しいメソッドが受け取る引数は、コマンドの使用方法に基づいていることに注意することが重要です。 例えば、上記の `something` メソッドは、すべての位置とキーワードベースのパラメータを受け取ります。 + +.. 列:: + +``` +CLIでは、位置パラメータとキーワードパラメータが、メソッドに位置引数またはキーワード引数として渡されます。 すべての値は次の例外を持つ`str`になります: + +- 代入されていないキーワードパラメータは次のようになります: `True` +- パラメータが`no`で始まる場合を除きます。 次のようになります: `False` +``` + +.. 列:: + +```` +```sh +sanic inspect something one two three --four --no-five --six=6 +``` +In your application log console, you will see: +``` +('one', 'two', 'three') +{'four': True, 'five': False, 'six': '6'} +``` +```` + +.. 列:: + +``` +APIを直接押すことでも同様です。JSONペイロードで公開することでメソッドに引数を渡すことができます。 唯一の注意点は、位置引数は `{"args": [...] }` として公開されるべきであるということです。 +``` + +.. 列:: + +```` +```sh +curl http://localhost:6457/something \ + --json '{"args":["one", "two", "three"], "four":true, "five":false, "six":6}' +``` +In your application log console, you will see: +``` +('one', 'two', 'three') +{'four': True, 'five': False, 'six': 6} +``` +```` + +## プロダクションでの使用 + +.. 危険:: + +``` +製品のインスペクターを公開する前に、このセクションのすべてのオプションを注意深く検討してください。 +``` + +リモート本番インスタンスでインスペクターを実行する場合、TLS 暗号化を要求し、API キー認証を必要とすることでエンドポイントを保護できます。 + +### TLS 暗号化 + +.. 列:: + +``` +TLS を介してインスペクタの HTTP インスタンスには、証明書と鍵へのパスを渡します。 +``` + +.. 列:: + +```` +```python +app.config.INSPECTOR_TLS_CERT = "/path/to/cert.pem" +app.config.INSPECTOR_TLS_KEY = "/path/to/key.pem" +``` +```` + +.. 列:: + +``` +`--secure` フラグまたは `https://` を使用する必要があります。 +``` + +.. 列:: + +```` +```sh +sanic inspect --secure --host= +``` +```sh +curl https://:6457 +``` +```` + +### API キー認証 + +.. 列:: + +``` +Bearer token authenticationでAPIをセキュリティ保護できます。 +``` + +.. 列:: + +```` +```python +app.config.INSPECTOR_API_KEY = "Super-Secret-200" +``` +```` + +.. 列:: + +``` +これには`--api-key` パラメータまたはベアラートトークン認証ヘッダが必要です。 +``` + +.. 列:: + +```` +```sh +sanic inspect --api-key=Super-Secret-200 +``` +```sh +curl http://localhost:6457 -H "Authorization: Bearer Super-Secret-200" +``` +```` + +## 設定 + +[configuration](./configuration.md) を参照してください。 diff --git a/guide/content/ja/guide/running/manager.md b/guide/content/ja/guide/running/manager.md new file mode 100644 index 0000000000..f1c4c0a919 --- /dev/null +++ b/guide/content/ja/guide/running/manager.md @@ -0,0 +1,657 @@ +--- +title: ワーカーマネージャー +--- + +# ワーカーマネージャー + +Worker マネージャーとその機能はバージョン 22.9 で導入されました。 + +_このセクションの詳細は、より高度な使用を目的としています。開始するのに必要なものではありません。_ + +マネージャーの目的は、開発環境と生産環境の間の一貫性と柔軟性を創造することです。 単一のワーカーを実行するか、複数のワーカーを実行するかに関わらず、自動再ロードの有無に関わらず、経験は同じになります。 + +一般的には以下のようになります: + +![](https://user-images.githubusercontent.com/166269/17677618-3b4089c3-6c6a-4ecc-8d7a-7eba2a7f29b0.png) + +Sanic を実行すると、メインプロセスは `WorkerManager` をインスタンス化します。 そのマネージャーは1つ以上の `WorkerProcess` の実行を担当しています。 一般的には2種類のプロセスがあります。 + +- サーバープロセスと +- 非サーバー プロセス + +ユーザーガイドでは、一般的にサーバープロセスを意味する「ワーカー」または「ワーカープロセス」という用語を使用します。 「マネージャー」とは、メインプロセスで実行されている単一のワーカーマネージャーを意味します。 + +## Sanic Server がプロセスを開始する方法 + +Sanic は [spawn](https://docs.python.org/3/library/multiprocessing.html#context-and-start-methods) を使用してプロセスを開始します。 つまり、プロセス/ワーカーごとに、アプリケーションのグローバルスコープが独自のスレッドで実行されます。 あなたがCLIを使ってSanicを実行しない場合\*この実際的な影響。 `__main__`でのみ実行されるように、ブロックの中に実行コードを入れ子にする必要があります。 + +```python +if __name__ == "__main__": + app.run() +``` + +しない場合は、次のようなエラーメッセージが表示される可能性があります。 + +``` +sanic.exceptions.ServerError: Sanic server could not start: [Errno 98] Address already in use. + +This may have happened if you are running Sanic in the global scope and not inside of a `if __name__ == "__main__"` block. + +See more information: https://sanic.dev/en/guide/deployment/manager.html#how-sanic-server-starts-processes +``` + +この問題を解決するには、`__name__ == "__main__"`ブロックの中に Sanic runを入れ子にする必要があります。 ネストの後にこのメッセージを受信し続ける場合、または CLI の使用中にこのメッセージが表示される場合。 使おうとしているポートはマシン上では利用できない 別のポートを選択しなければならない + +### ワーカーを開始 + +すべてのワーカープロセスは開始時に確認を送信する必要があります。 これはボンネットの下で起こり、開発者としてあなたは何もする必要はありません。 ただし、1人以上のワーカーがメッセージを送信しない場合、マネージャーはステータスコード`1`で終了します。 またはワーカープロセスは、起動中に例外をスローします。 例外が発生しない場合、マネージャーは承認のために最大30秒間待機します。 + +.. 列:: + +``` +あなたが開始するために多くの時間が必要になることを知っている状況では、マネージャーをmonkeypatch ことができます。 しきい値はリスナーの中には含まれません。 アプリケーションのグローバルな範囲内のすべての実行時間に制限されます。 + +この問題に遭遇した場合、起動が遅い原因について詳しく調べる必要がある可能性があります。 +``` + +.. 列:: + +```` +```python +from sanic.worker.manager import WorkerManager + +WorkerManager.THRESHOLD = 100 # Value is in 0.1s +``` +```` + +詳細は format@@0(#worker-ack) を参照してください。 + +.. 列:: + +``` +上記のように、Sanic は [spawn](https://docs.python.org/3/library/multiprocessing.html#context-and-start-methods) を使用してワーカープロセスを開始します。 この動作を変更し、異なるstart メソッドを使用することの影響を認識したい場合は、ここに示すように変更できます。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic + +Sanic.start_method = "fork" +``` +```` + +### Worker ack + +すべてのワーカーがサブプロセスで実行されている場合、潜在的な問題が生じます:デッドロック。 これは子プロセスが機能を停止した場合に発生することがありますが、メインプロセスはこれが起こったことを認識していません。 したがって、Sanicサーバーは起動後にメインプロセスに自動的に`ack`メッセージ(確認のための略)を送信します。 + +バージョン22.9では、`ack`のタイムアウトは短く、`5s`に制限されていました。 バージョン 22.12 では、タイムアウトは `30s` に延長されました。 アプリケーションが30秒後にシャットダウンする場合、このしきい値を手動で増やす必要があるかもしれません。 + +.. 列:: + +``` +`WorkerManager.THRESHOLD` の値は `0.1s` 単位です。したがって1分に設定するには、値を `600` に設定してください。 + +この値は、アプリケーションで可能な限り早期に設定する必要があり、理想的にはグローバルスコープで設定する必要があります。 メインプロセスが開始された後に設定することはできません。 +``` + +.. 列:: + +```` +```python +from sanic.worker.manager import WorkerManager + +WorkerManager.THRESHOLD = 600 +``` +```` + +### ゼロダウンタイムの再起動 + +デフォルトでは、ワーカーを再起動すると、Sanicは新しいプロセスを開始する前に既存のプロセスを最初に分解します。 + +本番環境で再起動機能を使用しようとしている場合は、ゼロダウンタイムのリロードに興味があるかもしれません。 これは、新しいプロセスを開始するために、再ローダーを強制的に変更することによって達成することができます。 [ack](#worker-ack) まで待ってから、古いプロセスを分解します。 + +.. 列:: + +``` +マルチプレクサから`zero_downtime`引数を使います。 +``` + +.. 列:: + +```` +```python +app.m.restart(zero_downtime=True) +``` +```` + +_v22.12_ に追加されました + +## ワーカープロセス間で共有コンテキストを使用する + +Python provides a few methods for [exchanging objects](https://docs.python.org/3/library/multiprocessing.html#exchanging-objects-between-processes), [synchronizing](https://docs.python.org/3/library/multiprocessing.html#synchronization-between-processes), and [sharing state](https://docs.python.org/3/library/multiprocessing.html#sharing-state-between-processes) between processes. これには通常、 `multiprocessing` と `ctypes` モジュールのオブジェクトが含まれます。 + +あなたがこれらのオブジェクトとそれらの操作方法に精通しているなら Sanicはこれらのオブジェクトをワーカープロセス間で共有するためのAPIを提供しています。 慣れていない場合は、 上記のリンク先の Python ドキュメントを読んで、共有コンテキストの実装を進める前にいくつかの例を試してみることをお勧めします。 + +format@@0(../basics/app.md#application-context) と同様に、アプリケーションの寿命にわたってアプリケーションが `app と状態を共有することができます。 tx`は、上記の特別なオブジェクトに対して共有コンテキストを提供します。 このコンテキストは `app.shared_ctx` として利用できます。この目的のためにオブジェクトを共有するために **ONLY** を使用します。 + +`shared_ctx` は次のようになります: + +- _NOT_ `int` や `dict` や `list` などの通常のオブジェクトを共有していません +- _違う_マシン上で実行されているSanicインスタンス間での状態の共有 +- _NOT_ 状態をワーカー以外のプロセスと共有 +- **のみ** 同じマネージャーによって管理されたサーバーワーカー間の状態を共有 + +`shared_ctx` に不適切なオブジェクトを追加すると警告になり、エラーにならない可能性があります。 `shared_ctx` に誤って安全でないオブジェクトを追加しないように注意してください。 これらの警告が原因でここに指示された場合、`shared_ctx` で安全でないオブジェクトを誤って使用した可能性があります。 + +.. 列:: + +``` +共有オブジェクトを作成するには、メインプロセスで作成し、 `main_process_start` リスナーの中に添付する必要があります。 +``` + +.. 列:: + +```` +```python +from multiprocessing import Queue + +@app.main_process_start +async def main_process_start(app): + app.shared_ctx.queue = Queue() +``` +```` + +このリスナーの外で `shared_ctx` オブジェクトにアタッチしようとすると、 `RuntimeError` になります。 + +.. 列:: + +``` +`main_process_start` リスナーでオブジェクトを作成し、`shared_ctx` にアタッチした後 アプリケーションインスタンスが利用可能な場所(例:リスナー、ミドルウェア、リクエストハンドラ)は、ワーカー内で利用可能になります。 +``` + +.. 列:: + +```` +```python +from multiprocessing import Queue + +@app.get(char@@2) +async def handler(request): + request.app.shared_ctx.queue.put(1) + ... +``` +```` + +## マルチプレクサへのアクセス + +アプリケーションインスタンスは、Manager や他のワーカープロセスとの相互作用へのアクセスを提供するオブジェクトへのアクセス権を持っています。 オブジェクトは `app.multiple` プロパティとしてアタッチされますが、`app.m` は別名でアクセスしやすくなります。 + +.. 列:: + +``` +たとえば、現在のワーカーの状態にアクセスできます。 +``` + +.. 列:: + +```` +```python +@app.on_request +async def print_state(request: Request): + print(request.app.m.name) + print(request.app.m.pid) + print(request.app.m.state) +``` +``` +Sanic-Server-0-0 +99999 +{'server': True, 'state': 'ACKED', 'pid': 99999, 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), 'starts': 2, 'restart_at': datetime.datetime(2022, 10, 1, 0, 0, 12, 861332, tzinfo=datetime.timezone.utc)} +``` +```` + +.. 列:: + +``` +`multiplace`は、マネージャーを終了するか、ワーカープロセスを再起動するためのアクセス権を持っています +``` + +.. 列:: + +```` +```python +# shutdown the entire application and all processes +app.m.name.terminate() + +# restart the current worker only +app.m.name.restart() + +# restart specific workers only (comma delimited) +app.m.name.restart("Sanic-Server-4-0,Sanic-Server-7-0") + +# restart ALL workers +app.m.name.restart(all_workers=True) # Available v22.12+ +``` +```` + +## ワーカーの状態 + +.. 列:: + +``` +上に示すように、`multiplace` は現在の実行中のワーカーの状態を報告するためにアクセスできます。 しかし、それはまた、実行されているすべてのプロセスのための状態を含みます。 +``` + +.. 列:: + +```` +```python +@app.on_request +async def print_state(request: Request): + print(request.app.m.workers) +``` +``` +{ + 'Sanic-Main': {'pid': 99997}, + 'Sanic-Server-0-0': { + 'server': True, + 'state': 'ACKED', + 'pid': 9999, + 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), + 'starts': 2, + 'restart_at': datetime.datetime(2022, 10, 1, 0, 0, 12, 861332, tzinfo=datetime.timezone.utc) + }, + 'Sanic-Reloader-0': { + 'server': False, + 'state': 'STARTED', + 'pid': 99998, + 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), + 'starts': 1 + } +} +``` +```` + +可能な状態は次のとおりです。 + +- `NONE` - 作業者が作成されましたが、プロセスはまだありません。 +- `IDLE` - プロセスは作成されましたが、まだ実行されていません +- `STARTING` - プロセスが開始されています +- `STARTED` - プロセスが開始されました +- `ACKED` - プロセスが開始され、承認を送信しました (通常はサーバーのプロセスのみ) +- `JOINED` - プロセスが終了し、メインプロセスに参加しました +- `TERMINATED` - プロセスが終了し、終了しました +- `RESTARTING` - プロセスが再起動しています +- `FAILED` - プロセスが例外に遭遇し、実行されなくなりました +- `COMPLETED` - プロセスが完了し、終了しました + +## 組み込みの非サーバー プロセス + +前述のように、マネージャーは非サーバプロセスを実行することもできます。 Sanicには2種類の非サーバープロセスが組み込まれており、format@@0(#running-custom-processes) を使用できます。 + +二つの組み込みプロセスは + +- [auto-reloader](./development.md#automatic-reloader) は、ファイルシステムの変更を監視し、再起動をトリガーするために必要に応じて有効になります +- [inspector](#inspector) オプションで実行中のインスタンスの状態への外部アクセスを提供することができます + +## Inspector + +Sanicは、CLIに「multiplane」の状態と機能を公開する能力を持っています。 現在、実行中のSanicインスタンスと同じマシン上でCLIコマンドを実行する必要があります。 デフォルトではインスペクターは無効になっています。 + +.. 列:: + +``` +有効にするには、設定値を `True` に設定します。 +``` + +.. 列:: + +```` +```python +app.config.INSPECTOR = True +``` +```` + +これで、これらの CLI コマンドのいずれかを実行することができます。 + +``` +健全な検査 reload サーバワーカーのリロードをトリガーする +sanic inspect shutdown the application and all processes +sanic inspect scale N ワーカー数を N +sanic inspect カスタムコマンドを実行 +``` + +![](https://user-images.githubusercontent.com/166269/190099384-2f2f3fae-22d5-4529-b279-8446f6b5f9bd.png) + +.. 列:: + +``` +これは、マシン上の小さなHTTPサービスを公開することで動作します。設定値を使用して場所を制御できます。 +``` + +.. 列:: + +```` +```python +app.config.INSPECTOR_HOST = "localhost" +app.config.INSPECTOR_PORT = 6457 +``` +```` + +format@@0(./inspector.md) は、インスペクターで何が可能かを知ることができます。 + +## カスタムプロセスの実行 + +Sanicで管理されたカスタムプロセスを実行するには、呼び出し可能ファイルを作成する必要があります。 そのプロセスが長時間実行されることを意図している場合は、`SIGINT`または`SIGTERM`信号によるシャットダウンコールを処理する必要があります。 + +.. 列:: + +``` +Pythonでそれを行う最も簡単な方法は、`KeyboardInterrupt`でループをラップすることです。 + +ボットのような別のアプリケーションを実行する場合。 この信号を処理する能力を持っている可能性があります おそらく何もする必要はありません +``` + +.. 列:: + +```` +```python +from time import slep + +def my_process(foo): + try: + while True: + sleep(1) + except KeyboardInterrupt: + print("done") +``` +```` + +.. 列:: + +``` +その呼び出し可能ファイルは `main_process_ready` リスナーに登録する必要があります。 重要なのは、format@@0(#using-shared-context-between-worker-processes) オブジェクトを登録するのと同じ場所ではないことです。 +``` + +.. 列:: + +```` +```python +@app.main_process_ready +async def ready(app: Sanic, _): +# app.manager.manage(, , ) + app.manager.manage("MyProcess", my_process, {"foo": "bar"}) +``` +```` + +### Transient v.耐久性のあるプロセス + +.. 列:: + +``` +`manage` メソッドでプロセスを管理する場合は、プロセスをトランジェントまたは耐久性にするオプションがあります。 一時的なプロセスは、オートリローダーによって再起動され、耐久性のあるプロセスは実行されません。 + +デフォルトでは、すべてのプロセスは耐久性があります。 +``` + +.. 列:: + +```` +```python +@app.main_process_ready +async def ready(app: Sanic, _): + app.manager.manage( + "MyProcess", + my_process, + {"foo": "bar"}, + transitent=True, + ) +``` +```` + +### 追跡されていないプロセス + +Sanicは全プロセスの状態を追跡します。 これは、 [multiplexer](./manager#access-to-the-multiplexer) オブジェクト、または [Inspector](./manager#inspector) からプロセスの状態にアクセスできることを意味します。 + +詳細は format@@0(./manager#worker-state) を参照してください。 + +バックグラウンドプロセスが長時間実行されていない場合もあります。 完了まで一度実行し、それらは終了します。 完了すると `FAILED` または `COMPLETED` のいずれかになります。 + +.. 列:: + +``` +長時間実行されていないプロセスを実行している場合は、`manage`メソッドに`tracked=False`を設定することでトラッキングを解除できます。 これは、プロセスが完了すると、追跡されたプロセスのリストから削除されることを意味します。 実行中のプロセスの状態のみを確認できます。 +``` + +.. 列:: + +```` +```python +@app.main_process_ready +async def ready(app: Sanic, _): + app.manager.manage( + "OneAndDone", + do_once, + {}, + tracked=False, + ) +``` +```` + +_v23.12_ に追加されました + +### 再起動可能なカスタムプロセス + +トランジェントであるカスタムプロセスは、**常に**再び起動可能になります。 つまり、自動再起動は想定どおりに動作します。 ただし、プロセスを手動で再起動できますが、オートリローダーで再起動できない場合はどうなりますか? + +.. 列:: + +``` +このシナリオでは、`manage`メソッドで`restartable=True`を設定できます。 これにより、手動でプロセスを再起動することができますが、自動再起動では再起動しません。 +``` + +.. 列:: + +```` +```python +@app.main_process_ready +async def ready(app: Sanic, _): + app.manager.manage( + "MyProcess", + my_process, + {"foo": "bar"}, + restartable=True, + ) +``` +```` + +.. 列:: + +``` +マルチプレクサから手動でそのプロセスを再起動できるようになりました。 +``` + +.. 列:: + +```` +```python +@app.get("/restart") +async def restart_handler(request: Request): + request.app.m.restart("Sanic-MyProcess-0") + return json({"foo": request.app.m.name}) +``` +```` + +_v23.12_ に追加されました + +### オンザフライプロセス管理 + +カスタムプロセスは通常 `main_process_ready` リスナーに追加されます。 ただし、アプリケーションの開始後にプロセスを追加したい場合があります。 たとえば、リクエストハンドラからプロセスを追加したい場合があります。 マルチプレクサはこれを行う方法を提供します。 + +.. 列:: + +``` +マルチプレクサへの参照ができたら、プロセスを追加するために `manage` を呼び出すことができます。 マネージャーの `manage` メソッドと同じ動作をします。 +``` + +.. 列:: + +```` +```python +@app.post("/start") +async def start_handler(request: Request): + request.app.m.manage( + "MyProcess", + my_process, + {"foo": "bar"}, + workers=2, + ) + return json({"foo": request.app.m.name}) +``` +```` + +_v23.12_ に追加されました + +## シングルプロセスモード + +.. 列:: + +``` +複数のプロセスの実行をオプトアウトしたい場合は、1つのプロセスでのみSanicを実行できます。 この場合、マネージャーは実行されません。 また、プロセスを必要とする機能(自動リロード、インスペクタなど)にもアクセスできません。 +``` + +.. 列:: + +```` +```sh +sanic path.to.server:app --single-process +``` +```python +if __name__ == "__main__": + app.run(single_process=True) +``` +```python +if __name__ == "__main__": + app.prepare(single_process=True) + Sanic.serve_single() +``` +```` + +## Sanic and multiprocessing + +Sanic は [`multiprocessing` module](https://docs.python.org/3/library/multiprocessing.html) を使用してワーカープロセスを管理します。 通常、Sanic の機能を妨げる可能性があるため、このモジュールの低レベル使用(start メソッドの設定など)は避けるべきです。 + +### Pythonでメソッドを開始 + +Sanicが何をしようとしているのかを説明する前に、`start_method`とは何か、なぜそれが重要なのかを理解することが重要です。 Python では一般的に、プロセスを開始するための 3 つの異なるメソッドが使用できます。 + +- `fork` +- `spawn` +- `forkserver` + +`fork` と `forkserver` メソッドは Unix システムでのみ使用でき、`spawn` は Windows で利用できる唯一のメソッドです。 選択肢がある Unix システムでは、`fork` は一般的にデフォルトのシステムメソッドです。 + +これらのメソッドの違いについては、format@@0(https://docs.python.org/3/library/multiprocessing.html#context-and-start-methods)を参照してください。 ただし、重要なことは、基本的に親プロセスのメモリ全体を子プロセスにコピーすることです。 `spawn` は新しいプロセスを作成し、そのプロセスにアプリケーションをロードします。 CLIを使っていない場合は、`__name__ == "__main__"`ブロックの中に、Sanic `run` を入れ子にする必要があります。 + +### サニックメソッドと開始方法 + +デフォルトでは、Sanicはstartメソッドとして`spawn`を使用します。 これは、Windows で利用可能な唯一の方法であり、Unix システムで最も安全な方法であるためです。 + +.. 列:: + +``` +もしあなたが Unix システムで Sanic を実行していて、代わりに `fork` を使用したい場合。 `Sanic`クラスに`start_method`を設定することで可能です。 他のモジュールをインポートする前に、アプリケーションで可能な限り早期にこれを行い、グローバルスコープで理想的にこれを行う必要があります。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic + +Sanic.start_method = "fork" +``` +```` + +### `RuntimeError`をクリアする + +次のような `RuntimeError` を受け取ったかもしれません。 + +``` +RuntimeError: Startメソッド'spawn'が要求されましたが、'fork'は既に設定されていました。 +``` + +そうであれば、アプリケーションのどこかでSanicがやろうとしていることと競合するstartメソッドを設定しようとしていることを意味します。 これを解決するにはいくつかのオプションがあります。 + +.. 列:: + +``` +**OPTION 1:** Sanicに、startメソッドが設定されており、再度設定しないようにすることができます。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic + +Sanic.START_METHOD_SET = True +``` +```` + +.. 列:: + +``` +**OPTION 2:** Sanicに、 `fork` を使うつもりで、 `spawn` を使わないように伝えることができます。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic + +Sanic.start_method = "fork" +``` +```` + +.. 列:: + +``` +**OPTION 3:** `multiprocessing` start メソッドを設定することで、`fork` の代わりに `spawn` を使用するようにPythonに指示できます。 +``` + +.. 列:: + +```` +```python +import multiprocessing + +multiprocessing.set_start_method("spawn") +``` +```` + +これらのいずれかのオプションでは、アプリケーションでできるだけ早くこのコードを実行する必要があります。 具体的なシナリオによっては、いくつかのオプションを組み合わせる必要がある場合があります。 + +.. note:: + +```` +The potential issues that arise from this problem are usually easily solved by just allowing Sanic to be in charge of multiprocessing. This usually means making use of the `main_process_start` and `main_process_ready` listeners to deal with multiprocessing issues. For example, you should move instantiating multiprocessing primitives that do a lot of work under the hood from the global scope and into a listener. + +```python +# This is BAD; avoid the global scope +from multiprocessing import Queue + +q = Queue() +``` + +```python +# This is GOOD; the queue is made in a listener and shared to all the processes on the shared_ctx +from multiprocessing import Queue + +@app.main_process_start +async def main_process_start(app): + app.shared_ctx.q = Queue() +``` +```` diff --git a/guide/content/ja/guide/running/running.md b/guide/content/ja/guide/running/running.md new file mode 100644 index 0000000000..98c4b45a5e --- /dev/null +++ b/guide/content/ja/guide/running/running.md @@ -0,0 +1,585 @@ +--- +title: 実行中のサニック +--- + +# 実行中のサニック + +Sanicは独自の内部Webサーバーを搭載しています。 ほとんどの状況では、これはデプロイに最適な方法です。 また、SanicをASGI対応のWebサーバーにバンドルされたASGIアプリとしてデプロイすることもできます。 + +## Sanic Server + +Sanicを走る主な方法は、付属の [CLI](#sanic-cli)を使うことです。 + +```sh +sanic path.to.server:app +``` + +この例では、Sanicは`path.to.server`というPythonモジュールを探すように指示されています。 このモジュールの中では、`app`という名前のグローバル変数を探します。これは、`Sanic(...)`のインスタンスでなければなりません。 + +```python +# ./path/to/server.py +from sanic import Sanic, Request, json + +app = Sanic("TestApp") + +@app.get("/") +async def handler(request: Request): + return json({"foo": "bar"}) +``` + +`app.run` をスクリプトとして呼び出すには、format@@0(#low-level-apprun) にドロップダウンすることもできます。 ただし、このオプションを選択した場合は、 `multiprocessing` で発生する可能性のある問題をより快適に処理する必要があります。 + +### Worker + +.. 列:: + +``` +デフォルトでは、Sanicはメインプロセスと単一のワーカープロセスを実行します([worker manager](./manager)を参照してください)。 d) 詳細については)。 + +ジュースをクランクアップするには、実行引数に含まれるワーカーの数を指定します。 +``` + +.. 列:: + +```` +```sh +sanic server:app --host=0.0.0.0 --port=1337 --works=4 +``` +```` + +Sanicは自動的に複数のプロセスとそれらの間のルートトラフィックを回します。 利用可能なプロセッサを持っている数だけの労働者をお勧めします。 + +.. 列:: + +``` +最大CPUパフォーマンスを得る最も簡単な方法は、 `--fast` オプションを使用することです。 これは自動的にシステム制約を与えられたワーカーの最大数を実行します。 + +*v21.12* に追加されました +``` + +.. 列:: + +```` +```sh +sanic server:app --host=0.0.0.0 --port=1337 --fast +``` +```` + +バージョン22.9では、Sanicは開発サーバーと本番サーバーの間でより一貫性と柔軟性を提供する新しいワーカーマネージャを導入しました。 format@@0(./manager.md) を読んで、worker の詳細を確認してください。 + +.. 列:: + +``` +Sanicを単一のプロセスでのみ実行したい場合は、run引数に`single_process`を指定します。 これは自動再読み込みを行い、ワーカーマネージャーが利用できなくなることを意味します。 + +*v22.9* に追加されました +``` + +.. 列:: + +```` +```sh +sanic server:app --host=0.0.0.0 --port=1337 --single-process +``` +```` + +### コマンドで実行 + +#### Sanic CLI + +すべてのオプションを見るには `sanic --help` を使います。 + +.. attrs:: +:title: Sanic CLI help output +:class: details + +```` +```text +$ sanic --help + + ▄███ █████ ██ ▄█▄ ██ █ █ ▄██████████ + ██ █ █ █ ██ █ █ ██ + ▀███████ ███▄ ▀ █ █ ██ ▄ █ ██ + ██ █████████ █ ██ █ █ ▄▄ + ████ ████████▀ █ █ █ ██ █ ▀██ ███████ + + To start running a Sanic application, provide a path to the module, where + app is a Sanic() instance: + + $ sanic path.to.server:app + + Or, a path to a callable that returns a Sanic() instance: + + $ sanic path.to.factory:create_app --factory + + Or, a path to a directory to run as a simple HTTP server: + + $ sanic ./path/to/static --simple + +Required +======== + Positional: + module Path to your Sanic app. Example: path.to.server:app + If running a Simple Server, path to directory to serve. Example: ./ + +Optional +======== + General: + -h, --help show this help message and exit + --version show program's version number and exit + + Application: + --factory Treat app as an application factory, i.e. a () -> callable + -s, --simple Run Sanic as a Simple Server, and serve the contents of a directory + (module arg should be a path) + --inspect Inspect the state of a running instance, human readable + --inspect-raw Inspect the state of a running instance, JSON output + --trigger-reload Trigger worker processes to reload + --trigger-shutdown Trigger all processes to shutdown + + HTTP version: + --http {1,3} Which HTTP version to use: HTTP/1.1 or HTTP/3. Value should + be either 1, or 3. [default 1] + -1 Run Sanic server using HTTP/1.1 + -3 Run Sanic server using HTTP/3 + + Socket binding: + -H HOST, --host HOST + Host address [default 127.0.0.1] + -p PORT, --port PORT + Port to serve on [default 8000] + -u UNIX, --unix UNIX + location of unix socket + + TLS certificate: + --cert CERT Location of fullchain.pem, bundle.crt or equivalent + --key KEY Location of privkey.pem or equivalent .key file + --tls DIR TLS certificate folder with fullchain.pem and privkey.pem + May be specified multiple times to choose multiple certificates + --tls-strict-host Only allow clients that send an SNI matching server certs + + Worker: + -w WORKERS, --workers WORKERS + Number of worker processes [default 1] + --fast Set the number of workers to max allowed + --single-process Do not use multiprocessing, run server in a single process + --legacy Use the legacy server manager + --access-logs Display access logs + --no-access-logs No display access logs + + Development: + --debug Run the server in debug mode + -r, --reload, --auto-reload + Watch source directory for file changes and reload on changes + -R PATH, --reload-dir PATH + Extra directories to watch and reload on changes + -d, --dev debug + auto reload + --auto-tls Create a temporary TLS certificate for local development (requires mkcert or trustme) + + Output: + --coffee Uhm, coffee? + --no-coffee No uhm, coffee? + --motd Show the startup display + --no-motd No show the startup display + -v, --verbosity Control logging noise, eg. -vv or --verbosity=2 [default 0] + --noisy-exceptions Output stack traces for all exceptions + --no-noisy-exceptions + No output stack traces for all exceptions + +``` +```` + +#### モジュールとして + +.. 列:: + +``` +Sanic アプリケーションはモジュールとして直接呼び出すこともできます。 +``` + +.. 列:: + +```` +```bash +python -m sanic server.app --host=0.0.0.0 --port=1337 --works=4 +``` +```` + +#### 工場の利用 + +非常に一般的な解決策は、アプリケーションをグローバル変数としてではなく\*開発することですが、代わりにファクトリパターンを使用することです。 この文脈で、"factory"とは、`Sanic(...)`のインスタンスを返す関数を意味します。 + +.. 列:: + +``` +`server.py`にこれがあるとします。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic + +def create_app() -> Sanic: + app = Sanic("MyApp") + + return app +``` +```` + +.. 列:: + +``` +このアプリケーションは、CLIで明示的にファクトリとして参照することで実行できます。 +``` + +.. 列:: + +```` +```sh +sanic server:create_app --factory +``` +Or, explicitly like this: +```sh +sanic "server:create_app()" +``` +Or, implicitly like this: +```sh +sanic server:create_app +``` + +*Implicit command added in v23.3* +```` + +### 低レベル `app.run` + +`app.run`を使用する場合は、他のスクリプトと同様にPythonファイルを呼び出します。 + +.. 列:: + +``` +`app.run` は name-main ブロックの中に正しく入れ子にする必要があります。 +``` + +.. 列:: + +```` +```python +# server.py +app = Sanic("MyApp") + +if __name__ == "__main__": + app.run() +``` +```` + +.. 危険:: + +```` +Be *careful* when using this pattern. A very common mistake is to put too much logic inside of the `if __name__ == "__main__":` block. + +🚫 This is a mistake + +```python +from sanic import Sanic +from my.other.module import bp + +app = Sanic("MyApp") + +if __name__ == "__main__": + app.blueprint(bp) + app.run() +``` + +If you do this, your [blueprint](../best-practices/blueprints.md) will not be attached to your application. This is because the `__main__` block will only run on Sanic's main worker process, **NOT** any of its [worker processes](../deployment/manager.md). This goes for anything else that might impact your application (like attaching listeners, signals, middleware, etc). The only safe operations are anything that is meant for the main process, like the `app.main_*` listeners. + +Perhaps something like this is more appropriate: + +```python +from sanic import Sanic +from my.other.module import bp + +app = Sanic("MyApp") + +if __name__ == "__mp_main__": + app.blueprint(bp) +elif __name__ == "__main__": + app.run() +``` +```` + +低レベルの`run` APIを使用するには、`sanic.Sanic`のインスタンスを定義した後、以下のキーワード引数を使用してrunメソッドを呼び出すことができます。 + +| パラメータ | デフォルト | 説明 | +| :---------------------------------------: | :--------------: | :--------------------------------------------------------------------------- | +| **ホスト** | `"127.0.0.1"` | サーバーをホストするためのアドレス。 | +| **ポート** | `8000` | サーバーをホストするためのポート。 | +| **unix** | `なし` | (TCP の代わりに) サーバーをホストする Unix ソケット名。 | +| **dev** | `False` | `debug=True`と`auto_reload=True`と同じです。 | +| **debug** | `False` | デバッグ出力を有効にします(サーバーを遅くします)。 | +| **ssl** | `なし` | ワーカーの SSL 暗号化の SSLContext です。 | +| **sock** | `なし` | サーバーが接続を受け入れるソケット。 | +| **works** | `1` | spawn するワーカープロセスの数。 高速では使用できません。 | +| **ループ** | `なし` | 非同期互換イベントループ。 何も指定されていない場合、Sanic は独自のイベントループを作成します。 | +| **protocol** | `HttpProtocol` | asyncio.protocolのサブクラス。 | +| **バージョン** | `HTTP.VERSION_1` | 使用する HTTP バージョン (`HTTP.VERSION_1` または `HTTP.VERSION_3` )。 | +| **access_log** | `True` | リクエスト処理時にログを有効にします(サーバーが大幅に遅くなります)。 | +| **auto_reload** | `なし` | ソースディレクトリの自動リロードを有効にします。 | +| **reload_dir** | `なし` | 自動リローダーが監視すべきディレクトリへのパスまたはリスト。 | +| **noisy_exceptions** | `なし` | 騒々しい例外をグローバルに設定するかどうか。 はいずれもデフォルトのままにします。 | +| **motd** | `True` | 起動時のメッセージを表示するかどうかを設定します。 | +| **motd_display** | `なし` | スタートアップメッセージに表示する追加のキー/値情報を持つディクト | +| **fast** | `False` | ワーカープロセスを最大化するかどうか。 ワーカーでは使用できません。 | +| **verbosity** | `0` | ログの詳細レベル。 最大は 2 です。 | +| **auto_tls** | `False` | ローカル開発用の TLS 証明書を自動作成するかどうか。 生産のためではありません。 | +| **single_process** | `False` | 単一のプロセスでSanicを実行するかどうか。 | + +.. 列:: + +``` +たとえば、パフォーマンスを向上させるためにアクセスログをオフにし、カスタムホストとポートにバインドすることができます。 +``` + +.. 列:: + +```` +```python +# server.py +app = Sanic("MyApp") + +if __name__ == "__main__": + app.run(host='0.0.0.0', port=1337, access_log=False) +``` +```` + +.. 列:: + +``` +`app.run(...)`を持つPythonスクリプトを実行してください。 +``` + +.. 列:: + +```` +```sh +python server.py +``` +```` + +もう少し高度な実装では、 `app.run` は `app.prepare` と `Sanic.serve` をフードの下で呼び出すことを知っておくと良いでしょう。 + +.. 列:: + +``` +したがって、これらは等価です: +``` + +.. 列:: + +```` +```python +if __name__ == "__main__": + app.run(host='0.0.0.0', port=1337, access_log=False) +``` +```python +if __name__ == "__main__": + app.prepare(host='0.0.0.0', port=1337, access_log=False) + Sanic.serve() +``` +```` + +.. 列:: + +``` +アプリケーションを複数のポートにバインドする必要がある場合に便利です。 +``` + +.. 列:: + +```` +```python +if __name__ == "__main__": + app1.prepare(host='0.0.0.0', port=9990) + app1.prepare(host='0.0.0.0', port=9991) + app2.prepare(host='0.0.0.0', port=555555) + Sanic.serve() +``` +```` + +### Sanic Simple Server + +.. 列:: + +``` +場合によっては、提供する必要がある静的ファイルのディレクトリがあります。 特に、localhostサーバーを素早く立ち上げるのに便利です。 Sanic shipにはシンプルなサーバーがあり、ディレクトリにそれを指すだけで済みます。 +``` + +.. 列:: + +```` +```sh +sanic ./path/to/dir --simple +``` +```` + +.. 列:: + +``` +これは自動リロードと組み合わせることもできます。 +``` + +.. 列:: + +```` +```sh +sanic ./path/to/dir --simple -reload -reload-dir=./path/to/dir +``` +```` + +_V21.6_に追加されました + +### HTTP/3 + +Sanic server provides HTTP/3 support using [aioquic](https://github.com/aiortc/aioquic). HTTP/3を使用するには**インストールする必要があります** + +```sh +pip install sanic aioquic +``` + +```sh +pip install sanic[http3] +``` + +HTTP/3 を開始するには、アプリケーションの実行時に明示的にリクエストする必要があります。 + +.. 列:: + +```` +```sh +sanic path.to.server:app --http=3 +``` + +```sh +sanic path.to.server:app -3 +``` +```` + +.. 列:: + +```` +```python +app.run(version=3) +``` +```` + +HTTP/3 と HTTP/1.1 の両方のサーバーを同時に実行するには、v22.3 で導入された [application multi-serve] (../release-notes/v22.3.html#application-multi-serve) を使用します。 これにより自動的に [Alt-Svc](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Alt-Svc) ヘッダーがHTTP/1.1 リクエストに追加され、クライアントにHTTP/3 としても利用可能であることを知らせます。 + +.. 列:: + +```` +```sh +sanic path.to.server:app --http=3 -http=1 +``` + +```sh +sanic path.to.server:app -3 -1 +``` +```` + +.. 列:: + +```` +```python +app.prepare(version=3) +app.prepare(version=1) +Sanic.serve() +``` +```` + +HTTP/3 は TLS を必要とするため、TLS 証明書なしでは HTTP/3 サーバーを起動できません。 `DEBUG`モードの場合は、format@@0(../how-to/tls.html) または `mkcert` を使用してください。 現在、HTTP/3 に対する自動的な TLS 設定は `trustme` と互換性がありません。 詳細は [development](./development.md) を参照してください。 + +_v22.6_に追加されました + +## ASGI + +SanicもASGIに準拠しています。 つまり、ご希望のASGIウェブサーバーを使用してSanicを実行することができます。 ASGI の 3 つの主な実装は [Daphne](http://github.com/django/daphne), [Uvicorn](https://www.uvicorn.org/), および [Hypercorn](https://pgjones.gitlab.io/hypercorn/index.html) です。 + +.. 警告:: + +``` +DaphneはASGIの`lifespan`プロトコルをサポートしていないため、Sanicを実行するために使用することはできません。詳細は[課題 264](https://github.com/django/daphne/issues/264)を参照してください。 +``` + +適切な実行方法については、ドキュメントに従ってください。しかし、次のようになります。 + +```sh +uvicorn myapp:app +``` + +```sh +hypercorn myapp:app +``` + +ASGIを使用する場合、いくつか注意すべきことがあります: + +1. Sanic ウェブサーバを使用する場合、websocketsは`websockets`パッケージを使用して実行されます。 ASGI モードでは、ウェブソケットは ASGI サーバで管理されるため、このパッケージは必要ありません。 +2. ASGIの寿命プロトコル は、起動とシャットダウンの2つのサーバーイベントのみをサポートしています。 Sanicは、起動前、起動後、シャットダウン前、シャットダウン後の4つを持っています。 したがって、ASGIモードで 起動イベントとシャットダウンイベントは連続して実行され、実際にはサーバープロセスの開始と終了の周りには実行されません(それ以降はASGIサーバーによって制御されます)。 ですから、`after_server_start` と `before_server_stop` を使うのがベストです。 + +### トリオ + +SanicはTrioでの実行を実験的にサポートしています。 + +```sh +hypercorn -k trio myapp:app +``` + +## グニコーン + +[Gunicorn](http://gunicorn.org/) ("Green Unicorn") は UNIX ベースのオペレーティングシステム用の WSGI HTTP Server です。 これは、RubyのUnicornプロジェクトから移植されたpre-forkワーカーモデルです。 + +SanicalアプリケーションをGunicornで実行するには、 [uvicorn](https://www.uvicorn.org/)のアダプターで使用する必要があります。 uvicornがインストールされていることを確認し、Gunicorn.worker.UvicornWorker\`でGunicornワーカークラスの引数を実行します: + +```sh +gunicorn myapp:app --bind 0.0.0.0.0:1337 --worker-class uvicorn.worker.UvicornWorker +``` + +詳細は [Gunicorn Docs](http://docs.gunicorn.org/en/latest/settings.html#max-requests) を参照してください。 + +.. 警告:: + +``` +一般的には、必要な場合以外は `gunicorn` を使用しないことをお勧めします。 Sanic Serverは、本番環境でSanicを実行するためにプライミングされています。この選択を行う前に、慎重に考慮事項を検討してください。 Gunicornは多くの設定オプションを提供しますが、Sanicを最速で走らせるための最良の選択ではありません。 +``` + +## パフォーマンスに関する考慮事項 + +.. 列:: + +``` +本番環境で実行する場合は、`debug` をオフにしてください。 +``` + +.. 列:: + +```` +```sh +sanic path.to.server:app +``` +```` + +.. 列:: + +``` +`access_log` をオフにすると、Sanicも最速に動作します。 + +まだアクセスログが必要で、このパフォーマンスを向上させたい場合は、[Nginx をプロキシとして使用することを検討してください](。 nginx.md)、そしてアクセスログを処理させます。Pythonが扱えるどんなものよりもずっと速くなります。 +``` + +.. 列:: + +```` +```sh +sanic path.to.server:app --no-access-logs +``` +```` diff --git a/guide/content/ja/help.md b/guide/content/ja/help.md new file mode 100644 index 0000000000..8c4e475210 --- /dev/null +++ b/guide/content/ja/help.md @@ -0,0 +1,32 @@ +--- +title: ヘルプが必要ですか? +layout: メイン +--- + +# ヘルプが必要ですか? + +開発者の積極的なコミュニティとして、私たちはお互いをサポートしようとしています。 ヘルプが必要な場合は、以下のいずれかをお試しください。 + +.. 列:: + +``` +### Discord 💬 + +Best place to turn for quick answers and live chat + +`#sanic-support` channel on the [Discord server](https://discord.gg/FARQzAEMAA) +``` + +.. 列:: + +``` +### Community Forums :busts_in_sシルエット: + + + +`Questions and Help` category on the [Forums](https://community.sanicframework.org/c/questions-and-help/6) +``` + +--- + +[Stack Overflow](https://stackoverflow.com/questions/tagged/sanic)の`[sanic]`タグも積極的に監視しています。 diff --git a/guide/content/ja/index.md b/guide/content/ja/index.md new file mode 100644 index 0000000000..c2b0245bdf --- /dev/null +++ b/guide/content/ja/index.md @@ -0,0 +1,334 @@ +--- +title: 超高速非同期のPythonウェブフレームワーク +layout: ホーム +features: + - title: シンプルで軽量な + details: スマートなデフォルトを持つ直感的なAPIと肥満がないと、アプリの構築を直進できます。 + - title: 不要で柔軟性があります + details: ツールを使うことで制約がなくても構築方法を構築できます。 + - title: パフォーマンスとスケーラブルな + details: 主な関心事として速度とスケーラビリティをゼロから構築しました。 大小のWebアプリケーションに電力を供給する準備ができています。 + - title: 生産準備完了 + details: ボックスの外には、Webアプリケーションに電力を供給するためのWebサーバーが付属しています。 + - title: 何百万人に信頼されています + details: Sanic は PyPI 上で最も人気のあるフレームワークの一つであり、非同期対応フレームワーク + - title: コミュニティ主導型の + details: このプロジェクトはコミュニティによって運営されています。 +--- + +### ⚡ 超高速非同期Pythonウェブフレームワーク + +.. attrs:: +:class: 列は mt-6 + +``` +.. attrs:: + :class: column is-4 + + #### Simple and lightweight + + Intuitive API with smart defaults and no bloat allows you to get straight to work building your app. + +.. attrs:: + :class: column is-4 + + #### Unopinionated and flexible + + Build the way you want to build without letting your tooling constrain you. + +.. attrs:: + :class: column is-4 + + #### Performant and scalable + + Built from the ground up with speed and scalability as a main concern. It is ready to power web applications big and small. + +.. attrs:: + :class: column is-4 + + #### Production ready + + Out of the box, it comes bundled with a web server ready to power your web applications. + +.. attrs:: + :class: column is-4 + + #### Trusted by millions + + Sanic is one of the overall most popular frameworks on PyPI, and the top async enabled framework + +.. attrs:: + :class: column is-4 + + #### Community driven + + The project is maintained and run by the community for the community. +``` + +.. attrs:: +:class: is-size-3 mt-6 + +``` +**機能とツールを使って。** +``` + +.. attrs:: +:class: is-size-3 ml-6 + +``` +**そしていくつかの {span:has-text-primary:you wouldn't believe}.** +``` + +.. tab:: Production-grade + +```` +After installing, Sanic has all the tools you need for a scalable, production-grade server—out of the box! + +Including [full TLS support](/en/guide/how-to/tls). + +```python +from sanic import Sanic +from sanic.response import text + +app = Sanic("MyHelloWorldApp") + +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` + +```sh +sanic path.to.server:app +[2023-01-31 12:34:56 +0000] [999996] [INFO] Sanic v22.12.0 +[2023-01-31 12:34:56 +0000] [999996] [INFO] Goin' Fast @ http://127.0.0.1:8000 +[2023-01-31 12:34:56 +0000] [999996] [INFO] mode: production, single worker +[2023-01-31 12:34:56 +0000] [999996] [INFO] server: sanic, HTTP/1.1 +[2023-01-31 12:34:56 +0000] [999996] [INFO] python: 3.10.9 +[2023-01-31 12:34:56 +0000] [999996] [INFO] platform: SomeOS-9.8.7 +[2023-01-31 12:34:56 +0000] [999996] [INFO] packages: sanic-routing==22.8.0 +[2023-01-31 12:34:56 +0000] [999997] [INFO] Starting worker [999997] +``` +```` + +.. tab:: TLS サーバ + +```` +Running Sanic with TLS enabled is as simple as passing it the file paths... +```sh +sanic path.to.server:app --cert=/path/to/bundle.crt --key=/path/to/privkey.pem +``` + +... or the a directory containing `fullchain.pem` and `privkey.pem` + +```sh +sanic path.to.server:app --tls=/path/to/certs +``` + +**Even better**, while you are developing, let Sanic handle setting up local TLS certificates so you can access your site over TLS at [https://localhost:8443](https://localhost:8443) + +```sh +sanic path.to.server:app --dev --auto-tls +``` +```` + +.. tab:: Websockets + +```` +Up and running with websockets in no time using the [websockets](https://websockets.readthedocs.io) package. +```python +from sanic import Request, Websocket + +@app.websocket("/feed") +async def feed(request: Request, ws: Websocket): + async for msg in ws: + await ws.send(msg) +``` +```` + +.. tab:: 静的ファイル + +```` +Serving static files is of course intuitive and easy. Just name an endpoint and either a file or directory that should be served. + +```python +app.static("/", "/path/to/index.html") +app.static("/uploads/", "/path/to/uploads/") +``` + +Moreover, serving a directory has two additional features: automatically serving an index, and automatically serving a file browser. + +Sanic can automatically serve `index.html` (or any other named file) as an index page in a directory or its subdirectories. + +```python +app.static( + "/uploads/", + "/path/to/uploads/", + index="index.html" +) +``` + +And/or, setup Sanic to display a file browser. + + +![image](/assets/images/directory-view.png) + +```python +app.static( + "/uploads/", + "/path/to/uploads/", + directory_view=True +) +``` +```` + +.. tab:: Lifecycle + +```` +Beginning or ending a route with functionality is as simple as adding a decorator. + +```python +@app.on_request +async def add_key(request): + request.ctx.foo = "bar" + +@app.on_response +async def custom_banner(request, response): + response.headers["X-Foo"] = request.ctx.foo +``` + +Same with server events. + +```python +@app.before_server_start +async def setup_db(app): + app.ctx.db_pool = await db_setup() + +@app.after_server_stop +async def setup_db(app): + await app.ctx.db_pool.shutdown() +``` + +But, Sanic also allows you to tie into a bunch of built-in events (called signals), or create and dispatch your own. + +```python +@app.signal("http.lifecycle.complete") # built-in +async def my_signal_handler(conn_info): + print("Connection has been closed") + +@app.signal("something.happened.ohmy") # custom +async def my_signal_handler(): + print("something happened") + +await app.dispatch("something.happened.ohmy") +``` +```` + +.. tab:: スマートエラー処理 + +```` +Raising errors will intuitively result in proper HTTP errors: + +```python +raise sanic.exceptions.NotFound # Automatically responds with HTTP 404 +``` + +Or, make your own: + +```python +from sanic.exceptions import SanicException + +class TeapotError(SanicException): + status_code = 418 + message = "Sorry, I cannot brew coffee" + +raise TeapotError +``` + +And, when an error does happen, Sanic's beautiful DEV mode error page will help you drill down to the bug quickly. + +![image](../assets/images/error-div-by-zero.png) + +Regardless, Sanic comes with an algorithm that attempts to respond with HTML, JSON, or text-based errors as appropriate. Don't worry, it is super easy to setup and customize your error handling to your exact needs. +```` + +.. tab:: App Inspector + +```` +Check in on your live, running applications (whether local or remote). +```sh +sanic inspect + +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Sanic │ +│ Inspecting @ http://localhost:6457 │ +├───────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────┤ +│ │ mode: production, single worker │ +│ ▄███ █████ ██ │ server: unknown │ +│ ██ │ python: 3.10.9 │ +│ ▀███████ ███▄ │ platform: SomeOS-9.8.7 +│ ██ │ packages: sanic==22.12.0, sanic-routing==22.8.0, sanic-testing==22.12.0, sanic-ext==22.12.0 │ +│ ████ ████████▀ │ │ +│ │ │ +│ Build Fast. Run Fast. │ │ +└───────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────┘ + +Sanic-Main + pid: 999996 + +Sanic-Server-0-0 + server: True + state: ACKED + pid: 999997 + start_at: 2023-01-31T12:34:56.00000+00:00 + starts: 1 + +Sanic-Inspector-0 + server: False + state: STARTED + pid: 999998 + start_at: 2023-01-31T12:34:56.00000+00:00 + starts: 1 +``` + +And, issue commands like `reload`, `shutdown`, `scale`... + +```sh +sanic inspect scale 4 +``` + +... or even create your own! + +```sh +sanic inspect migrations +``` +```` + +.. tab:: Extendable + +``` +In addition to the tools that Sanic comes with, the officially supported [Sanic Extensions](./plugins/sanic-ext/getting-started.md) provides lots of extra goodies to make development easier. + +- **CORS** protection +- Template rendering with **Jinja** +- **Dependency injection** into route handlers +- OpenAPI documentation with **Redoc** and/or **Swagger** +- Predefined, endpoint-specific response **serializers** +- Request query arguments and body input **validation** +- **Auto create** HEAD, OPTIONS, and TRACE endpoints +- Live **health monitor** +``` + +.. tab:: Developer Experience + +``` +Sanic is **built for building**. + +From the moment it is installed, Sanic includes helpful tools to help the developer get their job done. + +- **One server** - Develop locally in DEV mode on the same server that will run your PRODUCTION application +- **Auto reload** - Reload running applications every time you save a Python file, but also auto-reload **on any arbitrary directory** like HTML template directories +- **Debugging tools** - Super helpful (and beautiful) [error pages](/en/guide/best-practices/exceptions) that help you traverse the trace stack easily +- **Auto TLS** - Running a localhost website with `https` can be difficult, [Sanic makes it easy](/en/guide/how-to/tls) +- **Streamlined testing** - Built-in testing capabilities, making it easier for developers to create and run tests, ensuring the quality and reliability of their services +- **Modern Python** - Thoughtful use of type hints to help the developer IDE experience +``` diff --git a/guide/content/ja/organization/code-of-conduct.md b/guide/content/ja/organization/code-of-conduct.md new file mode 100644 index 0000000000..e07d63d85c --- /dev/null +++ b/guide/content/ja/organization/code-of-conduct.md @@ -0,0 +1,71 @@ +# 貢献者の契約行動コード + +## 私たちの誓い + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## 私たちの基準 + +正の環境を作り出すことに貢献する行動の例は以下のとおりです。 + +- 歓迎と包括的な言語の使用 +- 異なる視点や経験を尊重する +- 建設的な批判を優雅に受け入れます +- コミュニティにとって何が最善なのかに焦点を当てます +- 他のコミュニティメンバーへの共感を表示 + +参加者が受け入れられない行動の例としては、次のようなものがあります。 + +- 性的言語または画像の使用と歓迎されない性的注意または + の進歩。 +- トロール、侮辱的/軽蔑的なコメント、個人的または政治的な攻撃 +- 公的または私的な嫌がらせについて +- 物理的または電子的な + アドレスなど、他人の個人情報を明示的な許可なしに公開する +- プロの設定で合理的に不適切とみなされる他の行為 + +## 私たちの責任 + +プロジェクト管理者は、許容される +行動の基準を明確にする責任があり、受け入れられない行動のあらゆるインスタンスに対して、 +応答において適切かつ公正な是正措置をとることが期待されます。 + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## スコープ + +この行動規範は、個人がプロジェクトまたはコミュニティを代表している場合、プロジェクトスペースと公共スペース +の両方に適用されます。 Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +adam@sanicframework.org のプロジェクト チームに連絡することで、虐待、嫌がらせ、またはその他の受け入れられない行動のインスタンスが +報告される可能性があります。 +のすべての苦情は審査され調査され、 +が状況に対して必要かつ適切であると判断される対応となります。 The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +具体的な執行ポリシーの詳細については、別途掲載することがあります。 + +信仰に基づいて行動規範を遵守しない、または強制しないプロジェクトのメンテナーは、プロジェクトのリーダーシップの他の +メンバーによって決定されるように、一時的または恒久的な影響に直面する可能性があります。 + +## 表示 + +この行動規範は、[http://contributor-covenant.org/version/1/4][homepage] +で利用できる [Contributor Covenant][version] から適用されます。 + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/guide/content/ja/organization/contributing.md b/guide/content/ja/organization/contributing.md new file mode 100644 index 0000000000..2ec7dce872 --- /dev/null +++ b/guide/content/ja/organization/contributing.md @@ -0,0 +1,166 @@ +# コントリビューション + +ご興味をお持ちいただきありがとうございます! Sanicは常に貢献者を探しています。 貢献するコードが不快であれば、ソース・ファイルにdocstringを追加したり、[Sanic User Guide](https://github) を手伝ってください。 om/sanic-org/sanic-guide: ドキュメントまたは実装例を提供することにより、当然のことです! + +我々は,性別,性的指向,障害,民族性,宗教その他の個人的特性にかかわらず,すべての人に友好的で安全で歓迎的な環境を提供することにコミットしている。 format@@0(https://github.com/sanic-org/sanic/blob/master/CONDUCT.md) は行動の標準を設定します。 + +## インストール + +Sanicで開発する(主にテストを実行する)には、ソースからインストールすることを強くお勧めします。 + +そのため、既にリポジトリをクローンし、すでに設定されている仮想環境が作業ディレクトリにあると仮定してから、以下を実行します。 + +```sh +pip install -e " .[dev]" +``` + +## 依存関係の変更 + +`Sanic` は `requirements*.txt` ファイルを使用して、依存関係の管理に必要な作業を簡素化するために、依存関係に関連するあらゆる種類の依存関係を管理しません。 `setup.py`ファイル内で`sanic`が依存関係を管理する方法を説明する以下のセクションを読んで理解していることを確認してください。 + +| 依存関係の種類 | 使用法 | インストール | +| ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- | --------------------------- | +| 要件 | sanic が機能するために必要な最小の依存関係を事前に確認する | `pip3 install -e .` | +| tests_require / extras_require['test'] | `sanic`のユニットテストを実行するために必要な依存関係 | `pip3 install -e '.[test]'` | +| extras_require['dev'] | 貢献を追加するための追加開発要件 | `pip3 install -e '.[dev]'` | +| extras_require['docs'] | サニック文書の構築と強化に必要な依存関係 | `pip3 install -e '.[docs]'` | + +## すべてのテストを実行中 + +Sanicのテストを実行するには、以下のようにtoxを使用することをお勧めします。 + +```sh +tox +``` + +それは簡単です参照してください! + +`tox.ini` には異なる環境が含まれています。 Running `tox` without any arguments will +run all unittests, perform lint and other checks. + +## Unittestsを実行 + +`tox` 環境 -> `[testenv]` + +unittestsのみを実行するには、次のような環境で`tox`を実行します。 + +```sh + +tox -e py37 -v -- tests/test_config.py +# or +tox -e py310 -v -- tests/test_config.py +``` + +## lint のチェックを実行 + +`tox` 環境 -> `[testenv:lint]` + +`flake8`\ , `black` と `isort` のチェックを実行します。 + +```sh +tox -e lint +``` + +## 型アノテーションチェックを実行 + +`tox` 環境 -> `[testenv:type-checking]` + +`mypy`チェックを実行します。 + +```sh +tox -e タイプチェック +``` + +## 他のチェックを実行 + +`tox` 環境 -> `[testenv:check]` + +他のチェックを実行します。 + +```sh +tox -e check +``` + +## 静的解析を実行 + +`tox` 環境 -> `[testenv:security]` + +静的解析セキュリティスキャンを実行 + +```sh +tox -e セキュリティ +``` + +## ドキュメントの健全性チェックを実行 + +`tox` 環境 -> `[testenv:docs]` + +ドキュメント上で健全性チェックを行う + +```sh +tox -e ドキュメント +``` + +## コードスタイル + +Sanicはコードの一貫性を維持するために、以下のツールを使用します。 + +1. [isort](https://github.com/timothycrossley/isort) +2. [black](https://github.com/python/black) +3. [flake8](https://github.com/PyCQA/flake8) +4. [slotscheck](https://github.com/ariebovenberg/slotscheck) + +### isort + +`isort` は Python インポートをソートします。 これは、インポートをアルファベット順にソートされた3つのカテゴリに分割します。 + +1. ビルトイン +2. サードパーティーの +3. プロジェクト固有の + +### ブラック + +`black` はPythonのコードフォーマッタです。 + +### flake8 + +`flake8` は次のツールを一つにまとめるPythonスタイルガイドです。 + +1. PyFlakes +2. pycodestyle +3. Ned BatchelderのMcCabeスクリプト + +### slotscheck + +`slotscheck` は、`__slots__` に問題がないことを保証します (基本クラスの重複や欠落など)。 + +`isort` 、 `black` 、 `flake8` 、 `slotscheck` のチェックは `tox` lintチェック中に行われます。 + +**最も簡単**な方法は、コミットする前に以下を実行することです。 + +```bash +きれいにする +``` + +詳細については、format@@0(https://tox.readthedocs.io/en/latest/index.html)を参照してください。 + +## プルリクエスト + +したがって、プルリクエストの承認ルールは非常に簡単です。 + +1. すべてのプルリクエストは単体テストに合格しなければなりません。 +2. すべてのプルリクエストは、コア開発チームの少なくとも1人の現在のメンバーによって審査および承認されなければなりません。 +3. すべてのプルリクエストは flake8 チェックを渡す必要があります。 +4. すべてのプルリクエストは `isort` と `black` 要件に一致する必要があります。 +5. 免除が与えられない限り、全てのプルリクエストは **PROPERLY** 型に注釈を付ける必要があります。 +6. すべてのプルリクエストは、既存のコードと一致している必要があります。 +7. 任意の一般的なインターフェイスから何かを削除/変更することにした場合、非推奨メッセージは format@@0(https://sanicframework.org/ja/guide/project/polices.html#deprecation )に従って同行する必要があります。 +8. 新しい機能を実装する場合は、少なくとも1つの単位テストが必要です。 +9. 例は次のいずれかでなければなりません: + - Sanicの使い方の例 + - Sanic extensions の使用例 + - Sanicライブラリと非同期ライブラリの使用例 + +## ドキュメント + +戻って確認(_C) 変更されるようにドキュメントを再作業しています。_ diff --git a/guide/content/ja/organization/policies.md b/guide/content/ja/organization/policies.md new file mode 100644 index 0000000000..035b235bd1 --- /dev/null +++ b/guide/content/ja/organization/policies.md @@ -0,0 +1,80 @@ +# ポリシー + +## Versioning + +Sanic は format@@0(https://calver.org/), "calver" を使用しています。 具体的には、パターンは次のとおりです。 + +``` +YY.MM.MICRO +``` + +一般的に、バージョンは `YY.MM` 形式で参照されます。 `MICRO` は、`0`から始まる増分パッチのバージョンを示します。 + +## 脆弱性の報告 + +セキュリティ上の脆弱性が発見された場合は、GitHub で問題を **作成しない** ようお願いします。 代わりに、コミュニティフォーラムで format@@0(https://community.sanicframework.org/g/core-devs) を参照してください。 ログインすると、メッセージボタンをクリックしてコア開発者にメッセージを送信できます。 + +あるいは、Adam Hopkins の Discordにプライベートメッセージを送信することもできます。 format@@0(https://discord.gg/FARQzAEMAA) で彼を見つけてください。 + +これは、チームが問題に対処して解決するまで問題を公表しないようにするのに役立ちます。 + +## リリーススケジュール + +4 つあります (4) 年間スケジュールリリース: 3月, 6月, 9月, 12月. そのため、`YY.3`、`YY.6`、`YY.9`、`YY.12`の4つのリリースバージョンがあります。 + +このリリーススケジュールは以下のとおりです。 + +- 予測可能なリリースケイデンスが +- 比較的短い開発ウィンドウにより、定期的に機能がリリースされます。 +- [deprecations](#deprecation) を制御しました +- 年間のLTSと安定しています + +[S.C.O.P.E.](./scope.md) でカバーされるガバナンスモデルと併せて、年次リリースサイクルも使用します。 + +### 長期サポート v 中間リリース + +Sanicは12月に年に一度長期サポートリリース(別名「LTS」)をリリースしています。 LTSリリースでは、**24ヶ月**のバグ修正とセキュリティアップデートを受け取ります。 年間を通じた中間リリースは3ヶ月ごとに行われ、その後のリリースまでサポートされています。 + +| バージョン | リリース | LTS | サポート | +| ------------------------------------- | ---------- | ------------ | ---- | +| 24.12 | 2024-12-31 | 2026年から12年まで | ✅ | +| 24.6 | 2024-06-30 | | ⚪ | +| 23.12 | 2023-12-31 | 2025年から12年まで | ☑️ | +| 23.6 | 2023-07-25 | | ⚪ | +| 23.3 | 2023-03-26 | | ⚪ | +| 22.12 | 2022-12-27 | | ☑️ | +| 22.9 | 2022-09-29 | | ⚪ | +| 22.6 | 2022-06-30 | | ⚪ | +| 22.3 | 2022-03-31 | | ⚪ | +| 21.12 | 2021-12-26 | | ⚪ | +| 21.9 | 2021-09-30 | | ⚪ | +| 21.6 | 2021-06-27 | | ⚪ | +| 21.3 | 2021-03-21 | | ⚪ | +| 20.12 | 2020-12-29 | | ⚪ | +| 20.9 | 2020-09-30 | | ⚪ | +| 20.6 | 2020-06-28 | | ⚪ | +| 20.3 | 2020-05-14 | | ⚪ | +| 19.12 | 2019-12-27 | | ⚪ | +| 19.9 | 2019-10-12 | | ⚪ | +| 19.6 | 2019-06-21 | | ⚪ | +| 19.3 | 2019-03-23 | | ⚪ | +| 18.12 | 2018-12-27 | | ⚪ | +| 0.8.3 | 2018-09-13 | | ⚪ | +| 0.7.0 | 2017-12-06 | | ⚪ | +| 0.6.0 | 2017-08-03 | | ⚪ | +| 0.5.4 | 2017-05-09 | | ⚪ | +| 0.4.1 | 2017-02-28 | | ⚪ | +| 0.3.1 | 2017-02-09 | | ⚪ | +| 0.2.0 | 2017-01-14 | | ⚪ | +| 0.1.9 | 2016-12-25 | | ⚪ | +| 0.1.0 | 2016-10-16 | | ⚪ | + +☑️ = セキュリティ修正\ +✅ = フルサポート\ +⚪ = サポートなし + +## 廃止予定 + +機能が廃止される前、またはAPIに互換性のない変更が導入されます。 それは公表され非推奨の警告とともに2つのリリースサイクルを通じて表示されるものとします。 非推奨はLTSリリースで行われることはありません。 + +絶対的に保証された場合、変更または機能の削除はこれらのガイドラインの外で行われることがあります。 これらの状況はまれであるべきである。 たとえば、主要なセキュリティ問題を削減する代替手段がない場合に発生する可能性があります。 diff --git a/guide/content/ja/organization/scope.md b/guide/content/ja/organization/scope.md new file mode 100644 index 0000000000..078f5d0358 --- /dev/null +++ b/guide/content/ja/organization/scope.md @@ -0,0 +1,257 @@ +# Sanic Community Organization ポリシー E-manual (SCOPE) + +.. attrs:: +:class: is size-7 + +``` +_2019年12月バージョン1_ +``` + +## 目標 + +Sanicプロジェクトで促進される持続可能でコミュニティ主導の組織を作るために、(1)安定性と予測可能性、(2)反復性とエンハンスメントサイクルを促進する。 (3)外部からの関与、(4)全体的な信頼性の高いソフトウェア、(5)コミュニティメンバーにとって安全でやりがいのある環境。 + +## 概要 + +本ポリシーは、Sanic Community Organization (以下「SCO」)のガバナンスモデルです。 SCOは、それによって採択されたすべてのプロジェクトに対して責任を負う、合意に基づくメリットがあるコミュニティ組織です。 プロジェクトのいずれかに興味がある人は誰でもコミュニティに参加できます。 コミュニティやプロジェクトに貢献し、意思決定プロセスに参加する。 このドキュメントでは、その参加がどのように行われ、プロジェクトコミュニティ内でどのようにメリットを得るかについて説明します。 + +## 構成 + +SCOには複数の**プロジェクト**があります。 各プロジェクトは、Sanic community の傘下にある GitHub リポジトリによって表されます。 これらのプロジェクトは**ユーザー**によって開発された**貢献者**によって**解放マネージャー**によってリリースされ、**ステアリング評議会**によって最終的に監督される**コア開発者**によって管理されています。 これが Python プロジェクトや PEP 8016 に似ている場合は、意図的にそのように設計されているためです。 + +## 役割と責任 + +### ユーザー + +ユーザーはプロジェクトを必要とするコミュニティメンバーです。 彼らは、パッケージをダウンロードしてインストールする開発者や人員です。 ユーザーはコミュニティの**最も重要な**メンバーであり、それがなければプロジェクトは何の目的もありません。 誰でも利用者であり、プロジェクトによって採用されたライセンスは、適切なオープンソースライセンスでなければなりません。 + +_SCOはできるだけ多くのプロジェクトとコミュニティに参加するようユーザーに要求します。_ + +ユーザー貢献により、プロジェクトチームはそれらのユーザーのニーズを確実に満たすことができます。 一般的なユーザーの貢献には以下のものが含まれます(これらに限定されません)。 + +- プロジェクトについての伝道(例:ウェブサイトのリンクや口コミ啓発など) +- 新しいユーザーの視点から強みと弱みを開発者に伝えることです +- 道徳的サポートを提供する(「ありがとう」は長い道のりを行く) +- 資金援助を提供する(ソフトウェアはオープンソースですが、開発者は食べる必要があります) + +SCO、そのプロジェクト、およびそのコミュニティに引き続き関与し続けるユーザーは、多くの場合、ますます関与するようになります。 次のセクションで説明されているように、そのようなユーザーは自分自身が貢献者になる可能性があります。 + +### 貢献者 + +貢献者は、プロジェクトの1つ以上の方法で具体的に貢献するコミュニティメンバーです。 誰でも貢献者になることができ、貢献は多くの形式をとることができます。 貢献と要件は、それぞれのプロジェクトで個別に貢献ポリシーによって管理されます。 + +**プロジェクトへのコミットメントの期待はありません**特定のスキル要件はありません**と**選択プロセスはありません\*\*。 + +ユーザーとしての行動に加えて、貢献者は自分自身が次のいずれかまたは複数を実行していることがあります: + +- 新規ユーザーのサポート (既存のユーザーは新規ユーザーをサポートするのに最適なユーザーです) +- バグの報告 +- 要件を特定する +- グラフィックやウェブデザインを提供し +- プログラミング +- ユースケースの例 +- プロジェクトのインフラ整備の支援 +- ドキュメントの書き込み +- バグの修正 +- 機能の追加 +- 建設的な意見を述べコミュニティの議論に従事し + +貢献者はGitHubとコミュニティフォーラムを通じてプロジェクトに取り組んでいます。 彼らはプルリクエストを介してプロジェクト自体に変更を提出します。これは、一般的にコミュニティによってプロジェクトに含められると考えられます。 コミュニティフォーラムは、最初の貢献をする際に助けを求めるのに最適な場所です。 + +実際、貢献者の最も重要な役割の一つは、**単にコミュニティの会話に参加する**ことです。 プロジェクトの方向性に関するほとんどの決定は、コンセンサスによって行われます。 これについては、後述します。 しかし、一般的には、 貢献者が**自由に話す**(行動規範の範囲内で)プロジェクトの健康と方向性に役立ち、**意見や経験を表明**してコンセンサスの構築を促進します。 + +貢献者がプロジェクトの経験と慣れ親しみを得るにつれて、彼らの内側のプロファイルとコミュニティへのコミットメントが増加します。 ある段階では、コア開発チームにノミネートされることがあります。 + +### コア開発者 + +SCO傘下の各プロジェクトには、コア開発者のチームがあります。 彼らはそのプロジェクトを担当している人々です。 + +_コア開発者とは何ですか?_ + +コア開発者は、コミュニティとの継続的な関わりを通じてプロジェクトの継続的な開発に取り組んでいることを示したコミュニティメンバーです。 コア開発者であることは、プロジェクトのリソースに直接アクセスできるようにすることで、貢献者がプロジェクト関連のアクティビティをより簡単に継続できるようにします。 フォークからのプルリクエストを介して変更を送信することなく、プロジェクトリポジトリに直接変更を加えることができます。 + +これは、コア開発者が自分の望むことを自由に行うことを意味するものではありません。 実際、コア開発者はパッケージの最終リリースに対して、貢献者より直接的な権限を持っていません。 この名誉は、プロジェクトの目的と目的に対して健全な敬意を示しているコミュニティの重要なメンバーを示しています。 彼らの仕事は公表前にコミュニティによって見直され続けています + +_コア開発者はプロジェクトで何ができますか?_ + +各プロジェクトは、この役割を若干異なって定義することができます。 ただし、 一般的に使用されているのは、個人がコミュニティ内で信頼されるようになったということです 彼らは今何らかの制御を与えられるようになったのです これは、保護されていないブランチに対するプッシュ権限、およびプルリクエストの承認で声を持つ能力の形で来ます。 + +プロジェクトは様々なコミュニケーションメカニズムを採用し、すべての貢献がコミュニティ全体によって見直されるようにしています。 これには、コミュニティフォーラムだけでなく、GitHubが提供するツールも含まれます。 貢献者がコア開発者になるために招待されるまでに。 ユーザーとしてそして貢献者として 様々なツールやワークフローに精通しているべきです + +_コア開発者になるには?_ + +誰でもコア開発者になることができます。 特別な要件はありませんチームプレーヤーとして プロジェクトに積極的に参加する意欲と能力を示す以外は + +通常、潜在的なコア開発者は、プロジェクト、その目的、およびその戦略について理解していることを示す必要があります。 彼らはまた、期間の間にプロジェクトに貴重な貢献を提供します。 ただし、資格については、**技術的またはその他のスキル**の要件はありません。 + +新しいコア開発者はいつでも**既存のコア開発者**にノミネートされることができます。 少なくとも年に2回(4月と10月)は、運営協議会によって実施される投票プロセスがあります。 投票は秘密の投票によって行われるべきです。 そのプロジェクトの各コア開発者は、投票で候補者の数に相当する数の票を受け取ります。 例えば、4人のノミネート者がいる場合、各既存のコア開発者には4つの投票があります。 コア開発者は、しかし、それらの投票をキャストすることができますが、複数回のノミネートに投票することはできません。 候補者は、キャスト投票数(適格投票数ではない)から3分の2の承認を得なければなりません。 コア開発者によって受け入れられると、それは指名を承認し、最終的に決定するためのステアリング評議会の責任です。 The Steering Council は、ノミネート者がコア開発者のタイトルを受け取るのに十分な功績があるかどうかを判断する権利を持っていません。 しかしながら、コミュニティの健全性が必要とされる場合には、投票を無効にする権利は保持されます。 + +投票が行われると、集計された投票結果がコミュニティフォーラムに掲載されます。 ノミネート候補者は、それらに対するオーバーライドの説明を要求する権利があります。 コア開発者として認められていない候補者は、今後再度ノミネートされる可能性があります。 + +コア開発者であることは、権利ではなく特権であることを認識することが重要です。 その特権は獲得されなければならず、一度獲得されると極端な状況で運営評議会(次のセクションを参照)によって削除することができます。 しかし、通常の状況では、個々の人がプロジェクトやコミュニティと引き続き関わりたいと思っている限り、開発者のタイトルは存在します。 + +プロジェクトへの平均以上の貢献度を示すコミッター, 特にその戦略的な方向性と長期的な健康に関して. ステアリング評議会またはリリースマネージャのメンバーになるために指名することができます。 この役割については、以下で説明します。 + +_コア開発者の権利と責任は何ですか?_ + +議論されたように、すべき決定の大部分はコンセンサスの構築によって行われる。 問題がより論争的になった、または主要な決定がなされる必要がある特定の状況で。 Release Manager または Steering Council は、下記より詳細に概説されている RFC プロセスを実装するために決定(または要求される)することができます。 + +また、コミュニティのガバナンスにおいて発言権を持つことは、コア開発者にも課せられています。 すべてのプロジェクトのコア開発者は、運営評議会に推薦され、選挙に投票する能力を持っています。 + +このポリシー(以下「SCOPE」)は、アクティブなコア開発者の3分の2の権限の下でのみ変更することができます。 養子縁組終了後最初の6ヶ月で コア開発者は単なる大多数のアクティブ開発者の権限の下で 変更を加える権利を保有しています + +_コア開発者が非アクティブになったらどうしますか?_ + +すべてのコア開発者がプロジェクトに参加し、定期的に活動を続けることが期待されています。 しかし、そのようなコミットメントは時々現実的ではないかもしれない、あるいは可能ではないかもしれないことも理解されています。 + +そのため、 運営評議会は、参加を奨励する義務があり、コア開発者がもはや参加する意思がない、または参加できる能力がない場合は、非アクティブな状態に置く責任があります。 この主な目的は、**行動のために人を罰しない**ことです。 開発プロセスが続けられるようにするためです + +この目的のために、「非アクティブ」になるコア開発者は、リポジトリに対する権利をコミットせず、いかなる投票にも参加してはなりません。 選挙に投票する資格を得るためには、前の予定されたプロジェクトリリース時にコア開発者**がアクティブでなければなりません**。 + +活動していないメンバーは、運営評議会にいつでも地位を復活させるよう要請することができます。 そして、そのような要請に応じて、運営評議会は、コア開発者を再び活性化させるものとする。 + +一定期間アクティブな地位を維持することができないことを知っている個人は、運営評議会と連絡を取り、必要に応じて非アクティブであると宣言するよう求められます。 + +「アクティブ」なコア開発者は、過去6ヶ月間に有意義な方法で参加した個人です。 これ以上の定義は、運営協議会の裁量の範囲内にあります。 + +### リリースマネージャー + +コア開発者は、保護されていないブランチに対してコミットとマージを行うためにのみアクセスできるものとします。 「master」ブランチと他の保護されたブランチは、そのプロジェクトのリリース管理チームによって管理されます。 リリースマネージャーは、コア開発チームによってコア開発チームから選出され、完全なリリースサイクルのために役立つものとします。 + +各コア開発チームは、各リリースサイクルに必要なリリースマネージャーの数を決定できます。 少なくとも2人のリリースマネージャーがリリースサイクルで責任を分担し、一人の人にあまり努力を強いられないようにすることを強くお勧めします。 しかし、彼らの努力が妨げられるほど多くの管理者がいるべきではありません。 + +リリース管理チームの主な責任は次のとおりです。 + +- 技術的な議論を監視し促進することで開発サイクルを前進させ +- リリースカレンダーを確立し、パッケージをリリースするために必要なアクションを実行します +- マスターブランチや他の保護されたブランチへのプルリクエストを承認する +- マスターブランチと他の保護されたブランチにプルリクエストをマージする + +リリースマネージャーは、プルリクエストのマージを拒否または保留する権限がありません。これらは、コントリビューション基準を満たしており、コミュニティによって受け入れられています。 何を開発すべきかを決めるのは彼らの責任ではありません。 共同体の決定が下されプロジェクトが進められているのです + +時折、合意によっては達成できない決定が必要な場合があります。 その場合、リリース マネージャは、RFC プロセスへの決定の削除を要求する権限を持っています。 これは定期的に発生するべきではありません(以下で説明するように必要な場合を除きます)。そして、その使用は、より共同的なコンセンサスの構築戦略に賛成することをお勧めしません。 + +すべてのプロジェクトが同じ要件を持っているわけではないので、 プロジェクトのリリースマネージャーに関する詳細は、本ポリシーの付録またはプロジェクトのコントリビューションガイドラインに記載されています。 + +必要に応じて、ステアリング評議会には、職務を放棄する、または他の正当な理由のために、リリースマネージャーを削除する権利があります。 + +### 運営評議会format@@0 + +運営協議会は、「プロジェクト所有者」として特定され、SCOのリソースと資産を管理する個人からなる統治機関です。 彼らの最終的な目標は、障害を取り除き、必要に応じてメンバーを支援することにより、プロジェクトの円滑な運営を確保することです。 彼らはコミュニティの定期的な声になることが期待されています。 + +_運営評議会に何ができるか?_ + +運営評議会のメンバーは、**他のどのコア開発者よりも権限を持っていません**。 プロジェクトの決定、約束、合併などを行う追加の権利は一切ありません + +ただし、本体として、運営審議会には以下のような能力があります。 + +- すべての RFC を受け入れ、再配布、および拒否する +- コミュニティの行動規範を施行し +- リポジトリ、サーバー、フォーラム、統合サービスなどのコミュニティ・アセットを管理する (または、そのような権限を他の誰かに委任する) +- コア開発者を非アクティブな状態に配置します。このポリシーで与えられたその他の執行措置を適切に取ります。 極端な場合にはコア開発者を排除し +- コミュニティの傘下からプロジェクトを採用したり削除したりします + +運営評議会は、可能な限りその権限を、適切な場合には他の意欲的なコミュニティメンバーに委任することが強く奨励される。 + +運営評議会は、このポリシーを変更する権限を持っていません\*\*。 + +_運営協議会のメンバーは何人いますか?_ + +4 + +4票の委員会のように思われますが、多数決を破る方法がなく、潜在的に行き詰まり状態に終わる可能性があります。 運営評議会は、できるだけ多くの投票から落胆しています。 代わりにコンセンサスによって動作しようとする必要があります, それは問題に投票する必要がある場合は、3つの同意票を必要とします. + +_運営協議会のメンバーはどれくらいですか?_ + +単期は1月から2暦年間とする。 利用規約は、毎年、前年の評議会から継続している2人のメンバーがあるようにずれるものとします。 + +そのため、就任票は2年の任期に2つの役職と1年の任期に2つの役職を設けなければならない。 + +提供できる用語の数に制限はなく、個人が連続した用語を提供することができます。 + +_ステアリング評議会を運営するのは誰ですか?_ + +運営評議会が選出された後、グループは、議長として行動する一人を共同で決定しなければならない。 議長は、運営協議会の他のメンバーよりも任意の追加の権利や権限を持っていません。 + +議長の役割は、単にコーディネーターであり、ファシリテーターとしての役割です。 議長は、すべてのガバナンスプロセスが遵守されることを確実にすることが期待されます。 位置は、より行政的で明白であり、議題を設定し、グループの議論を調整することが期待されています。 + +_諮問委員はどうやって選ばれますか?_ + +年に一度、プロジェクトごとに**すべての適格なコア開発者**は、Steering Councilのメンバーを選出する権利を有します。 + +推薦は9月1日から開始し、9月30日に終了するものとします。 その後、10月1日に投票を開始し、10月31日に終了します。 その年のサニック・フレームワークの6月リリースの日にアクティブなすべてのコア開発者は、ステアリング・カウンシルの空席あたり1票を受け取る資格があります。 明確にするために、投票の資格を得るために、コア開発者はSanic Frameworkのコア開発者である必要はありません\*\*。 それぞれのプロジェクトの中で活動しているのです + +投票の上位者は、勝者として宣言されるものとします。 提携がある場合は、結ばれた候補者自身が決定がランダムにされる前に紛争を解決することが強く奨励されます。 + +運営評議会の議決については、上位2名が2年間務める。 次の2人の有権者が1年議席に就くことになります + +運営評議会の適格な候補者になるには 個人は過去12ヶ月間少なくとも1つのプロジェクトの現役の 中心的な開発者である必要があります + +_空きがある場合はどうしますか_ + +運営審議会に欠員がある場合。 次の最高の投票受領者は任期の残りを完了するために提出されなければならない この方法で見つけることができない場合 運営評議会は、議席を埋めるための最も適切な行動の方法を決定することができます(任命、投票、または他の意味によってかどうか)。 + +運営協議会のメンバーが非アクティブになった場合 その後、その個人は直ちに運営委員会から解任され、議席は空席となります。 + +極端な場合 すべてのコア開発者の本体は、適格な投票コア開発者の3分の2が運営評議会のメンバーを削除する投票権を持っています。 + +_運営評議会はどのように事業を行うか?_ + +可能な限り、運営評議会は、事業と議論を公然と行う。 コミュニティのメンバーは、彼らとの会話に参加することを許可する必要があります。 ただし、個人的に議論を行う場合には、必要または適切な場合があります。 会話のための適切な会場を選択することは議長の行政業務の一部です。 + +操作方法の詳細はポリシーの範囲を超えています。 運営評議会は、「リアルタイム」の議論で四半期に少なくとも1回は会うことを奨励されます。 これは、ビデオ会議、ライブチャット、または他の適切な手段によって達成することができます。 + +## サポート + +コミュニティのすべての参加者は、プロジェクト管理インフラストラクチャ内のユーザーにサポートを提供することをお勧めします。 このサポートは、コミュニティを成長させる方法として提供されています。 支援を求める人は、プロジェクト内のすべての支援活動が自発的であることを認識し、したがって、時間が許す限り提供されます。 したがって、保証された回答時間または結果を必要とするユーザーは、コミュニティメンバーからサポート契約を購入するよう求める必要があります。 しかし、それ自身の条件でプロジェクトに取り組むことをいとわない人のために。 他のユーザーを支援してくれるのは理想的です + +## 意思決定プロセス + +プロジェクトの将来についての決定は、コミュニティのすべてのメンバーとの議論を通じて行われます。 最新のユーザーから最も経験豊富なメンバーまで 誰もが声を持っている。 + +機密性のないプロジェクト管理に関する議論は、コミュニティフォーラムやその他の指定されたチャンネルで行われます。 時折、機密性の高い議論がプライベートに行われることがあります。 + +プロジェクトが終わりのない議論と継続的な投票によって妨げられないようにするために、プロジェクトは**怠惰な合意**のポリシーを運営しています。 これにより、正式な投票に頼らずに大多数の決定を行うことができます。 **大きな判断** (以下に定義します) については、別途コメントリクエスト(RFC) プロセスがあります。 + +### 技術的な決定 + +プルリクエストと技術的な決定は、通常、以下のカテゴリーに分類されます。 + +- **ルーチン**: ドキュメント修正、クリーンアップや追加テストのためのコード変更。 機能の変更はありません。 +- **Minor**: バグを修正したり、些細な機能を導入したりするコードベースへの変更。 改行の変更はありません。 +- **Major**: 既存のAPIを壊したり廃止したりするコードベースに変更を加えたり、些細な方法で操作を変更したり、重要な機能を追加したりします。 + +リポジトリへの変更がマージの前に適切な承認を受け取ることを確認するのは、通常、リリースマネージャの責任です。 + +リリースマネージャーは、追加の入力なしにコード品質の基準を満たすルーチンの決定を個別にレビューし、受け入れる権限を保持します。 + +### 怠惰な + +意思決定(コミュニティまたは運営評議会によって)には通常、次のステップが含まれます。 + +- 提案 +- ディスカッション +- 投票(議論を通じて合意に達していない場合) +- 決定 + +コミュニティメンバーは誰でも、コミュニティによる検討のための提案をすることができます。 新しいアイデアについてのディスカッションを開始するには、コミュニティフォーラムに適切なチャネルにメッセージを投稿する必要があります。 または、GitHub でアイデアを実装するプルリクエストを送信します。 これは、レビューを促し、必要に応じて、アイデアの議論を促します。 + +このレビューと議論の目的は、貢献の承認を得ることです。 プロジェクトコミュニティのほとんどの人々がビジョンを共有しているので、コンセンサスを得るためには議論の必要性がほとんどないことが多い。 + +一般に、提案やパッチに明示的に反対する人がいない限り、それはコミュニティの支援を受けていると認識されています。 これは怠惰な合意と呼ばれます; すなわち, 彼らの意見を述べていない人々は、暗黙のうちに提案の実施に合意しました. + +怠惰なコンセンサスはSCOの中で非常に重要な概念です。 大勢の人々が効率的にコンセンサスに達することを可能にするのはこのプロセスです。 提案に異議なしに自分の立場を述べるのに時間を費やす必要はない 他の人はそのようなメッセージを読む時間を費やす必要はない + +怠惰なコンセンサスのために有効であること。 提案に異議はないと仮定する前に適切な時間が必要です これは状況によりますが、一般的には72時間が合理的であると想定されています。 この要件により、誰もが提案を読み、ダイジェスト、および回答するのに十分な時間を与えられることが保証されます。 この期間は、その場所と時間の約束に関係なく、すべての参加者の可能な限り包括的であるように選択されます。 議論のファシリテーター(議長であろうとリリースマネージャーであろうと)。 (該当する場合) このようなコンセンサスに到達するまでの適切な時間の長さを決定することが義務付けられます。 + +いわゆるルーチン決定に関する上記のように、リリース管理者は短期間で決定を行う権利を有します。 このような場合には、怠惰なコンセンサスが暗示されなければならない。 + +### コメントのリクエスト (RFC) + +運営評議会は、RFC プロセスの監督を担当します。 それはコミュニティのすべてのメンバーに議論を開いているプロセスである。 提案を検討し、メンバーが有意義な議論を行うための十分な時間を確保しなければならない。 + +最終的な決定は、ステアリング評議会によって与えられます。 しかし、運営審議会が、コミュニティ内に存在する可能性のあるコンセンサスに反する決定を採用することは強く落胆しています。 時折、コンセンサスとプロジェクト全体とコミュニティの目標の間に矛盾がある場合、これは起こるかもしれません。 + +RFC は、運営評議会によって規定されているような公的な方法で運営評議会への提出によって開始されるものとします。 議論は、継続し、一般的に運営委員会によって促進されるものとします, そして、具体的に議長. + +運営評議会が適切だと感じる場合には、RFC プロセスは怠惰な合意に賛成して放棄される可能性があります。 diff --git a/guide/content/ja/plugins/sanic-ext/configuration.md b/guide/content/ja/plugins/sanic-ext/configuration.md new file mode 100644 index 0000000000..f0db931894 --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/configuration.md @@ -0,0 +1,288 @@ +--- +title: Sanic Extensions - 設定 +--- + +# 設定 + +Sanic Extensionsは、format@@0(../../guide/deployment/configuration.md)と同じ方法で設定できます。 これにより、Sanic Extensionsを非常に簡単に構成できます。 + +```python +app = Sanic("MyApp") +app.config.OAS_URL_PREFIX = "/apidocs" +``` + +しかし、検討すべき設定オプションはいくつかあります。 + +## 手動の`extend` + +.. 列:: + +``` +Sanic Extensionsはアプリケーションに自動的にアタッチされますが、手動で`extend`を選択することができます。 これを行うと、すべての設定値をキーワード引数(小文字)として渡すことができます。 +``` + +.. 列:: + +```` +```python +app = Sanic("MyApp") +app.extend(oas_url_prefix="/apidocs") +``` +```` + +.. 列:: + +``` +あるいは、一度に 1 つの `dict` として渡すこともできます。 +``` + +.. 列:: + +```` +```python +app = Sanic("MyApp") +app.extend(config={"oas_url_prefix": "/apidocs"}) +``` +```` + +.. 列:: + +``` +これらのソリューションはどちらも、IDEによって構成設定の名前が検出できないという事実に苦しんでいます。 したがって、使用できる型注釈付きオブジェクトもあります。これは開発に役立つはずです。 +``` + +.. 列:: + +```` +```python +from sanic_ext import Config + +app = Sanic("MyApp") +app.extend(config=Config(oas_url_prefix="/apidocs")) +``` +```` + +## 設定 + +.. note:: + +```` +多くの場合、アプリケーションのためにこれらを変更する最も簡単な方法 (環境に依存して変更しない可能性が高いので) `appに直接設定することです。 onfig` オブジェクト + +ここで示すように、設定キーの大文字のバージョンを使用します。 + +```python +app = Sanic("MyApp") +app.config.OAS_URL_PREFIX = "/apidocs" +``` +```` + +### `cors` + +- **タイプ**: `bool` +- **デフォルト**: `True` +- **説明**: CORS 保護を有効にするかどうか + +### `cors_allow_headers` + +- **タイプ**: `str` +- **デフォルト**: `"*"` +- **説明**: ヘッダの値: `access-control-allow-headers` + +### `cors_always_send` + +- **タイプ**: `bool` +- **デフォルト**: `True` +- **説明**: ヘッダを常に送信するかどうか: `access-control-allow-origin` + +### `cors_automatic_options` + +- **タイプ**: `bool` +- **デフォルト**: `True` +- **Description**: _ない_ルートの「OPTIONS」エンドポイントを自動的に生成するかどうか + +### `cors_expose_headers` + +- **タイプ**: `str` +- **デフォルト**: `""` +- **説明**: ヘッダの値: `access-control-expose-headers` + +### `cors_max_age` + +- **タイプ**: `int` +- **デフォルト**: `5` +- **説明**: ヘッダの値: `access-control-max-age` + +### `cors_methods` + +- **タイプ**: `str` +- **デフォルト**: `""` +- **説明**: ヘッダの値: `access-control-access-control-allow-methods` + +### `cors_origins` + +- **タイプ**: `str` +- **デフォルト**: `""` +- **説明**: ヘッダの値: `access-control-allow-origin` + +.. 警告:: + +``` +`*`をここに置くときは注意してください。 セキュリティの問題である可能性があるため、何をしているかを知っていない限り、これをしないでください。 +``` + +### `cors_send_wildcard` + +- **タイプ**: `bool` +- **デフォルト**: `False` +- **Description**: リクエストオリジンの代わりにワイルドカードを送信するかどうか + +### `cors_supports_credentials` + +- **タイプ**: `bool` +- **デフォルト**: `False` +- **説明**: ヘッダの値: `access-control-allow-credentials` + +### `cors_vary_header` + +- **タイプ**: `bool` +- **デフォルト**: `True` +- **Description**: `vary`ヘッダーを追加するかどうか + +### `http_all_methods` + +- **タイプ**: `bool` +- **デフォルト**: `True` +- **Description**: 許可されている HTTP の `CONNECT` と `TRACE` メソッドを追加します + +### `http_auto_head` + +- **タイプ**: `bool` +- **デフォルト**: `True` +- **Description**: 自動的に `HEAD` ハンドラを `GET` ルートに追加します。 + +### `http_auto_options` + +- **タイプ**: `bool` +- **デフォルト**: `True` +- **説明**: 自動的に `OPTIONS` ハンドラをルートなしで追加します + +### `http_auto_trace` + +- **タイプ**: `bool` +- **デフォルト**: `False` +- **説明**: 自動的に `TRACE` ハンドラをルートなしで追加します + +### `oas` + +- **タイプ**: `bool` +- **デフォルト**: `True` +- **説明**: OpenAPI仕様の生成を有効にするかどうか + +### `oas_autodoc` + +- **タイプ**: `bool` +- **デフォルト**: `True` +- **説明**: ルート関数のdocstringからOpenAPIの詳細を自動的に抽出するかどうか + +### `oas_ignore_head` + +- **タイプ**: `bool` +- **デフォルト**: `True` +- **Description**: When `True`, `HEAD`エンドポイントをOpenAPI仕様に追加しません + +### `oas_ignore_options` + +- **タイプ**: `bool` +- **デフォルト**: `True` +- **説明**: When `True` はOpenAPI仕様に`OPTIONS`エンドポイントを追加しません + +### `oas_path_to_redoc_html` + +- **タイプ**: `Optional[str]` +- **デフォルト**: `None` +- **Description**: 既存のやり直しHTMLをオーバーライドするHTMLファイルへのパス + +### `oas_path_to_swagger_html` + +- **タイプ**: `Optional[str]` +- **デフォルト**: `None` +- **説明**: 既存の Swagger HTML をオーバーライドする HTML ファイルへのパス + +### `oas_ui_default` + +- **タイプ**: `Optional[str]` +- **デフォルト**: `"redoc"` +- **説明**: どのOASドキュメントを `oas_url_prefix` エンドポイントで提供します。`None` の場所にドキュメントがありません。 + +### `oas_ui_redoc` + +- **タイプ**: `bool` +- **デフォルト**: `True` +- **説明**: やり直しのUIを有効にするかどうか + +### `oas_ui_swagger` + +- **タイプ**: `bool` +- **デフォルト**: `True` +- **説明**: Swagger UI を有効にするかどうか + +### `oas_ui_swagger_version` + +- **タイプ**: `str` +- **デフォルト**: `"4.1.0"` +- **説明**: どのSwaggerバージョンを使用するか + +### `oas_uri_to_config` + +- **タイプ**: `str` +- **Default**: `"/swagger-config"` +- **説明**: Swagger 設定を提供するパス + +### `oas_uri_to_json` + +- **タイプ**: `str` +- **Default**: `"/openapi.json"` +- **説明**: OpenAPI JSON を提供するためのパス + +### `oas_uri_to_redoc` + +- **タイプ**: `str` +- **デフォルト**: `"/redoc"` +- **説明**: やり直しへのパス + +### `oas_uri_to_swagger` + +- **タイプ**: `str` +- **Default**: `"/swagger"` +- **説明**: Swagger へのパス + +### `oas_url_prefix` + +- **タイプ**: `str` +- **Default**: `"/docs"` +- **説明**: OASドキュメントのすべてのwitllが添付するブループリントのURLプレフィックス。 + +### `swagger_ui_configuration` + +- **タイプ**: `Dict[str, Any]` +- **デフォルト**: `{"apisSorter": "alpha", "operationsSorter": "alpha", "docExpansion": "full"}` +- **説明**: フロントエンドに提供される Swagger ドキュメント + +### `templating_enable_async` + +- **タイプ**: `bool` +- **デフォルト**: `True` +- **Description**: 神社`Environment` に `enable_async` を設定するかどうか + +### `templating_path_to_templates` + +- **タイプ**: `Union[str, os.PathLike, Sequence[Union[str, os.PathLike]]] ` +- **デフォルト**: `templates` +- **説明**: テンプレートファイルがどこにあるか、単一のパス、または複数のパスです。 + +### `trace_excluded_headers` + +- **タイプ**: `Sequence[str]` +- **デフォルト**: `("authorization", "cookie")` +- **Description**: `TRACE`リクエストへのレスポンスから抑制されるヘッダーを指定します。 diff --git a/guide/content/ja/plugins/sanic-ext/convenience.md b/guide/content/ja/plugins/sanic-ext/convenience.md new file mode 100644 index 0000000000..8cb677a4b0 --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/convenience.md @@ -0,0 +1,134 @@ +--- +title: サニックエクステンション - Convenience +--- + +# 便利さ + +## 固定シリアライザー + +.. 列:: + +``` +多くの場合、アプリケーションを開発する場合、常に同じ種類のレスポンスを返す特定のルートがあります。 この場合、返品シリアライザとエンドポイントを事前に定義できます。 返されるべきものは内容だけです +``` + +.. 列:: + +```` +```python +from sanic_ext import serializer + +@app.get("/") +@serializer(text) +async def hello_world(request, name: str): + if name.isnumeric(): + return "hello " * int(name) + return f"Hello, {name}" +``` +```` + +.. 列:: + +``` +`serializer`デコレータはステータスコードを追加することもできます。 +``` + +.. 列:: + +```` +```python +from sanic_ext import serializer + +@app.post("/") +@serializer(text, status=202) +async def create_something(request): + ... +``` +```` + +## カスタムシリアライザー + +.. 列:: + +``` +`@serializer`デコレータを使用して、有効な型(`HTTPResonse`)を返す限り、独自のカスタム関数を渡すこともできます。 +``` + +.. 列:: + +```` +```python +def message(retval, request, action, status): + return json( + { + "request_id": str(request.id), + "action": action, + "message": retval, + }, + status=status, + ) + +@app.post("/") +@serializer(message) +async def do_action(request, action: str): + return "This is a message" +``` +```` + +.. 列:: + +``` +今度は、文字列だけを返すと、素敵なシリアル化された出力が返されます。 +``` + +.. 列:: + +```` +```python +$ curl localhost:8000/eat_cookies -X POST +{ + "request_id": "ef81c45b-235c-46d-9dbd-b550f8fa77f9", + "action": "eat_cookies", + "message": "This is a message" +} + +``` +```` + +## カウンターを要求する + +.. 列:: + +``` +Sanic Extensionsには`Request`のサブクラスが付属しており、ワーカープロセスごとに処理されたリクエストの数を自動的に追跡できるように設定できます。 これを有効にするには、アプリケーションコントラクターに `CounttedRequest` クラスを渡す必要があります。 +``` + +.. 列:: + +```` +```python +from sanic_ext import CountedRequest + +app = Sanic(..., request_class=CounttedRequest) +``` +```` + +.. 列:: + +``` +ワーカープロセスの生涯中に提供されるリクエスト数にアクセスできるようになります。 +``` + +.. 列:: + +```` +```python +@app.get("/") +async def handler(request: CountedRequest): + return json({"count": request.count}) +``` +```` + +可能であれば、リクエスト数は [worker state](../../guide/deployment/manager.md#worker-state) にも追加されます。 + +![](https://user-images.githubusercontent.com/166269/190922460-43bd2cfc-f81a-443b-b84f-07b6ce475cbf.png) diff --git a/guide/content/ja/plugins/sanic-ext/custom.md b/guide/content/ja/plugins/sanic-ext/custom.md new file mode 100644 index 0000000000..50e6984433 --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/custom.md @@ -0,0 +1,92 @@ +--- +title: サニックエクステンション - Custom +--- + +# カスタムの拡張機能 + +独自の拡張機能を作成することは可能です。 + +バージョン 22.9 は `Extend.register` [method](#extension-preregistration) を追加しました。 これにより、アプリケーションにカスタムの拡張機能を追加することが非常に簡単になります。 + +## 拡張機能の解剖学 + +すべてのエクステンションは `Extension` のサブクラスでなければなりません。 + +### 必須 + +- `name`: 規約により、名前は全て小文字の文字列です +- `startup`: 拡張機能が追加されたときに実行されるメソッド + +### 省略可能 + +- `label`: MOTD の拡張機能に関する追加情報を返すメソッド +- `included`: 拡張機能を有効にするかどうかを真偽値を返すメソッド (設定状態を確認するために例えば使用できます) + +### 例 + +```python +from sanic import Request, Sanic, json +from sanic_ext import Extend, Extension + +app = Sanic(__name__) +app.config.MONITOR = True + +class AutoMonitor(Extension): + name = "automonitor" + + def startup(self, bootstrap) -> None: + if self.included(): + self.app.before_server_start(self.ensure_monitor_set) + self.app.on_request(self.monitor) + + @staticmethod + async def monitor(request: Request): + if request.route and request.route.ctx.monitor: + print("....") + + @staticmethod + async def ensure_monitor_set(app: Sanic): + for route in app.router.routes: + if not hasattr(route.ctx, "monitor"): + route.ctx.monitor = False + + def label(self): + has_monitor = [ + route + for route in self.app.router.routes + if getattr(route.ctx, "monitor", None) + ] + return f"{len(has_monitor)} endpoint(s)" + + def included(self): + return self.app.config.MONITOR + +Extend.register(AutoMonitor) + +@app.get("/", ctx_monitor=True) +async def handler(request: Request): + return json({"foo": "bar"}) +``` + +## エクステンションの事前登録 + +.. 列:: + +``` +`Extend.register` はカスタムエクステンションの追加を簡単にします。 +``` + +.. 列:: + +```` +```python +from sanic_ext import Extend, Extension + +class MyCustomExtension(Extension): + ... + +Extend.register(MyCustomExtension()) +``` +```` + +_v22.9_に追加されました diff --git a/guide/content/ja/plugins/sanic-ext/getting-started.md b/guide/content/ja/plugins/sanic-ext/getting-started.md new file mode 100644 index 0000000000..2c7c6e1e82 --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/getting-started.md @@ -0,0 +1,92 @@ +--- +title: サニックエクステンション - はじめに +--- + +# はじめに + +Sanic Extensionsは、SCOによって開発され、維持されている_公式にサポートされている_プラグインです。 このプロジェクトの主な目的は、Web API および Web アプリケーション開発を容易にする追加機能を追加することです。 + +## 特徴 + +- CORS保護 +- 神社でテンプレートをレンダリングする +- ルートハンドラへの依存性インジェクション +- やり直しや Swagger を使用した OpenAPI ドキュメント +- 事前定義されたエンドポイント固有のレスポンスシリアライザー +- クエリクエストの引数と本文入力のバリデーションをリクエスト +- `HEAD`、`OPTIONS`、および `TRACE`エンドポイントを自動的に作成します + +## 最低要件 + +- **Python**: 3.8+ +- **Sanic**: 21.9+ + +## インストール + +最良の方法は、Sanic自体と一緒にSanic Extensionsをインストールすることです: + +```bash +pip install sanic[ext] +``` + +もちろん、単独でインストールすることもできます。 + +```bash +pip install sanic-ext +``` + +## アプリケーションを拡張 + +Sanic Extensionsは、すぐにたくさんの機能を利用できるようになります。 + +.. 列:: + +``` +Sanic Extensions(v21.12+)をセットアップするには、次の手順を実行する必要があります。**何もありません**。環境内にインストールされている場合は、セットアップして準備が整います。 + +このコードは、[Sanic Getting Started page](../../guide/getting-started)のHello, world appです。 d) _何も変更せずに `sanic-ext` がバックグラウンドにインストールされた Sanic 拡張機能を使用します。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic +from sanic.response import text + +app = Sanic("MyHelloWorldApp") + +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` +```` + +.. 列:: + +``` +**_OLD DEPRECATED SETUP_** + +In v21.9, the easiest way to get started is to instantiate it with `Extend`. + +If you look back at the Hello, world app in the [Sanic Getting Started page](../../guide/getting-started.md), you will see the only additions here are the two highlighted lines. +``` + +.. 列:: + +```` +```python +from sanic import Sanic +from sanic.response import text +from sanic_ext import Extend + +app = Sanic("MyHelloWorldApp") +Extend(app) + +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` +```` + +どのように設定されているかに関わらず、OpenAPI ドキュメントを表示し、動作中のいくつかの機能を確認できるようになりました: [http://localhost:8000/docs](http://localhost:8000/docs)。 diff --git a/guide/content/ja/plugins/sanic-ext/health-monitor.md b/guide/content/ja/plugins/sanic-ext/health-monitor.md new file mode 100644 index 0000000000..1f9a0f63f9 --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/health-monitor.md @@ -0,0 +1,81 @@ +--- +title: サニックエクステンション - ヘルスモニター +--- + +# ヘルスモニター + +ヘルスモニターには、`sanic>=22.9` と `sanic-ext>=22.9` の両方が必要です。 + +Sanic Extensionsを設定して、作業工程の健全性を監視できます。 format@@0(../../guide/deployment/manager.md#single-process-mode) ではないことが必要です。 + +## セットアップ + +.. 列:: + +``` +ヘルスモニターは無効になっています。使用したい場合はオプトインしてエンドポイントを有効にする必要があります。 +``` + +.. 列:: + +```` +```python +app.config.HEALTH = True +app.config.HEALTH_ENDPOINT = True +``` +```` + +## どのように機能します + +モニターは新しいバックグラウンドプロセスを設定し、それぞれのワーカープロセスから定期的に活気を認識します。 ワーカープロセスがレポートを何度も見逃した場合、モニターはそのワーカーを再起動します。 + +## 診断エンドポイント + +.. 列:: + +``` +ヘルスモニターは、[worker state](../../guide/deployment/manager)を出力する診断エンドポイントも有効にします。 d#worker-state). デフォルトではIDが無効になっています。 + +.. danger:: + + 診断エンドポイントはセキュリティ保護されていません。 本番環境でデプロイする場合は、プロキシサーバーを使用して保護する手順を実行する必要があります。 そうでない場合は、サーバーの状態に関する詳細が漏れるため、本番環境でこの機能を無効にすることを検討することをお勧めします。 +``` + +.. 列:: + +```` +``` +$ curl http://localhost:8000/__health__ +{ + 'Sanic-Main': {'pid': 99997}, + 'Sanic-Server-0-0': { + 'server': True, + 'state': 'ACKED', + 'pid': 9999, + 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), + 'starts': 2, + 'restart_at': datetime.datetime(2022, 10, 1, 0, 0, 12, 861332, tzinfo=datetime.timezone.utc) + }, + 'Sanic-Reloader-0': { + 'server': False, + 'state': 'STARTED', + 'pid': 99998, + 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), + 'starts': 1 + } +} +``` +```` + +## 設定 + +| キー | タイプ | デフォルト | 説明 | +| ------------------------------------ | ------ | --------------- | --------------------------- | +| 健康 | `bool` | `False` | この拡張機能を有効にするかどうか。 | +| HEALTH_ENDPOINT | `bool` | `False` | 診断のエンドポイントを有効にするかどうか。 | +| 最大値が足りません | `int` | `3` | ワーカープロセスが再起動される前に連続したミスの数。 | +| 健康状態がありません。 | `int` | `10` | モニターがワーカープロセスの健全性をチェックする秒数。 | +| ヘルスモニター | `bool` | `True` | かどうかのヘルスモニターを有効にします。 | +| 内部レポート | `int` | `5` | 活気のあるそれぞれの確認を報告するまでの秒数。 | +| CHALLENGE_LABEL | `str` | `""` | 診断エンドポイントの URI パス。 | +| PREFIX | `str` | `"/__health__"` | 診断のブループリントの URI プレフィックス。 | diff --git a/guide/content/ja/plugins/sanic-ext/http/cors.md b/guide/content/ja/plugins/sanic-ext/http/cors.md new file mode 100644 index 0000000000..50f9f8a81f --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/http/cors.md @@ -0,0 +1,97 @@ +--- +title: Sanic Extensions - CORS保護 +--- + +# CORS保護 + +クロスオリジンリソース共有(CORS)は、単独で_巨大_トピックです。 ここのドキュメントでは、_何_についての詳細を説明することはできません。 あなたは非常にそれによって提示されたセキュリティの問題を理解するために、独自にいくつかの研究を行うことを奨励されています, そして、ソリューションの背後にある理論. [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) は素晴らしい第一歩です。 + +超簡単な言葉で CORS保護は、ウェブページが別のドメインから情報にアクセスできる方法と時間を容易にするためにブラウザが使用するフレームワークです。 これは、シングルページアプリケーションを構築するすべての人にとって非常に重要です。 フロントエンドは `https://portal.myapp.com` のようなドメインであることがよくありますが、 `https://api.myapp.com` からバックエンドにアクセスする必要があります。 + +ここでの実装は[`sanic-cors`](https://github.com/ashleysommer/sanic-cors)に大きくインスパイアされており、順番には [`flask-cors`](https://github.com/corydolphin/flask-cors) に基づいています。 したがって、ほぼ`sanic-cors`を`sanic-ext`に置き換えることができます。 + +## 基本的な実装 + +.. 列:: + +``` +format@@0(method) の例に示すように。 d#options), Sanic Extensionsは自動的にCORS保護を有効にします。しかし、あまり使いすぎることはありません。 + +*裸の最小値*では、アプリケーションにアクセスする目的の起点に`config.CORS_ORIGINS`を設定することを**高度に** 推奨します。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic, text +from sanic_ext import Extend + +app = Sanic(__name__) +app.config.CORS_ORIGINS = "http://foobar.com,http://bar.com" +Extend(app) + +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` + +``` +$ curl localhost:8000 -X OPTIONS -i +HTTP/1.1 204 No Content +allow: GET,HEAD,OPTIONS +access-control-allow-origin: http://foobar.com +connection: keep-alive +``` +```` + +## 設定 + +ただし、CORSの設定を開始すると、CORSの保護の真の機能が実現します。 ここにすべてのオプションの表があります。 + +| キー | タイプ | デフォルト | 説明 | +| -------------------------- | -------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `CORS_ALLOW_HEADERS` | `str` または `List[str]` | `"*"` | `access-control-allow-headers` に表示されるヘッダーの一覧です。 | +| `CORS_ALWAYS_SEND` | `bool` | `True` | `True` の場合、 `access-control-allow-origin` の値は常に設定されます。 `False` の場合、`Origin` ヘッダーがある場合にのみ設定されます。 | +| `CORS_AUTOMATIC_OPTIONS` | `bool` | `True` | 入力されるプリフライトリクエストが受信されると、`access-control-allow-headers` 、 `access-control-max-age` 、 `access-control-allow-methods` ヘッダの値を自動的に設定するかどうか。 `False`の場合、これらの値は`@cors`デコレータで装飾されたルートにのみ設定されます。 | +| `CORS_EXPOSE_HEADERS` | `str` または `List[str]` | `""` | `access-control-expose-headers` ヘッダに設定されるヘッダの特定のリスト。 | +| `CORS_MAX_AGE` | `str`, `int`, `timedelta` | `0` | プリフライト応答の最大秒数は `access-control-max-age` ヘッダーを使用してキャッシュできます。 偽の値を指定すると、ヘッダーは設定されません。 | +| `CORS_METHODS` | `str` または `List[str]` | `""` | `access-control-allow-methods` ヘッダーに設定されているように、許可されたオリジンがアクセスできるHTTPメソッドです。 | +| `CORS_ORIGINS` | `str`, `List[str]`, `re.Pattern` | `"*"` | `access-control-allow-origin` ヘッダーに設定されているように、リソースにアクセスできるオリジンです。 | +| `CORS_SEND_WILD` | `bool` | `False` | `True`の場合、`origin`リクエストヘッダの代わりにワイルドカード`*`オリジンを送信します。 | +| `CORS_SUPPORT_CREDENTIALS` | `bool` | `False` | `access-control-allow-credentials` ヘッダーを設定します。 | +| `CORS_VARY_HEADER` | `bool` | `True` | `vary`ヘッダを追加するかどうか。 | + +_上記の「リスト[str]」と書かれている簡潔さのために、`リスト`、`set`、`frozenset`、または`tuple`のいずれかのインスタンスは受け入れられます。 あるいは、値が `str` の場合、カンマ区切りのリストにすることもできます。_ + +## ルートレベルの上書き + +.. 列:: + +``` +特定のルートのアプリ全体の設定を上書きする必要がある場合もあります。 これを可能にするには、 `@sanic_ext.cors()` デコレータを使用して、ルート固有の値を設定します。 + +このデコレータで上書きできる値は次のとおりです。 + +- `origin` +- `expose_headers` +- `allow_headers` +- `allow_methods` +- `supports_credentials` +- `max_age` +``` + +.. 列:: + +```` +```python +from sanic_ext import cors + +app.config.CORS_ORIGINS = "https://foo.com" + +@app.get("/", host="bar.com") +@cors(origin="https://bar.com") +async def hello_world(request): + return text("Hello, world.") +``` +```` diff --git a/guide/content/ja/plugins/sanic-ext/http/methods.md b/guide/content/ja/plugins/sanic-ext/http/methods.md new file mode 100644 index 0000000000..2a0c72957c --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/http/methods.md @@ -0,0 +1,165 @@ +--- +title: サニックエクステンション - HTTPメソッド +--- + +# HTTPメソッド + +## 自動エンドポイント + +デフォルトの動作は、すべての `GET` ルートの `HEAD` エンドポイントと、すべての +ルートの `OPTIONS` エンドポイントを自動的に生成することです。 さらに、 `TRACE` エンドポイントを自動生成するオプションもあります。 ただし、これらは +デフォルトでは有効になっていません。 + +### 頭 + +.. 列:: + +``` +- **Configuration**: `AUTO_HEAD` (default `True`) +- **MDN**: [Read more](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) + +A `HEAD` request provides the headers and an otherwise identical response to what a `GET` request would provide. +However, it does not actually return the body. +``` + +.. 列:: + +```` +```python +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` + +Given the above route definition, Sanic Extensions will enable `HEAD` responses, as seen here. + +``` +$ curl localhost:8000 --head +HTTP/1.1 200 OK +access-control-allow-origin: * +content-length: 13 +connection: keep-alive +content-type: text/plain; charset=utf-8 +``` +```` + +### オプション + +.. 列:: + +``` +- **Configuration**: `AUTO_OPTIONS` (default `True`) +- **MDN**: [Read more](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS) + +`OPTIONS` requests provide the recipient with details about how the client is allowed to communicate with a given +endpoint. +``` + +.. 列:: + +```` +```python +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` + +Given the above route definition, Sanic Extensions will enable `OPTIONS` responses, as seen here. + +It is important to note that we also see `access-control-allow-origins` in this example. This is because +the [CORS protection](cors.md) is enabled by default. + +``` +$ curl localhost:8000 -X OPTIONS -i +HTTP/1.1 204 No Content +allow: GET,HEAD,OPTIONS +access-control-allow-origin: * +connection: keep-alive +``` +```` + +.. tip:: + +``` +Sanic Extensionsは自動的にこれらのルートを設定しますが、`@app.options`ルートを手動で作成する場合、上書きされることはありません。 +``` + +### トレースする + +.. 列:: + +``` +- **Configuration**: `AUTO_TRACE` (default `False`) +- **MDN**: [Read more](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/TRACE) + +By default, `TRACE` endpoints will **not** be automatically created. However, Sanic Extensions **will allow** you to +create them if you wanted. This is something that is not allowed in vanilla Sanic. +``` + +.. 列:: + +```` +```python +@app.route("/", methods=["trace"]) +async def handler(request): + ... +``` + +To enable auto-creation of these endpoints, you must first enable them when extending Sanic. + +```python +from sanic_ext import Extend, Config + +app.extend(config=Config(http_auto_trace=True)) +``` + +Now, assuming you have some endpoints setup, you can trace them as shown here: + +``` +$ curl localhost:8000 -X TRACE +TRACE / HTTP/1.1 +Host: localhost:9999 +User-Agent: curl/7.76.1 +Accept: */* +``` +```` + +.. tip:: + +``` +`AUTO_TRACE` のセットアップはとても役に立ちます。 アプリケーションがプロキシの背後に展開されている場合は特にプロキシがどのように動作するかを判断するのに役立ちます +``` + +## 追加メソッドのサポート + +Vanilla Sanicは以下のHTTPメソッドでエンドポイントを構築できます。 + +- [GET](/ja/guide/basics/routing.html#get) +- [POST](/ja/guide/basics/routing.html#post) +- [PUT](/ja/guide/basics/routing.html#put) +- [HEAD](/ja/guide/basics/routing.html#head) +- [OPTIONS](/ja/guide/basics/routing.html#options) +- [PATCH](/ja/guide/basics/routing.html#patch) +- [DELETE](/ja/guide/basics/routing.html#delete) + +詳細は [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) を参照してください。 + +.. 列:: + +``` +There are, however, two more "standard" HTTP methods: `TRACE` and `CONNECT`. Sanic Extensions will allow you to build +endpoints using these methods, which would otherwise not be allowed. + +It is worth pointing out that this will *NOT* enable convenience methods: `@app.trace` or `@app.connect`. You need to +use `@app.route` as shown in the example here. +``` + +.. 列:: + +```` +```python +@app.route("/", methods=["trace", "connect"]) +async def handler(_): + return empty() +``` +```` diff --git a/guide/content/ja/plugins/sanic-ext/injection.md b/guide/content/ja/plugins/sanic-ext/injection.md new file mode 100644 index 0000000000..62c0b2a8b1 --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/injection.md @@ -0,0 +1,396 @@ +--- +title: Sanic Extensions - 依存性インジェクション +--- + +# 依存性インジェクション + +依存性インジェクションは、定義された関数署名に基づいてルートハンドラに引数を追加するメソッドです。 具体的には、ハンドラの引数の**型注釈**を見てみます。 これは以下のような場合に便利です。 + +- (現在のセッションユーザのような) リクエストヘッダに基づいてオブジェクトを取得しています +- 特定のオブジェクトを特定の型に再作成する +- リクエストオブジェクトを使用してデータを先読みする +- 自動注入サービス + +`Extend` インスタンスには、依存注入に使用される2つの基本的なメソッドがあります。下位レベルの `add_dependency` と上位レベルの `dependency` です。 + +**低レベル**: `app.ext.add_dependency(...)` + +- `type: Type,`: オブジェクトの型になるいくつかの一意のクラス。 +- `constructor: Optional[Callable[..., Any]],` (OPTIONAL): その型を返す関数。 + +**より高いレベル**: `app.ext.dependency(...)` + +- `obj: Any`: 注入したいすべてのオブジェクト +- `name: Optional[str]`: 参照として交互に使用できるいくつかの名前 + +ここでいくつかのユースケースを見てみましょう。 + +.. 警告:: + +``` +v21.12 より前に依存性インジェクションを使用した場合、下位レベルの API メソッドは `injection` と呼ばれました。 それ以降、`add_dependency`に名前が変更され、v21で始まります。 2 `injection`は`add_dependency`のエイリアスです。`injection`メソッドはv22.6の削除のために非推奨となりました。 +``` + +## 基本的な実装 + +最も単純なユースケースは、単に値を再キャストすることです。 + +.. 列:: + +``` +これは、マッチしたパスパラメータに基づいて生成するモデルがある場合に便利です。 +``` + +.. 列:: + +```` +```python +@dataclass +class IceCream: + flavor: str + + def __str__(self) -> str: + return f"{self.flavor.title()} (Yum!)" + +app.ext.add_dependency(IceCream) + +@app.get("/") +async def ice_cream(request, flavor: IceCream): + return text(f"You chose: {flavor}") +``` + +``` +$ curl localhost:8000/chocolate +You chose Chocolate (Yum!) +``` +```` + +.. 列:: + +``` +これは、`type`引数のコンストラクタにキーワード引数を渡すことで動作します。前の例はこれと同じです。 +``` + +.. 列:: + +```` +```python +flavor = IceCream(flavor="chocolate") +``` +```` + +## 追加のコンストラクター + +.. 列:: + +``` +コンストラクタを渡す必要がある場合もあります。これは関数や、コンストラクタとして動作するclassメソッドであってもよいでしょう。 この例では、`Person を呼び出す注入を作成しています。 最初に「やり直す」。 + +この例でも重要なことは、実際に **two (2)** オブジェクトを注入していることです! もちろん、このようにする必要はありませんが、関数署名に基づいてオブジェクトを注入します。 +``` + +.. 列:: + +```` +```python +@dataclass +class PersonID: + person_id: int + +@dataclass +class Person: + person_id: PersonID + name: str + age: int + + @classmethod + async def create(cls, request: Request, person_id: int): + return cls(person_id=PersonID(person_id), name="noname", age=111) + + +app.ext.add_dependency(Person, Person.create) +app.ext.add_dependency(PersonID) + +@app.get("/person/") +async def person_details( + request: Request, person_id: PersonID, person: Person +): + return text(f"{person_id}\n{person}") +``` + +``` +$ curl localhost:8000/person/123 +PersonID(person_id=123) +Person(person_id=PersonID(person_id=123), name='noname', age=111) +``` +```` + +`constructor` が ext.add_dependency`(この例のように) に渡されたときに呼び出されます。 そうでない場合は、`type`を呼び出してオブジェクトを作成します。`constructor\`を渡すことについて、いくつかの重要なことに注意してください。 + +1. 位置`request: Request`引数は通常、期待されています。 `Person を見なさい。 上記の reate` メソッドは、`request` と format@@0(#arbitrary-constructors) を使用して、`request` を必要としない呼び出し元を使用する方法を例としています。 +2. マッチしたパスパラメータはすべてキーワード引数として注入されます。 +3. 依存関係はチェーンとネストが可能です。 前の例では、 `Person` dataclass が `PersonID` になっていることに注意してください。 つまり、 `PersonID` が最初に呼び出され、 `Person.create` を呼び出すときにその値がキーワード引数に追加されます。 + +## 任意のコンストラクター + +.. 列:: + +``` +時々、注入可能な _without_ を `Request` オブジェクトにしたいと思うかもしれません。 これは、オブジェクトを作成する任意のクラスや関数がある場合に便利です。 callable が必要な引数を持っている場合は、それ自体が注入可能なオブジェクトである必要があります。 + +これは、単一のリクエストの寿命のためにのみ存在すべきサービスやその他のタイプのオブジェクトがある場合に非常に便利です。 たとえば、このパターンを使用して、データベース プールから単一の接続をプルすることができます。 +``` + +.. 列:: + +```` +```python +class Alpha: + ... + +class Beta: + def __init__(self, alpha: Alpha) -> None: + self.alpha = alpha + +app.ext.add_dependency(Alpha) +app.ext.add_dependency(Beta) + +@app.get("/beta") +async def handler(request: Request, beta: Beta): + assert isinstance(beta.alpha, Alpha) +``` +```` + +_v22.9_に追加されました + +## `Request`からのオブジェクト + +.. 列:: + +```` +Sometimes you may want to extract details from the request and preprocess them. You could, for example, cast the request JSON to a Python object, and then add some additional logic based upon DB queries. + +.. warning:: + + If you plan to use this method, you should note that the injection actually happens *before* Sanic has had a chance to read the request body. The headers should already have been consumed. So, if you do want access to the body, you will need to manually consume as seen in this example. + + ```python + await request.receive_body() + ``` + + + This could be used in cases where you otherwise might: + + - use middleware to preprocess and add something to the `request.ctx` + - use decorators to preprocess and inject arguments into the request handler + + In this example, we are using the `Request` object in the `compile_profile` constructor to run a fake DB query to generate and return a `UserProfile` object. +```` + +.. 列:: + +```` +```python +@dataclass +class User: + name: str + +@dataclass +class UserProfile: + user: User + age: int = field(default=0) + email: str = field(default="") + + def __json__(self): + return ujson.dumps( + { + "name": self.user.name, + "age": self.age, + "email": self.email, + } + ) + +async def fake_request_to_db(body): + today = date.today() + email = f'{body["name"]}@something.com'.lower() + difference = today - date.fromisoformat(body["birthday"]) + age = int(difference.days / 365) + return UserProfile( + User(body["name"]), + age=age, + email=email, + ) + +async def compile_profile(request: Request): + await request.receive_body() + profile = await fake_request_to_db(request.json) + return profile + +app.ext.add_dependency(UserProfile, compile_profile) + +@app.patch("/profile") +async def update_profile(request, profile: UserProfile): + return json(profile) +``` + +``` +$ curl localhost:8000/profile -X PATCH -d '{"name": "Alice", "birthday": "2000-01-01"}' +{ + "name":"Alice", + "age":21, + "email":"alice@something.com" +} +``` +```` + +## 注入サービス + +データベース接続プールのようなものを作成し、 `app.ctx` オブジェクトに保存するのは一般的なパターンです。 これにより、アプリケーション全体で利用可能になります。これは確かに便利です。 しかし欠点の一つは、もはや一緒に作業する型付けされたオブジェクトを持っていないことです。 依存性注入を使用してこれを修正できます。 最初に、先ほどの例で使ったように、下位レベルの `add_dependency` を使ってコンセプトを示します。 しかし、より高いレベルの `dependency` メソッドを使うより良い方法があります。 + +### `add_dependency`を使用する下位レベルのAPI。 + +.. 列:: + +``` +これは format@@0(#objects-from-the-request) に非常によく似ています。目的は `Request` オブジェクトから何かを抽出することです。 この例では、データベースオブジェクトが `app.ctx` インスタンス上に作成され、依存性インジェクションコンストラクタで返されます。 +``` + +.. 列:: + +```` +```python +class FakeConnection: + async def execute(self, query: str, **arguments): + return "result" + +@app.before_server_start +async def setup_db(app, _): + app.ctx.db_conn = FakeConnection() + app.ext.add_dependency(FakeConnection, get_db) + +def get_db(request: Request): + return request.app.ctx.db_conn + + + +@app.get("/") +async def handler(request, conn: FakeConnection): + response = await conn.execute("...") + return text(response) +``` +``` +$ curl localhost:8000/ +result +``` +```` + +### `dependency`を使用するより高いレベルのAPI。 + +.. 列:: + +``` +依存性注入を追加するときに利用できる実際の *object* があるので、より高いレベルの `dependency` メソッドを使用できます。 これにより、パターンが書けるようになります。 + +アプリケーションインスタンスの寿命を通じて存在し、特定の要求ではない何かを注入する場合は、このメソッドを常に使用する必要があります。 特定の要求ではないため、サービス、サードパーティクライアント、および接続プールに非常に便利です。 +``` + +.. 列:: + +```` +```python +class FakeConnection: + async def execute(self, query: str, **arguments): + return "result" + +@app.before_server_start +async def setup_db(app, _): + db_conn = FakeConnection() + app.ext.dependency(db_conn) + +@app.get("/") +async def handler(request, conn: FakeConnection): + response = await conn.execute("...") + return text(response) +``` +``` +$ curl localhost:8000/ +result +``` +```` + +## 一般的なタイプ + +format@@0(https://docs.python.org/3/library/typing.html#typing.Generic)を使用するときは、気をつけてください。 Sanicの依存性注入が機能する方法は、型の定義全体に一致することです。 したがって、`Foo` は `Foo[str] ` と同じではありません。 型が推測されるので、format@@0(#the-higher-level-api-using-dependency) を使用しようとすると、これは特に難しいことがあります。 + +.. 列:: + +``` +例えば、`Test[str] `の定義がないため、これは **期待通りに動作しません** 。 +``` + +.. 列:: + +```` +```python +import typing +from sanic import Sanic, text + +T = typing.TypeVar("T") + +class Test(typing.Generic[T]): + test: T + +app = Sanic("testapp") +app.ext.dependency(Test()) + +@app.get("/") +def test(request, test: Test[str]): + ... +``` +```` + +.. 列:: + +``` +この例を動作させるには、注入するタイプの明示的な定義を追加する必要があります。 +``` + +.. 列:: + +```` +```python +import typing +from sanic import Sanic, text + +T = typing.TypeVar("T") + +class Test(typing.Generic[T]): + test: T + +app = Sanic("testapp") +_singleton = Test() +app.ext.add_dependency(Test[str], lambda: _singleton) + +@app.get("/") +def test(request, test: Test[str]): + ... +``` +```` + +## 設定 + +.. 列:: + +``` +デフォルトでは、依存関係は `http.routing.after` [signal](../../guide/advanced/signals.md#built-in-signals) の後に注入されます。v22.9 以降は、`http.handler.before` 信号に変更できます。 +``` + +.. 列:: + +```` +```python +app.config.INJECTION_SIGNAL = "http.handler.before" +``` +```` + +_v22.9_に追加されました diff --git a/guide/content/ja/plugins/sanic-ext/logger.md b/guide/content/ja/plugins/sanic-ext/logger.md new file mode 100644 index 0000000000..c8fa9d7308 --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/logger.md @@ -0,0 +1,38 @@ +--- +title: Sanic Extensions - バックグラウンドロガー +--- + +# バックグラウンドログ + +バックグラウンドロガーには `sanic>=22.9` と `sanic-ext>=22.9` が必要です。 + +Sanic Extensionsを設定すると、バックグラウンドプロセスからすべてのメッセージをログに記録できます。 format@@0(../../guide/deployment/manager.md#single-process-mode) ではないことが必要です。 + +ロギングは高価な操作になることがあります。 すべてのログをバックグラウンドプロセスにプッシュすることで、パフォーマンスのメリットが得られる可能性があります。 + +## セットアップ + +.. 列:: + +``` +バックグラウンドロガーが無効になっています。使用する場合はオプトインする必要があります。 +``` + +.. 列:: + +```` +```python +app.config.LOGGING = True +``` +```` + +## どのように機能します + +有効にすると、拡張機能は `multiprocessing.Queue` を作成します。 format@@0(../../guide/best-practices/logging.md) 上のすべてのハンドラを削除し、[`QueueHandler`](https://docs.python.org/3/library/logging.handlers.html#queuehandler) に置き換えます。 メッセージがログされると、ハンドラによってキューにプッシュされます。 バックグラウンドプロセスで元々あったログハンドラを読み取ることができます つまり、通常どおりにロギングを設定することができ、「ただ動作する」ことができます。 + +## 設定 + +| キー | タイプ | デフォルト | 説明 | +| ------------------------------------------------------------- | ------ | ------- | ----------------------- | +| ログイン | `bool` | `False` | この拡張機能を有効にするかどうか。 | +| LOGGING_QUEUE_最大サイズ | `int` | `4096` | メッセージが拒否される前のキューの最大サイズ。 | diff --git a/guide/content/ja/plugins/sanic-ext/openapi.md b/guide/content/ja/plugins/sanic-ext/openapi.md new file mode 100644 index 0000000000..ae66d6e105 --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/openapi.md @@ -0,0 +1,11 @@ +--- +title: サニックエクステンション - OAS +--- + +# Openapi + +- デコレータでドキュメントを追加する +- CBV のドキュメント +- autoconの使用 +- Redoc/swaggerによるドキュメントのレンダリング +- 検証 diff --git a/guide/content/ja/plugins/sanic-ext/openapi/advanced.md b/guide/content/ja/plugins/sanic-ext/openapi/advanced.md new file mode 100644 index 0000000000..75589a9bcc --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/openapi/advanced.md @@ -0,0 +1,13 @@ +--- +title: サニックエクステンション - Advanced OAS +--- + +# 高度な設定 + +_Documentation in progress_ + +## CBV + +## 建設計画 + +## コンポーネント diff --git a/guide/content/ja/plugins/sanic-ext/openapi/autodoc.md b/guide/content/ja/plugins/sanic-ext/openapi/autodoc.md new file mode 100644 index 0000000000..4c3d7389ec --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/openapi/autodoc.md @@ -0,0 +1,154 @@ +--- +title: サニックエクステンション - 自動ドキュメント +--- + +# 自動ドキュメント + +エンドポイントの文書化を容易にするために、Sanic Extensionsはdocstringを使ってドキュメントを作成します。 + +## 概要と説明 + +.. 列:: + +``` +要約と説明を作成するために、関数の docstring が使用されます。 この例からわかるように、docstringは最初の行を要約として使用するようにパースされています。 文字列の説明として残りの部分を指定します。 +``` + +.. 列:: + +```` +```python +@app.get("/foo") +async def handler(request, something: str): + """This is a simple foo handler + + It is helpful to know that you could also use **markdown** inside your + docstrings. + + - one + - two + - three""" + return text(">>>") +``` +```json +"paths": { + "/foo": { + "get": { + "summary": "This is a simple foo handler", + "description": "It is helpful to know that you could also use **markdown** inside your
docstrings.

- one
- two
- three", + "responses": { + "default": { + "description": "OK" + } + }, + "operationId": "get_handler" + } + } +} +``` +```` + +## 操作レベルYAML + +.. 列:: + +``` +これを展開するには、有効なOpenAPI YAML を docstring に追加します。単純に `openapi:` を含む行を追加し、その後に YAML を追加します。 + +例に示す`---`は*不要*です。 YAML をドキュメント文字列の明確なセクションとして視覚的に識別するために、そこにあるだけです。 +``` + +.. 列:: + +```` +```python +@app.get("/foo") +async def handler(request, something: str): + """This is a simple foo handler + + Now we will add some more details + + openapi: + --- + operationId: fooDots + tags: + - one + - two + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: Just some dots + """ + return text("...") +``` +```json +"paths": { + "/foo": { + "get": { + "operationId": "fooDots", + "summary": "This is a simple foo handler", + "description": "Now we will add some more details", + "tags": [ + "one", + "two" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "How many items to return at one time (max 100)", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Just some dots" + } + } + } + } +} +``` +```` + +.. note:: + +``` +YAML ドキュメントとデコレータの両方が使用される場合、ドキュメントを生成する際に優先されるデコレータのコンテンツです。 +``` + +## docstringsを除外 + +.. 列:: + +``` +Sometimes a function may contain a docstring that is not meant to be consumed inside the documentation. + +**Option 1**: Globally turn off auto-documentation `app.config.OAS_AUTODOC = False` + +**Option 2**: Disable it for the single handler with the `@openapi.no_autodoc` decorator +``` + +.. 列:: + +```` +```python +@app.get("/foo") +@openapi.no_autodoc +async def handler(request, something: str): + """This is a docstring about internal info only. Do not parse it. + """ + return text("...") +``` +```` diff --git a/guide/content/ja/plugins/sanic-ext/openapi/basics.md b/guide/content/ja/plugins/sanic-ext/openapi/basics.md new file mode 100644 index 0000000000..3eec956e06 --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/openapi/basics.md @@ -0,0 +1,84 @@ +--- +title: サニックエクステンション - Basic OAS +--- + +# 基本 + +.. note:: + +``` +Sanic ExtensionsのOpenAPI実装は、[`sanic-openapi`](https://github.com/sanic-org/sanic-openapi)のOAS3実装に基づいています。 実際、Sanic Extensionsは、Sanic Extensionsのリリース時にメンテナンスモードになったプロジェクトの後継者として大きな意味を持っています。 以前に `sanic-openapi` を使用していた場合は、Sanic Extensionsへのアップグレードの簡単なパスが必要です。 残念ながら、このプロジェクトは OAS2 仕様をサポートしていません。 +``` + +.. 列:: + +``` +Sanic Extensionsは、[v3.0 OpenAPI仕様](https://swagger.io/specification/)を使用して自動的に生成されたAPIドキュメントを提供しています。特別なことはありません。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic + +app = Sanic("MyApp") + +# Add all of your views +``` +```` + +これを行うと、既存のアプリケーションに基づいてすでに生成された美しいドキュメントが表示されます。 + +- [http://localhost:8000/docs](http://localhost:8000/docs) +- [http://localhost:8000/docs/redoc](http://localhost:8000/docs/redoc) +- [http://localhost:8000/docs/swagger](http://localhost:8000/docs/swagger) + +ドキュメントのルート変更について学ぶには、[section on configuration](../configuration.md) をチェックしてください。 2 つの UI のいずれかをオフにして、`/docs` ルートでどのUIが利用できるかをカスタマイズすることもできます。 + +.. 列:: + +``` +Using [Redoc](https://github.com/Redocly/redoc) + +![Redoc](/assets/images/sanic-ext-redoc.png) +``` + +.. 列:: + +``` +or [Swagger UI](https://github.com/swagger-api/swagger-ui) + +![Swagger UI](/assets/images/sanic-ext-swagger.png) +``` + +## 仕様メタデータの変更 + +.. 列:: + +``` +メタデータを変更したい場合は、 `describe` メソッドを使用してください。 + +`dedent` の例では、複数行の文字列を少しクリーナーにするために `description` 引数を使用しています。 これは必要ありません。任意の文字列値を渡すことができます。 +``` + +.. 列:: + +```` +```python +from textwrap import dedent + +app.ext.openapi.describe( + "Testing API", + version="1.2.3", + description=dedent( + """ + # Info + This is a description. It is a good place to add some _extra_ doccumentation. + + **MARKDOWN** is supported. + """ + ), +) +``` +```` diff --git a/guide/content/ja/plugins/sanic-ext/openapi/decorators.md b/guide/content/ja/plugins/sanic-ext/openapi/decorators.md new file mode 100644 index 0000000000..daaf85adb2 --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/openapi/decorators.md @@ -0,0 +1,520 @@ +--- +title: Sanic Extensions - OAS Decorators +--- + +# デコレーター + +スキーマにコンテンツを追加する主なメカニズムは、エンドポイントを飾ることです。 過去に +が`sanic-openapi`を使ったことがあるなら、これはよく知っているはずです。 The decorators and their arguments match closely +the [OAS v3.0 specification](https://swagger.io/specification/). + +.. 列:: + +``` +All of the examples show will wrap around a route definition. When you are creating these, you should make sure that +your Sanic route decorator (`@app.route`, `@app.get`, etc) is the outermost decorator. That is to say that you should +put that first and then one or more of the below decorators after. +``` + +.. 列:: + +```` +```python +from sanic_ext import openapi + +@app.get("/path/to/") +@openapi.summary("This is a summary") +@openapi.description("This is a description") +async def handler(request, something: str): + ... +``` +```` + +.. 列:: + +``` +You will also see a lot of the below examples reference a model object. For the sake of simplicity, the examples will +use `UserProfile` that will look like this. The point is that it can be any well-typed class. You could easily imagine +this being a `dataclass` or some other kind of model object. +``` + +.. 列:: + +```` +```python +class UserProfile: + name: str + age: int + email: str +``` +```` + +## 定義デコレーター + +### `@openapi.definition` + +`@openapi.definition` デコレータを使用すると、一度にパス上の操作のすべての部分を定義できます。 それは装飾者の残りの部分と同じ操作定義を作成する能力を持っているという点でオムニバス +デコレータです。 +複数のフィールド固有のデコレータまたは単一のデコレータを使用することは、開発者にとってスタイルの選択です。 + +フィールドは意図的に複数の型を受け入れることで、操作を定義するのが最も簡単になります。 + +**引数** + +| フィールド | タイプ | +| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `body` | **dict, RequestBody, _YourModel_** | +| `deprecated` | **bool** | +| `description` | **str** | +| `document` | **str, ExternalDocumentation** | +| `exclude` | **bool** | +| `operation` | **str** | +| `パラメータ` | **str, dict, Parameter, [str], [dict], [Parameter]** | +| `response` | **dict, Response, _YourModel_, [dict], [Response]** | +| `summary` | **str** | +| `tag` | **str, Tag, [str], [Tag]** | +| `secured` | **Dict[str, Any]** | + +**例** + +.. 列:: + +```` +```python +@openapi.definition( + body=RequestBody(UserProfile, required=True), + summary="User profile update", + tag="one", + response=[Success, Response, status=400)], +) +``` +```` + +.. 列:: + +_より多くの例については以下を参照してください。 以下のデコレータのいずれかの値は、対応する +キーワード引数で使用できます。_ + +## フィールド固有の装飾 + +以下のデコレータは `@openapi` に基づいています。 + +### body + +**引数** + +| フィールド | タイプ | +| ----------- | ---------------------------------- | +| **content** | **_YourModel_, dict, RequestBody** | + +**例** + +.. 列:: + +```` +```python +@openapi.body(UserProfile) +``` + +```python +@openapi.body({"application/json": UserProfile}) +``` + +```python +@openapi.body(RequestBody({"application/json": UserProfile})) +``` +```` + +.. 列:: + +```` +```python +@openapi.body({"content": UserProfile}) +``` + +```python +@openapi.body(RequestBody(UserProfile)) +``` + +```python +@openapi.body({"application/json": {"description": ...}}) +``` +```` + +### 非推奨です + +**引数** + +_なし_ + +**例** + +.. 列:: + +```` +```python +@openapi.deprecated() +``` +```` + +.. 列:: + +```` +```python +@openapi.deprecated +``` +```` + +### 説明 + +**引数** + +| フィールド | タイプ | +| ------ | ------- | +| `text` | **str** | + +**例** + +.. 列:: + +```` +```python +@openapi.description( + """This is a **description**. + +## You can use `markdown` + +- And +- make +- lists. +""" +) +``` +```` + +.. 列:: + +### ドキュメント + +**引数** + +| フィールド | タイプ | +| ------------- | ------- | +| `url` | **str** | +| `description` | **str** | + +**例** + +.. 列:: + +```` +```python +@openapi.document("http://example.com/docs") +``` +```` + +.. 列:: + +```` +```python +@openapi.document(ExternalDocumentation("http://example.com/more")) +``` +```` + +### 除外する + +他のデコレータと同様にルート定義に使用したり、ブループリントで呼び出したりできます。 + +**引数** + +| フィールド | タイプ | デフォルト | +| ------ | -------- | -------- | +| `flag` | **bool** | **True** | +| `bp` | **設計図** | | + +**例** + +.. 列:: + +```` +```python +@openapi.exclude() +``` +```` + +.. 列:: + +```` +```python +openapi.exclude(bp=some_blueprint) +``` +```` + +### 操作 + +操作 ID を設定します。 + +**引数** + +| フィールド | タイプ | +| ------ | ------- | +| `name` | **str** | + +**例** + +.. 列:: + +```` +```python +@openapi.operation("doNothing") +``` +```` + +.. 列:: + +**引数** + +| フィールド | タイプ | デフォルト | +| ---------- | ----------------------------------------- | ----------- | +| `name` | **str** | | +| `schema` | _**type**_ | **str** | +| `location` | **"query", "header", "path" or "cookie"** | **"query"** | + +**例** + +.. 列:: + +```` +```python +@openapi.parameter("thing") +``` + +```python +@openapi.parameter(parameter=Parameter("foobar", deprecated=True)) +``` +```` + +.. 列:: + +```` +```python +@openapi.parameter("Authorization", str, "header") +``` + +```python +@openapi.parameter("thing", required=True, allowEmptyValue=False) +``` +```` + +### 応答 + +**引数** + +`Response` オブジェクトを使用する場合は、他の引数を渡すべきではありません。 + +| フィールド | タイプ | +| ------------- | ----------------------------- | +| `status` | **int** | +| `content` | **_type_, _YourModel_, dict** | +| `description` | **str** | +| `response` | **応答** | + +**例** + +.. 列:: + +```` +```python +@openapi.response(200, str, "This is endpoint returns a string") +``` + +```python +@openapi.response(200, {"text/plain": str}, "...") +``` + +```python +@openapi.response(response=Response(UserProfile, description="...")) +``` + +```python +@openapi.response( + response=Response( + { + "application/json": UserProfile, + }, + description="...", + status=201, + ) +) +``` +```` + +.. 列:: + +```` +```python +@openapi.response(200, UserProfile, "...") +``` + +```python +@openapi.response( + 200, + { + "application/json": UserProfile, + }, + "Description...", +) +``` +```` + +### summary + +**引数** + +| フィールド | タイプ | +| ------ | ------- | +| `text` | **str** | + +**例** + +.. 列:: + +```` +```python +@openapi.summary("This is an endpoint") +``` +```` + +.. 列:: + +### タグ + +**引数** + +| フィールド | タイプ | +| ------- | ------------ | +| `*args` | **str, Tag** | + +**例** + +.. 列:: + +```` +```python +@openapi.tag("foo") +``` +```` + +.. 列:: + +```` +```python +@openapi.tag("foo", Tag("bar")) +``` +```` + +### 保護されている + +**引数** + +| フィールド | タイプ | +| ----------------- | --------------------------------------------------------------------------- | +| `*args, **kwargs` | **str, Dict[str, Any]** | + +**例** + +.. 列:: + +```` +```python +@openapi.secured() +``` +```` + +.. 列:: + +.. 列:: + +```` +```python +@openapi.secured("foo") +``` +```` + +.. 列:: + +```` +```python +@openapi.secured("token1", "token2") +``` +```` + +.. 列:: + +```` +```python +@openapi.secured({"my_api_key": []}) +``` +```` + +.. 列:: + +```` +```python +@openapi.secured(my_api_key=[]) +``` +```` + +`add_security_scheme` を使用することを忘れないでください。 詳細は [security](./security.md) を参照してください。 +\`\` + +## Pydanticとの統合 + +Pydanticモデルはformat@@0(https://pydantic-docs.helpmanual.io/usage/schema/)を生成する機能を持っています。 + +.. 列:: + +``` +Pydanticモデルスキーマ生成を利用するには、スキーマの代わりに出力を渡してください。 +``` + +.. 列:: + +```` +```python +from sanic import Sanic, json +from sanic_ext import validate, openapi +from pydantic import BaseModel, Field + +@openapi.component +class Item(BaseModel): + name: str + description: str = None + price: float + tax: float = None + +class ItemList(BaseModel): + items: List[Item] + +app = Sanic("test") + +@app.get("/") +@openapi.definition( + body={ + "application/json": ItemList.schema( + ref_template="#/components/schemas/{model}" + ) + }, +) +async def get(request): + return json({}) +``` +```` + +.. note:: + +``` +`ref_template`を設定することが重要です。デフォルトではPydanticは標準のOASではないテンプレートを選択します。 これにより、最終的なドキュメントを生成する際にスキーマが見つかりません。 +``` + +_v22.9_に追加されました diff --git a/guide/content/ja/plugins/sanic-ext/openapi/security.md b/guide/content/ja/plugins/sanic-ext/openapi/security.md new file mode 100644 index 0000000000..d9b90d0d7e --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/openapi/security.md @@ -0,0 +1,110 @@ +--- +title: サニックエクステンション - OASセキュリティスキーム +--- + +# セキュリティスキーム + +認証スキームを文書化するには、2つのステップがあります。 + +_Security は v21.12.2_ からのみ利用できます。 + +## スキームをドキュメント + +.. 列:: + +```` +The first thing that you need to do is define one or more security schemes. The basic pattern will be to define it as: + +```python +add_security_scheme("", "") +``` + +The `type` should correspond to one of the allowed security schemes: `"apiKey"`, `"http"`, `"oauth2"`, `"openIdConnect"`. You can then pass appropriate keyword arguments as allowed by the specification. + +You should consult the [OpenAPI Specification](https://swagger.io/specification/) for details on what values are appropriate. +```` + +.. 列:: + +```` +```python +app.ext.openapi.add_security_scheme("api_key", "apiKey") +app.ext.openapi.add_security_scheme( + "token", + "http", + scheme="bearer", + bearer_format="JWT", +) +app.ext.openapi.add_security_scheme("token2", "http") +app.ext.openapi.add_security_scheme( + "oldschool", + "http", + scheme="basic", +) +app.ext.openapi.add_security_scheme( + "oa2", + "oauth2", + flows={ + "implicit": { + "authorizationUrl": "http://example.com/auth", + "scopes": { + "on:two": "something", + "three:four": "something else", + "threefour": "something else...", + }, + } + }, +) +``` +```` + +## エンドポイントをドキュメント + +.. 列:: + +``` +2つのオプションがあります。文書には_all_エンドポイントがあります。 +``` + +.. 列:: + +```` +```python +app.ext.openapi.secured() +app.ext.openapi.secured("token") +``` +```` + +.. 列:: + +``` +または、特定のルートのみをドキュメントします。 +``` + +.. 列:: + +```` +```python +@app.route("/one") +async def handler1(request): + """ + openapi: + --- + security: + - foo: [] + """ + +@app.route("/two") +@openapi.secured("foo") +@openapi.secured({"bar": []}) +@openapi.secured(baz=[]) +async def handler2(request): + ... + +@app.route("/three") +@openapi.definition(secured="foo") +@openapi.definition(secured={"bar": []}) +async def handler3(request): + ... +``` +```` diff --git a/guide/content/ja/plugins/sanic-ext/openapi/ui.md b/guide/content/ja/plugins/sanic-ext/openapi/ui.md new file mode 100644 index 0000000000..7c9bcd7cf6 --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/openapi/ui.md @@ -0,0 +1,30 @@ +--- +title: サニックエクステンション - OAS UI +--- + +# UI + +Sanic Extensionsには、RedocとSwaggerの両方のインターフェースが付属しています。 あなたはそれらのいずれかまたは両方を使用する選択があります。 以下の端点があなたのためにセットアップされています。元の `/docs` にはRedocが表示されています。 + +- `/docs` +- `/docs/openapi.json` +- `/docs/redoc` +- `/docs/swagger` +- `/docs/openapi-config` + +## 設定オプション + +| **キー** | **Type** | **デフォルト** | **降順** | +| -------------------------- | --------------- | ------------------- | ---------------------------------------------------------------------------------------- | +| `OAS_IGNORE_HEAD` | `bool` | `True` | 「HEAD」エンドポイントを表示するかどうかを設定します。 | +| `OAS_IGNORE_Options` | `bool` | `True` | 「OPTIONS」エンドポイントを表示するかどうかを設定します。 | +| `OAS_PATH_TO_REDOC_HTML` | `Optional[str]` | `なし` | デフォルトのやり直しのHTMLをオーバーライドするHTMLへのパス | +| `OAS_PATH_TO_SWAGGER_HTML` | `Optional[str]` | `なし` | デフォルトの Swagger HTML を上書きするための HTML へのパス | +| `OAS_UI_DEFAULT` | `Optional[str]` | `"redoc"` | `redoc` または `swagger` に設定できます。 ベースルート上に表示する UI を制御します。 `None` に設定されている場合、ベースルートは設定されません。 | +| `OAS_UI_REDOC` | `bool` | `True` | Redoc UI を有効にするかどうか。 | +| `OAS_UI_SWAGGER` | `bool` | `True` | Swagger UI を有効にするかどうか。 | +| `OAS_URI_TO_CONFIG` | `str` | `"/openapi-config"` | Swagger が使用する OpenAPI 設定への URI パス | +| `OAS_URI_TO_JSON` | `str` | `"/openapi.json"` | JSON ドキュメントへの URI パスです。 | +| `OAS_URI_TO_REDOC` | `str` | `"/redoc"` | 再ドックへのURIパス | +| `OAS_URI_TO_SWAGGER` | `str` | `"/swagger"` | Swagger への URI パスです。 | +| `OAS_URL_PREFIX` | `str` | `"/docs"` | OpenAPIドキュメントのブループリントに使用するURLプレフィックス。 | diff --git a/guide/content/ja/plugins/sanic-ext/templating/html5tagger.md b/guide/content/ja/plugins/sanic-ext/templating/html5tagger.md new file mode 100644 index 0000000000..7a13eae73b --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/templating/html5tagger.md @@ -0,0 +1,7 @@ +--- +title: Sanic Extensions - html5tagger +--- + +# 近日公開 + +[sanic-org/html5tagger on GitHub](https://github.com/sanic-org/html5tagger/) を参照してください。 diff --git a/guide/content/ja/plugins/sanic-ext/templating/jinja.md b/guide/content/ja/plugins/sanic-ext/templating/jinja.md new file mode 100644 index 0000000000..df307b8608 --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/templating/jinja.md @@ -0,0 +1,171 @@ +--- +title: Sanic Extensions - Jinja +--- + +# テンプレート設定 + +Sanic Extensions は、テンプレートをルートハンドラに簡単に統合するのに役立ちます。 + +## 依存関係 + +**現在、 [Jinja](https://github.com/pallets/jinja/)のみサポートしています。** + +format@@0(https://jinja.palletsprojects.com/en/3.1.x/) テンプレートを作成する方法に慣れていない場合は、format@@1を参照してください。 + +Sanic Extensionsは、環境にインストールされている場合、自動的にJinjaをセットアップしてロードします。 したがって、必要なセットアップはJinjaのインストールだけです。 + +``` +pip install Jinja2 +``` + +## ファイルからテンプレートをレンダリングする + +あなたには3つの方法があります: + +1. テンプレートファイルを事前にロードするデコレータを使用する +2. レンダリングされた `HTTPResponse` オブジェクトを返す +3. `LazyResponse`を作るハイブリッドパターン。 + +`./templates/foo.html`というファイルがあるとしましょう。 + +```html + + + + + My Webpage + + + +

Hello, world!!!!

+
    + {% for item in seq %} +
  • {{ item }}
  • + {% endfor %} +
+ + + +``` + +Sanic + Jinjaでどのようにレンダリングできるか見てみましょう。 + +### Option 1 - as a decorator + +.. 列:: + +``` +このアプローチの利点は、起動時にテンプレートをあらかじめ定義できることです。 これは、より少ないフェッチがハンドラで起こる必要があることを意味し、したがって最速のオプションであるべきです。 +``` + +.. 列:: + +```` +```python +@app.get("/") +@app.ext.template("foo.html") +async def handler(request: Request): + return {"seq": ["one", "two"]} +``` +```` + +### Option 2 - 戻り値オブジェクトとして + +.. 列:: + +``` +これはSanicの`text`、`json`、`html`、`file`などのパターンを模倣するものです。 それはそれを直接制御しているので、レスポンスオブジェクトにほとんどのカスタマイズを可能にする。 他の `HTTPResponse` オブジェクトと同様に、ヘッダー、クッキーなどを制御できます。 +``` + +.. 列:: + +```` +```python +from sanic_ext import render + +@app.get("/alt") +async def handler(request: Request): + return await render( + "foo.html", context={"seq": ["three", "four"]}, status=400 + ) +``` +```` + +### Option 3 - hybrid/lazy + +.. 列:: + +``` +このアプローチでは、テンプレートはハンドラ内ではなく前面で定義されます(パフォーマンスのため)。 次に、`render` 関数はデコレータ内で適切な `HTTPResponse` を構築するために使用できる `LazyResponse` を返します。 +``` + +.. 列:: + +```` +```python +from sanic_ext import render + +@app.get("/") +@app.ext.template("foo.html") +async def handler(request: Request): + return await render(context={"seq": ["five", "six"]}, status=400) +``` +```` + +## 文字列からテンプレートをレンダリング + +.. 列:: + +``` +Pythonコードの中でテンプレートを書く(または生成する)ことを望むことがあり、HTMLファイルから_not_読み込みを望むことがあります。 この場合でも、上記で見た「render」関数を使うことができます。`template_source` を使うだけです。 +``` + +.. 列:: + +```` +```python +from sanic_ext import render +from textwrap import dedent + +@app.get("/") +async def handler(request): + template = dedent(""" + + + + + My Webpage + + + +

Hello, world!!!!

+
    + {% for item in seq %} +
  • {{ item }}
  • + {% endfor %} +
+ + + + """) + return await render( + template_source=template, + context={"seq": ["three", "four"]}, + app=app, + ) +``` +```` + +.. note:: + +``` +この例では、複数行の文字列の先頭にある空白を削除するために `textwrap.dedent` を使用します。 それは必要ありませんが、コードと生成されたソースの両方をきれいに保つためにちょうどいいタッチです。 +``` + +## 開発と自動再読み込み + +自動再読み込みがオンになっている場合、テンプレートファイルへの変更によりサーバーの再読み込みが実行されます。 + +## 設定 + +[settings](./configuration.md#settings) の `templating_enable_async` と `templating_path_to_templates` を参照してください。 diff --git a/guide/content/ja/plugins/sanic-ext/validation.md b/guide/content/ja/plugins/sanic-ext/validation.md new file mode 100644 index 0000000000..54c8e82904 --- /dev/null +++ b/guide/content/ja/plugins/sanic-ext/validation.md @@ -0,0 +1,201 @@ +--- +title: サニックエクステンション - 検証 +--- + +# 検証 + +Webアプリケーションの最も一般的に実装されている機能の1つは、ユーザー入力検証です。 明らかな理由から、これはセキュリティの問題だけでなく、単に明白な良い実践です。 データが期待値に合致していることを確認し、そうでない場合は `400` レスポンスを投げます。 + +## 実装 + +### Dataclassの検証 + +format@@0(https://docs.python.org/3/library/dataclasses.html)の導入により、Pythonは定義されたスキーマを満たすオブジェクトを簡単に作成できました。 しかし、標準ライブラリは型チェック検証のみをサポートしており、実行時の検証ではありません。 Sanic Extensionsは、 `dataclasses`を使って受信するリクエストに対して実行時の検証を行う機能を追加します。 `pydantic`または`attrs`のいずれかがインストールされている場合は、それらのライブラリのいずれかを使用することもできます。 + +.. 列:: + +``` +まず、モデルを定義します。 +``` + +.. 列:: + +```` +```python +@dataclass +class SearchParams: + q: str +``` +```` + +.. 列:: + +``` +次に、ルートに添付してください +``` + +.. 列:: + +```` +```python +from sanic_ext import validate + +@app.route("/search") +@validate(query=SearchParams) +async def handler(request, query: SearchParams: + return json(asdict(query)) +``` +```` + +.. 列:: + +``` +これで、受信リクエストのバリデーションが完了するはずです。 +``` + +.. 列:: + +```` +``` +$ curl localhost:8000/search +⚠️ 400 — Bad Request +==================== +Invalid request body: SearchParams. Error: missing a required argument: 'q' +``` +``` +$ curl localhost:8000/search\?q=python +{"q":"python"} +``` +```` + +### Pydanticによる検証 + +Pydanticモデルも使用できます。 + +.. 列:: + +``` +まず、モデルを定義します。 +``` + +.. 列:: + +```` +```python +class Person(BaseModel): + name: str + age: int +``` +```` + +.. 列:: + +``` +次に、ルートに添付してください +``` + +.. 列:: + +```` +```python +from sanic_ext import validate + +@app.post("/person") +@validate(json=Person) +async def handler(request, body: Person): + return json(body.dict()) +``` +```` + +.. 列:: + +``` +これで、受信リクエストのバリデーションが完了するはずです。 +``` + +.. 列:: + +```` +``` +$ curl localhost:8000/person -d '{"name": "Alice", "age": 21}' -X POST +{"name":"Alice","age":21} +``` +```` + +### Attrsによる検証 + +Attrsも使用できます。 + +.. 列:: + +``` +まず、モデルを定義します。 +``` + +.. 列:: + +```` +```python +@attrs.define +class Person: + name: str + age: int + +``` +```` + +.. 列:: + +``` +次に、ルートに添付してください +``` + +.. 列:: + +```` +```python +from sanic_ext import validate + +@app.post("/person") +@validate(json=Person) +async def handler(request, body: Person): + return json(attrs.asdict(body)) +``` +```` + +.. 列:: + +``` +これで、受信リクエストのバリデーションが完了するはずです。 +``` + +.. 列:: + +```` +``` +$ curl localhost:8000/person -d '{"name": "Alice", "age": 21}' -X POST +{"name":"Alice","age":21} +``` +```` + +## 何を検証できますか? + +`validate` デコレータは、3つの場所から受信したユーザーデータを検証するために使用できます: JSON body data (`request. son`), form body data (`request.form`), and query parameters (`request.args`). + +.. 列:: + +``` +予想通り、デコレータのキーワード引数を使用してモデルをアタッチすることができます。 +``` + +.. 列:: + +```` +```python +@validate( + json=ModelA, + query=ModelB, + form=ModelC, +) +``` +```` diff --git a/guide/content/ja/plugins/sanic-testing/clients.md b/guide/content/ja/plugins/sanic-testing/clients.md new file mode 100644 index 0000000000..f81a88e58e --- /dev/null +++ b/guide/content/ja/plugins/sanic-testing/clients.md @@ -0,0 +1,152 @@ +--- +title: サニックテスト - テストクライアント +--- + +# クライアントのテスト + +3つの異なるテストクライアントが用意されており、それぞれが異なる機能を提供します。 + +## 定期的な同期クライアント: `SanicTestClient` + +`SanicTestClient` はローカルネットワーク上で実際のバージョンの Sanic Server を実行し、テストを実行します。 エンドポイントを呼び出すたびに、アプリケーションのバージョンを起動し、ホスト OS 上のソケットにバインドします。 次に、そのアプリケーションへ直接呼び出しを行うために `httpx` を使用します。 + +これは、Sanicアプリケーションがテストされる典型的な方法です。 + +.. 列:: + +``` +Sanic Testing をインストールすると、通常の `SanicTestClient` をセットアップせずに使用できます。 これは、サニックがフードの下であなたのために脚を動作させるからです。 +``` + +.. 列:: + +```` +```python +app.test_client.get("/path/to/endpoint") +``` +```` + +.. 列:: + +``` +しかし、クライアントを自分でインスタンス化することが望ましいと思うかもしれません。 +``` + +.. 列:: + +```` +```python +from sanic_testing.testing import SanicTestClient + +test_client = SanicTestClient(app) +test_client.get("/path/to/endpoint") +``` +```` + +.. 列:: + +``` +テストクライアントを開始するための3つ目のオプションは、`TestManager` を使用することです。 これは、`SanicTestClient` と `SanicASGITestClient` の両方を設定する便利なオブジェクトです。 +``` + +.. 列:: + +```` +```python +from sanic_testing import TestManager + +mgr = TestManager(app) +app.test_client.get("/path/to/endpoint") +# or +mgr.test_client.get("/path/to/endpoint") +``` +```` + +以下のいずれかの方法でリクエストを行うことができます。 + +- `SanicTestClient.get` +- `SanicTestClient.post` +- `SanicTestClient.put` +- `SanicTestClient.patch` +- `SanicTestClient.delete` +- `SanicTestClient.options` +- `SanicTestClient.head` +- `SanicTestClient.websocket` +- `SanicTestClient.request` + +これらのメソッドは `httpx` を使用するときとほぼ同じように使うことができます。 `httpx`に渡す引数は、**ひとつの注意を払って**以下のように受け入れられます: `test_clientを使用している場合。 equest`とHTTPメソッドを手動で指定したい場合は、`http_method`を使用してください。 + +```python +test_client.request("/path/to/endpoint", http_method="get") +``` + +## ASGI async client: `SanicASGITestClient` + +リクエストごとにサーバーをスピンアップする `SanicTestClient` とは異なり、`SanicASGITestClient` はありません。 代わりに、`httpx`ライブラリを使用して、SanicをASGIアプリケーションとして実行し、ルートハンドラにアクセスして実行します。 + +.. 列:: + +``` +このテストクライアントは全ての同じメソッドを提供し、一般的には `SanicTestClient` として動作します。 唯一の違いは、呼び出しごとに`await`を追加する必要があることです。 +``` + +.. 列:: + +```` +```python +await app.test_client.get("/path/to/endpoint") +``` +```` + +`SanicASGITestClient` は `SanicTestClient` と全く同じ3つの方法で使用できます。 + +.. note:: + +``` +`SanicASGITestClient` は ASGI アプリケーションでのみ使用する必要はありません。 `SanicTestClient` は同期エンドポイントのみをテストする必要がないのと同じ方法です。どちらのクライアントも、*任意*のSanicアプリケーションをテストすることができます。 +``` + +## 永続的なサービスクライアント: `ReusableClient` + +このクライアントは `SanicTestClient` と同様の前提で動作し、アプリケーションのインスタンスを立ち上げ、実際の HTTP リクエストを行います。 しかし、`SanicTestClient` とは異なり、`ReusableClient` を使用する場合は、アプリケーションのライフサイクルを制御します。 + +つまり、リクエストごとに**新しいWebサーバーを起動しません**。 代わりに、サーバーを起動し、必要に応じて停止し、同じ実行中のインスタンスに複数のリクエストを行うことができます。 + +.. 列:: + +``` +他の2つのクライアントとは異なり、このクライアントを**インスタンス化**して使用する必要があります: +``` + +.. 列:: + +```` +```python +from sanic_testing.reusable import ReusableClient + +client = ReusableClient(app) +``` +```` + +.. 列:: + +``` +作成されると、コンテキストマネージャーの内部のクライアントを使用します。マネージャーの範囲外の場合、サーバーはシャットダウンします。 +``` + +.. 列:: + +```` +```python +from sanic_testing.reusable import ReusableClient + +def test_multiple_endpoints_on_same_server(app): + client = ReusableClient(app) + with client: + _, response = client.get("/path/to/1") + assert response.status == 200 + + _, response = client.get("/path/to/2") + assert response.status == 200 +``` +```` diff --git a/guide/content/ja/plugins/sanic-testing/getting-started.md b/guide/content/ja/plugins/sanic-testing/getting-started.md new file mode 100644 index 0000000000..d305e9b1b1 --- /dev/null +++ b/guide/content/ja/plugins/sanic-testing/getting-started.md @@ -0,0 +1,85 @@ +--- +title: サニックテスト - はじめに +--- + +# はじめに + +Sanic Testingは、Sanicの_公式_テストクライアントです。 その主な使用は、Sanicプロジェクト自体のテストに電力を供給することです。 ただし、APIテストをすばやく実行するための使いやすいクライアントとしても意味されます。 + +## 最低要件 + +- **Python**: 3.7+ +- **Sanic**: 21.3+ + +21.3より古いSanicのバージョンは、このモジュールをSanic自体に`sanic.testing`として統合しています。 + +## インストール + +PyPI からテストをインストールすることができます。 + +``` +pip install sanic-testing +``` + +## 基本的な使用法 + +`sanic-testing`パッケージが環境の中にある限り、使い始める必要はありません。 + +### 同期テストを書く + +テストクライアントを使用するには、アプリケーションインスタンスの `test_client` プロパティにアクセスするだけです。 + +```python +import pytest +from sanic import Sanic, response + +@pytest.fixture +def app(): + sanic_app = Sanic("TestSanic") + + @sanic_app.get("/") + def basic(request): + return response.text("foo") + + return sanic_app + +def test_basic_test_client(app): + request, response = app.test_client.get("/") + + assert request.method.lower() == "get" + assert response.body == b"foo" + assert response.status == 200 +``` + +### 非同期テストを書く + +`pytest`でasyncテストクライアントを使用するには、`pytest-asyncio`プラグインをインストールしてください。 + +``` +pip install pytest-asyncio +``` + +非同期テストを作成し、ASGI クライアントを使用できます。 + +```python +import pytest +from sanic import Sanic, response + +@pytest.fixture +def app(): + sanic_app = Sanic(__name__) + + @sanic_app.get("/") + def basic(request): + return response.text("foo") + + return sanic_app + +@pytest.mark.asyncio +async def test_basic_asgi_client(app): + request, response = await app.asgi_client.get("/") + + assert request.method.lower() == "get" + assert response.body == b"foo" + assert response.status == 200 +``` diff --git a/guide/content/ja/release-notes/2021/v21.12.md b/guide/content/ja/release-notes/2021/v21.12.md new file mode 100644 index 0000000000..17bb2ee99a --- /dev/null +++ b/guide/content/ja/release-notes/2021/v21.12.md @@ -0,0 +1,531 @@ +--- +title: バージョン 21.12 (LTS) +--- + +# バージョン 21.12 (LTS) + +.. TOC:: + +## はじめに + +これはバージョン21のformat@@0(../../org/polices.md#release-schedule)の最終リリースです。 バージョン21は現在、長期サポートに入り、2023年12月までの2年間サポートされます。 + +## 知っておくべきこと + +詳細は [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html) をご覧ください。 注目すべき新機能や破損した機能、そしてアップグレードする機能... + +### アプリケーション名とブループリント名 + +[v21.6](./v21.6.md#stricter-application-and-blueprint-names-deprecation) では、新しい制約のセットに準拠するためにアプリケーションと設計図名が必要でした。 この変更は現在起動時に適用されています。 + +名前**必須**: + +1. 英数字のみを使用 (`a-zA-Z0-9`) +2. ハイフン(`-`)またはアンダースコア(`_`)を含めることができます +3. 文字またはアンダースコアで始まる必要があります (`a-zA-Z_`) + +### アプリケーションとブループリントの厳密なプロパティ + +`Sanic`または`Blueprint`オブジェクトのプロパティを直接設定することを許可する古い寛大さは廃止されました。 `ctx` オブジェクトを使用してください。 + +```python +app = Sanic("MyApp") +app.ctx.db = Database() +``` + +### 削除 + +次の非推奨機能は存在しません。 + +- `sanic.exceptions.abort` +- `sanic.views.CompositionView` +- `sanic.response.StreamingHTTPResponse` + +### ストリーミング応答をアップグレードする (まだない場合) + +`sanic.response.stream` レスポンスメソッドは **廃止予定** で、v22.6 で削除されます。 古い学校のストリーミング応答を使用している場合は、アップグレードしてください。 + +**OLD - 非推奨** + +```python +async def sample_streaming_fn(response): + await response.write("foo,") + await response.write("bar") + +@app.route("/") +async def test(request: Request): + return stream(sample_streaming_fn, content_type="csv") +``` + +**現在** + +```python +async def sample_streaming_fn(response): + await response.write("foo,") + await response.write("bar") + +@app.route("/") +async def test(request: Request): + response = await request.respond(content_type="text/csv") + await response.send("foo,") + await response.send("bar") +``` + +### CLIのオーバーホールとMOTD (メッセージ・オブ・ザ・デイ) + +Sanic CLIはかなり広範なアップグレードを受けています。 `app.run()`と同等の新機能を追加します。 また、新しいMOTD ディスプレイが含まれており、実行環境に関する迅速かつ一目のハイライトを提供します。 MOTD は TTY を認識しているため、サーバログでは冗長性が低くなります。 主にアプリケーション開発の利便性を目的としています。 + +``` +$ sanic --help +usage: sanic [-h] [--version] [--factory] [-s] [-H HOST] [-p PORT] [-u UNIX] [--cert CERT] [--key KEY] [--tls DIR] [--tls-strict-host] + [-w WORKERS | --fast] [--access-logs | --no-access-logs] [--debug] [-d] [-r] [-R PATH] [--motd | --no-motd] [-v] + [--noisy-exceptions | --no-noisy-exceptions] + module + + ▄███ █████ ██ ▄█▄ ██ █ █ ▄██████████ + ██ █ █ █ ██ █ █ ██ + ▀███████ ███▄ ▀ █ █ ██ ▄ █ ██ + ██ █████████ █ ██ █ █ ▄▄ + ████ ████████▀ █ █ █ ██ █ ▀██ ███████ + + To start running a Sanic application, provide a path to the module, where + app is a Sanic() instance: + + $ sanic path.to.server:app + + Or, a path to a callable that returns a Sanic() instance: + + $ sanic path.to.factory:create_app --factory + + Or, a path to a directory to run as a simple HTTP server: + + $ sanic ./path/to/static --simple + +Required +======== + Positional: + module Path to your Sanic app. Example: path.to.server:app + If running a Simple Server, path to directory to serve. Example: ./ + +Optional +======== + General: + -h, --help show this help message and exit + --version show program's version number and exit + + Application: + --factory Treat app as an application factory, i.e. a () -> callable + -s, --simple Run Sanic as a Simple Server, and serve the contents of a directory + (module arg should be a path) + + Socket binding: + -H HOST, --host HOST Host address [default 127.0.0.1] + -p PORT, --port PORT Port to serve on [default 8000] + -u UNIX, --unix UNIX location of unix socket + + TLS certificate: + --cert CERT Location of fullchain.pem, bundle.crt or equivalent + --key KEY Location of privkey.pem or equivalent .key file + --tls DIR TLS certificate folder with fullchain.pem and privkey.pem + May be specified multiple times to choose multiple certificates + --tls-strict-host Only allow clients that send an SNI matching server certs + + Worker: + -w WORKERS, --workers WORKERS Number of worker processes [default 1] + --fast Set the number of workers to max allowed + --access-logs Display access logs + --no-access-logs No display access logs + + Development: + --debug Run the server in debug mode + -d, --dev Currently is an alias for --debug. But starting in v22.3, + --debug will no longer automatically trigger auto_restart. + However, --dev will continue, effectively making it the + same as debug + auto_reload. + -r, --reload, --auto-reload Watch source directory for file changes and reload on changes + -R PATH, --reload-dir PATH Extra directories to watch and reload on changes + + Output: + --motd Show the startup display + --no-motd No show the startup display + -v, --verbosity Control logging noise, eg. -vv or --verbosity=2 [default 0] + --noisy-exceptions Output stack traces for all exceptions + --no-noisy-exceptions No output stack traces for all exceptions +``` + +### サーバーの動作モードと変更は `debug` にあります + +`DEV`と`PRODUCTION`の2つの実行モードがあります。 デフォルトでは、Sanic サーバーは `PRODUCTION` モードで実行されます。 これは展開を目的としています。 + +現在、`DEV`モードは、古いSanicバージョンでは`debug=True`がどのように動作するかと非常によく似ています。 ただし、v22.3では。 `debug=True` は自動リロードを**もう有効にしません** 。 デバッグと自動再ロードを行いたい場合は、`DEV`モードを有効にしてください。 + +**開発** + +``` +$ sanic server:app --dev +``` + +```python +app.run(debug=True, auto_reload=True) +``` + +**製品** + +``` +$ sanic server:app +``` + +```python +app.run() +``` + +v22.3 以降、`PRODUCTION` モードはデフォルトでアクセスログを有効にしなくなりました。 + +変更の概要は以下のとおりです。 + +| フラグ | モード | トレースバック | ログ | アクセスログ | Reload | 最大ワーカー数 | +| ------- | ----- | ------- | ------ | ------ | ------ | ------- | +| --debug | DEBUG | はい | DEBUG | はい | ^1 | | +| | PROD | いいえ | INFO^2 | ^3 | | | +| --dev | DEBUG | はい | DEBUG | はい | はい | | +| --fast | | | | | | はい | + +- ^1 `--debug` で自動リロードと削除を非推奨にする 22.3 +- ^2 22.3の後、これはWARNINGに移動します +- ^3 後に 22.3: いいえ + +### 許可された最大ワーカー数 + +`--fast` を使用すると、許容されるワーカーの最大数を簡単に呼び出すことができます。 + +``` +$ sanic server:app --fast +``` + +```python +app.run(fast=True) +``` + +### ファーストクラスのサニックエクステンションのサポート + +[Sanic Extensions](../../plugins/sanic-ext/getting-started.md) は、API開発者向けに特化した多数の追加機能を提供します。 パッケージが環境内にある限り、追加のセットアップなしで提供するすべての機能を簡単に実装できるようになりました。 これらの機能は次のとおりです。 + +- `HEAD`、`OPTIONS`、および `TRACE`エンドポイントを自動的に作成します +- CORS保護 +- 事前定義されたエンドポイント固有のレスポンスシリアライザー +- ルートハンドラへの依存性インジェクション +- やり直しや Swagger を使用した OpenAPI ドキュメント +- クエリクエストの引数と本文入力のバリデーションをリクエスト + +推奨される方法は、Sanic と一緒にインストールすることですが、独自にパッケージをインストールすることもできます。 + +.. 列:: + +```` +``` +$ pip install sanic[ext] +``` +```` + +.. 列:: + +```` +``` +$ pip install sanic sanic-ext +``` +```` + +その後、追加の設定は必要ありません。 Sanic Extensionsはアプリケーションに接続され、**これ以上の設定はありません**ですべての機能を提供します。 + +動作を変更したり、追加の設定をしたい場合は、 `app.extend` を使ってSanic拡張機能を変更することができます。 以下の例は等価です。 `Config` オブジェクトは、IDE開発に役立つ型注釈を提供することです。 + +.. 列:: + +```` +```python +# This is optional, not required +app = Sanic("MyApp") +app.extend(config={"oas_url_prefix": "/apidocs"}) +``` +```` + +.. 列:: + +```` +```python +# This is optional, not required +app = Sanic("MyApp") +app.config.OAS_URL_PREFIX = "/apidocs" +``` +```` + +.. 列:: + +```` +```python +# This is optional, not required +from sanic_ext import Config + +app = Sanic("MyApp") +app.extend(config=Config(oas_url_prefix="/apidocs")) +``` +```` + +.. 列:: + +### 文脈依存の例外 + +[v21.9](./v21.9.md#default-exception-messages) では、アプリケーション全体で例外を一貫して発生させる機能を簡素化する例外にデフォルトのメッセージを追加しました。 + +```python +class TeapotError(SanicException): + status_code = 418 + message = "Sorry, I cannot brew coffee" + +raise TeapotError +``` + +しかし、これには二つのことが欠けていました。 + +1. 動的で予測可能なメッセージ形式 +2. エラーメッセージにコンテキストを追加する機能 (詳細は後で) + +現在のリリースでは、Sanic の例外はエラーメッセージを書くときにコンテキストを提供するための追加情報を持つことができます。 + +```python +class TeapotError(SanicException): + status_code = 418 + + @property + def message(self): + return f"Sorry {self.extra['name']}, I cannot make you coffee" + +raise TeapotError(extra={"name": "Adam"}) +``` + +新機能では、例外インスタンスに `extra` メタを渡すことができます。 この`extra` infoオブジェクトは `PRODUCTION` モードでは抑制されますが、 `DEVELOPMENT` モードでは表示されます。 + +.. 列:: + +``` +**製品** + +![image](https://user-images.githubusercontent.com/166269/139014161-cda67cd1-843f-4ad2-9fa1-acb94a59fc4d.png) +``` + +.. 列:: + +``` +**開発版** + +![image](https://user-images.githubusercontent.com/166269/139014121-0596b084-b3c5-4adb-994e-31ba6eba6dad.png) +``` + +上からアイテム2に戻る: _エラーメッセージにコンテキストを追加する機能_ + +これは、エラーメッセージをJSON形式で渡すことを意図しているマイクロサービスやAPIを作成する場合に特に便利です。 このユースケースでは、パース可能なエラーメッセージ以外にも、クライアントに詳細を返すコンテキストを持たせたいと考えています。 + +```python +raise TeapotError(context={"foo": "bar"}) +``` + +この情報は **私たちが常にエラーで渡したい** です(利用可能な場合)。 次のようにすべきです: + +.. 列:: + +```` +**PRODUCTION** + +```json +{ + "description": "I'm a teapot", + "status": 418, + "message": "Sorry Adam, I cannot make you coffee", + "context": { + "foo": "bar" + } +} +``` +```` + +.. 列:: + +```` +**DEVELOPMENT** + +```json +{ + "description": "I'm a teapot", + "status": 418, + "message": "Sorry Adam, I cannot make you coffee", + "context": { + "foo": "bar" + }, + "extra": { + "name": "Adam", + "more": "lines", + "complex": { + "one": "two" + } + }, + "path": "/", + "args": {}, + "exceptions": [ + { + "type": "TeapotError", + "exception": "Sorry Adam, I cannot make you coffee", + "frames": [ + { + "file": "handle_request", + "line": 83, + "name": "handle_request", + "src": "" + }, + { + "file": "/tmp/p.py", + "line": 17, + "name": "handler", + "src": "raise TeapotError(" + } + ] + } + ] +} +``` +```` + +### バックグラウンドタスク管理 + +`app を使用する場合。 バックグラウンドタスクを作成するためのdd_task` メソッド これで、オプションの `name` キーワード引数を渡すことができ、取得またはキャンセルすることができます。 + +```python +app.add_task(dummy, name="dummy_task") +task = app.get_task("dummy_task") + +app.cancel_task("dummy_task") +``` + +### 定義内のコンテキストのkwargsをルーティングします + +route (ルート)が定義された場合、任意の数のキーワード引数を `ctx_` プレフィックスで追加できます。 これらの値はルート `ctx` オブジェクトに注入されます。 + +```python +@app.get("/1", ctx_label="something") +async def handler1(request): + ... + +@app.get("/2", ctx_label="something") +async def handler2(request): + ... + +@app.get("/99") +async def handler99(request): + ... + +@app.on_request +async def do_something(request): + if request.route.ctx.label == "something": + ... +``` + +### ブループリントはいつでも登録できます + +Sanicの以前のバージョンでは、アプリケーションにブループリントを付けることができるときに厳しい順序がありました。 `app.blueprint(bp)` を全てのオブジェクトをブループリントのインスタンスに追加する前に\*実行した場合、それらは見逃されます。 + +今では、いつでもブループリントを添付することができ、それに添付されているすべてが起動時に含まれます。 + +### ノイズ例外(ログにすべての例外を強制) + +新しい `NOISY_EXCEPTIONS` 設定値があります。 `False`(デフォルト)の場合、Sanicは任意の`SanicException`の`quiet`プロパティを尊重します。 つまり、 `quiet=True` を持つ例外はログ出力に表示されません。 + +しかし、`NOISY_EXCEPTIONS=True`を設定した場合、`quiet`値に関係なくすべての例外が記録されます。 + +これはデバッグ時に役立ちます。 + +```python +app.config.NOISY_EXCEPTIONS = True +``` + +### シグナルイベント を `Enum` として表示 + +便宜上、すべての信号値が組み込まれている `Enum` があります。 + +```python +from sanic.signals import Event + +@app.signal(Event.HTTP_LIFECYCLE_BEGIN) +async def connection_opened(conn_info): +... +``` + +### 環境変数のカスタム型キャスト + +デフォルトでは、Sanicは環境変数を`config`インスタンスに適用するときに`int`、`float`、または`bool`値を変換します。 独自のコンバーターで拡張できます: + +```python +app = Sanic(..., config=Config(converters=[UUID])) +``` + +### 設定値により `uvloop` を無効にする + +`uvloop` の使用は、設定値で制御できます。 + +```python +app.config.USE_UVLOOP = False +``` + +### 複数の TLS 証明書でSanic サーバーを実行する + +Sanic は、複数の TLS 証明書で実行できます。 + +```python +app.run( + ssl=[ + "/etc/letsencrypt/live/example.com/", + "/etc/letsencrypt/live/mysite.example/", + ] +) +``` + +## ニュース + +### 近日公開予定:Sanicを使ったPythonウェブ開発 + +Sanicについての本は、コア開発者の一人[@ahopkins](https://github.com/ahopkins)によってまもなく公開されます。 + +詳細は [sanicbook.com](https://sanicbook.com) でご覧ください。 + +> Webアプリケーションのパフォーマンスとスケーラビリティを向上させるために、Sanicとの作業の実用的な知識を備えてください。 それを実行しながら。 変化するビジネスニーズに応えるためにアプリケーションをカスタマイズすることを学ぶにつれて、開発スキルをレベルアップします。アプリを大幅にオーバーエンジニアリングすることなく。 + +本の収益の一部はSanic Community Organizationに入って、Sanicの開発と運営に資金を提供します。 だから、本を購入することはあなたがサニックをサポートすることができる別の方法です。 + +### ドキュメントのダークモード + +まだ気づいていない場合は、このSanicウェブサイトをネイティブダークモードで利用できるようになりました。 ページ右上のテーマを切り替えることができます。 + +## ありがとうございます + +このリリースに参加いただき、ありがとうございます: :clap: + +[@adarsharegmi](https://github.com/adarsharegmi) +[@ahopkins](https://github.com/ahopkins) +[@ashleysommer](https://github.com/ashleysommer) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@cnicodeme](https://github.com/cnicodeme) +[@kianmeng](https://github.com/kianmeng) +[@meysam81](https://github.com/meysam81) +[@nuxion](https://github.com/nuxion) +[@prryplatypus](https://github.com/prryplatypus) +[@realDragonium](https://github.com/realDragonium) +[@SaidBySolo](https://github.com/SaidBySolo) +[@sjsadowski](https://github.com/sjsadowski) +[@Tronic](https://github.com/tronic) +[@Varriount](https://github.com/Varriount) +[@vltr](https://github.com/vltr) +[@whos4n3](https://github.com/whos4n3) + +そして、[@miss85246](https://github.com/miss85246)と[@ZinkLu](https://github.com/ZinkLu)に感謝します。 + +--- + +あなたがプロジェクトを楽しんでいるなら、貢献を検討してください。 もちろん私たちはコード貢献は大好きですが、どんな形でも貢献できます。 ドキュメントを書いたり、ユースケースを示したり、会話に参加したり、声を知らせたりすることを検討してください。format@@0(https://opencollective.com/sanic-org/) diff --git a/guide/content/ja/release-notes/2021/v21.3.md b/guide/content/ja/release-notes/2021/v21.3.md new file mode 100644 index 0000000000..77cb2353b8 --- /dev/null +++ b/guide/content/ja/release-notes/2021/v21.3.md @@ -0,0 +1,272 @@ +--- +title: バージョン 21.3 +--- + +# バージョン 21.3 + +.. TOC:: + +## はじめに + +サニックは今より速くなりました。 + +まあ、それはすでに速かったです。 しかし、v21リリースの最初の反復で、いくつかの具体的な改善を行った主要なマイルストーンをいくつか取り入れました。 これらは、長年の作品にされてきたいくつかのアイデアを包含し、最終的にリリースされたバージョンにそれを作りました。 + +.. warning:: Breaking changes + +```` +Version 21.3 introduces a lot of new features. But, it also includes some breaking changes. This is why these changes were introduced after the last LTS. If you rely upon something that has been removed, you should continue to use v20.12LTS until you are able to upgrade. + +```bash +pip install "sanic>=20.12,<20.13" +pip freeze > requirements.txt +``` + +For most typical installations, you should be able to upgrade without a problem. +```` + +## 知っておくべきこと + +注目すべき新機能や破損した機能、そしてアップグレードする機能... + +### Python 3.7+ のみ + +このバージョンは Python 3.6 のサポートを削除します。 バージョン 20.12LTS は Python 3.6 を 2022 年12 月、バージョン 19 までサポートします。 2LTSは2021年12月にEOLまでサポートします。 + +format@@0(../project/policy.md#long-term-support-v-interim-releases) についてもっと読んでください。 + +### ファーストクラスの市民としてストリーミング中 + +最大の速度向上は、リクエスト/レスポンスサイクルを単一のフローに統合することでした。 以前は、定期的なサイクルとストリーミングサイクルに違いがありました。 APIは互換性のために現在同じままでいるにもかかわらず、これは実際には簡素化されています。 利点は、 **すべての** のリクエストに新しい利点が表示されるようになりました。 + +format@@0(../advanced/streaming.md#response-streaming) についてもっと読んでください。 + +### ルーターのオーバーホール + +古いSanicルータは正規表現に基づいていました。 また、実行時に変更が困難になった癖も多数あり、パフォーマンス上の問題も生じた。 この変更は長年にわたって行われており、format@@0(https://community.sanicframework.org/t/a-fast-new-router/649/41) でルーターをコンパイルしました。 年間を通して追加の改善を探してください。 + +外向きのAPIは後方互換性を保っています。 ただし、ルーター内の何かにアクセスしている場合は、いくつかの変更に気づくことがあります。 例: + +1. `Router.get()`に新しい戻り値があります +2. `Route`は適切なクラスオブジェクトで、`namedtuple`ではありません。 +3. ルーターを手動で構築する場合は、使用する前に `Router.finalize()` を呼び出す必要があります。 +4. ルートにマッチする新しい``パターンがあります +5. 少なくとも 1 つのルートが定義されていないアプリケーションを起動することはできません。 + +ルーターは以下のリポジトリにあります: [sanic-org/sanic-router](https://github.com/sanic-org/sanic-router)。format@@0(https://pypi.org/project/sanic-routing/) + +### Signals API ⭐️ + +_BETA 機能: v21.6_ で確定する API + +新しいルータの利点として、format@@0(https://github.com/sanic-org/sanic/issues/1630)にも電力を供給できることが挙げられます。 この機能は現在一般向けにリリースされており、パブリックAPIは最終的な形で変更されない可能性があります。 + +この機能の中核的なアイデアは次のとおりです。 + +1. サーバーへのプラグインやライフサイクルのリクエストを行うことができます +2. アプリケーションを通じてメッセージを同期および送信する新しいツールを提供します。 +3. 最終的にはさらにパフォーマンスを向上させることができます + +APIは3つの新しいメソッドを導入しました。 + +- `@app.signal(...)` - シグナルハンドラを定義するため。 見た目とルートのように動作します。 そのシグナルがディスパッチされるたびに、このハンドラが実行されます。 +- `app.event(...)` - イベントがトリガーされるまで実行を一時停止するアプリケーションのどこでも使用できる待ち時間。 +- `app.dispatch(...)` - イベントをトリガーしてシグナルハンドラを実行します。 + +```python +@app.signal("foo.bar.") +async def signal_handler(thing, **kwargs): + print(f"[signal_handler] {thing=}", kwargs) + +async def wait_for_event(app): + while True: + print("> waiting") + await app.event("foo.bar.*") + print("> event found\n") + +@app.after_server_start +async def after_server_start(app, loop): + app.add_task(wait_for_event(app)) + +@app.get("/") +async def trigger(request): + await app.dispatch("foo.bar.baz") + return response.text("Done.") +``` + +### ルート名 + +以前は `route.name` と `route.endpoint` の両方で参照されていたルート。 似ていますが、少し違っていました。 これで、すべてのルートは**一貫して** 名前空間と参照されます。 + +```text +.[optional:] +``` + +この新しい "name" はプロパティ `route.name` に割り当てられています。 `route.endpoint` は非推奨となっており、v21.9 でそのプロパティを削除します。 それまでは、`route.name` のエイリアスになります。 + +また、静的、websocket、blueprint ルートなどで使用されていたプレフィックスを削除しました。 + +### 新しいデコレーター + +autocompleteでIDEを支援するいくつかの新しいコンビニエンスデコレータ。 + +```python +# @app.listener("...") +@app.before_server_start +@app.after_server_start +@app.before_server_start +@app.after_server_stop + +# @app.middleware("...") +@app.on_request +@app.on_response +``` + +### Unquote in route + +ASCII以外の文字を使用するルートがある場合、Sanicはテキストを`unquote`ではなくなります。 ルート定義を具体的に伝える必要があります。 + +```python +@app.route("/overload/", methods=["GET"], unquote=True) +async def handler2(request, param): + return text("OK2 " + param) + +request, response = app.test_client.get("/overload/您好") +assert response.text == "OK2 您好" +``` + +忘れた場合、テキストはエンコードされたままになります。 + +### `Request.match_info`を変更します + +`match_info` は常にマッチしたパスパラメータのデータを提供しています。 これで、ミドルウェアなどで変更できるようになりました。 + +```python +@app.on_request +def convert_to_snake_case(request): + request.match_info = to_snake(request.match_info) +``` + +### バージョンの種類がルートにあります + +ルート内の `version` 引数は以下のようになります。 + +- `str` +- `int` +- `float` + +```python +@app.route("/foo", version="2.1.1") +@app.route("/foo", version=2) +@app.route("/foo", version=2.1) +``` + +### ボディで安全な取り扱い方法 + +`GET`、`HEAD`、`OPTIONS`および`DELETE`のルートハンドラは、渡されたHTTPボディをデコードしません。 上書きすることができます: + +```python +@app.delete(..., ignore_body=False) +``` + +### アプリケーション、ブループリント、ブループリントグループパリティ + +`Sanic`と`Blueprint`クラスは共通のベースを持っています。 以前は、多くの機能を複製しており、それによって実装が若干異なりました。 両方とも同じベースクラスを継承するようになったので、開発者とプラグインにはより一貫性のある API が必要になりました。 + +また、ブループリントグループは `version` や `strict_slashes` キーワード引数のような一般的な URL 拡張もサポートするようになりました。 + +### 依存関係から `httpx` をドロップしました + +`httpx` に依存関係はありません。 + +### `testing`ライブラリを削除しました + +Sanic 社内テスト クライアントは削除されました。 これは [sanic-org/sanic-testing](https://github.com/sanic-org/sanic-testing) と、それ自身のformat@@0(https://pypi.org/project/sanic-testing/)にあります。 + +`sanic-testing`がインストールされている場合は、以前のように`Sanic()`アプリケーション・インスタンスで利用可能になります。 ですから、**のみ**変更する必要があるのは、テストスイートの要件に`sanic-testing`を追加することです。 + +### アプリケーションと接続レベルのコンテキスト (`ctx`) オブジェクト + +Version 19.9 format@@0(https://github.com/sanic-org/sanic/pull/1666/files) the `request.ctx` API この便利な構文により、リクエストオブジェクトにプロパティやデータを簡単にアタッチすることができます(例えば、 ミドルウェアで)、他の場所でアプリケーション情報を再利用します。 + +同様に、この概念は2つの場所で拡張されています: + +1. アプリケーション・インスタンスと +2. 交通機関の接続だ + +#### アプリケーションのコンテキスト + +一般的な使用例は、アプリケーションインスタンスにプロパティをアタッチすることです。 整合性のために、Sanic プロパティとの名前の衝突を避けるために、`ctx` オブジェクトは `Sanic` インスタンス上に存在します。 + +```python +@app.before_server_startup +async def startup_db(app, _): + # WRONG + app.db = await connect_to_db() + + # CORRECT + app.ctx.db = await connect_to_db() +``` + +#### 接続の内容 + +クライアントがキープライブヘッダーを送信すると、Sanicはトランスポートソケットをformat@@0(../deployment/configuration.md#keep-alive-timeout) に保持しようとします。 そのトランスポートオブジェクトには`ctx`オブジェクトが用意されています。 これは、(トランスポート層が再利用されている)単一のクライアントからの複数のリクエストが共有されることを意味します。 + +```python +@app.on_request +async def increment_foo(request): + if not hasattr(request.conn_info.ctx, "foo"): + request.conn_info.ctx.foo = 0 + request.conn_info.ctx.foo += 1 + +@app.get("/") +async def count_foo(request): + return text(f"request.conn_info.ctx.foo={request.conn_info.ctx.foo}") +``` + +```bash +$ curl localhost:8000 localhost:8000 +request.conn_info.ctx.foo=1 +request.conn_info.ctx.foo=2 +request.conn_info.ctx.foo=3 +``` + +.. 警告:: + +``` +接続レベルのコンテキストは実験的な機能であり、v21.6 で確定する必要があります。 +``` + +## ニュース + +### 新しいフロントページ 🎉 + +ドキュメントを二つに分割しました。 コードベース内の docstring は ReadTheDocs への sphinx ドキュメントのビルドを継続します。 ただし、API ドキュメントに限定されます。 新しいフロントページには、「サニック・ユーザー・ガイド」が含まれます。 + +新しいサイトはVuepressで動作します。 貢献は歓迎します。 ドキュメントの翻訳にもご協力いただきます。 + +その一環として、RTDドキュメントを刷新し、APIドキュメントのみに変更しました。 + +### チャットはDiscordに移動しました + +Gitterのチャットルームは段階的に廃止されることに一歩近づいた。 その場所でformat@@0(https://discord.gg/FARQzAEMAA)を開きました。 + +### Open Collective + +Sanic Community Organization は format@@0(https://opencollective.com/sanic-org) を開き、Sanic の開発を経済的にサポートしたい人を対象にしています。 + +### 2021 Release Manager + +@sjsadowskiと@yunstanfordに、2019年と2020年のリリースマネージャーを務めていただきありがとうございます。 今年のリリースマネージャーは @ahopkins と @vltr。 + +## ありがとうございます + +このリリースに参加いただき、ありがとうございます: :clap: + +[@ahopkins](https://github.com/ahopkins) [@akshgpt7](https://github.com/akshgpt7) [@artcg](https://github.com/ashleysommer)[@ashleysommer](https://github.com/ashleysommer) [@elis-k](https://github.com/elisamarayana)[@harshanararayana](https://github.com/harshanarayana)[@sadowski) [@sjsadowski](https://github.com/sjsadowski) + +To [@ConnorZhang](https://github.com/miss85246) and [@ZinkLu](https://github.com/ZinkLu) を中国語に翻訳する + +--- + +すべてのPRへのリンクなどを取得するには、チェンジログをチェックしてください。 diff --git a/guide/content/ja/release-notes/2021/v21.6.md b/guide/content/ja/release-notes/2021/v21.6.md new file mode 100644 index 0000000000..071fa30188 --- /dev/null +++ b/guide/content/ja/release-notes/2021/v21.6.md @@ -0,0 +1,352 @@ +--- +title: バージョン21.6 +--- + +# バージョン21.6 + +.. TOC:: + +## はじめに + +これはバージョン21のformat@@0(../project/polices.md#release-schedule)の2番目のリリースです。 12月の長期サポートバージョンでは、バージョン21が「確定」される前の9月にもう1つのリリースが予定されています。 ユーザーが気づいたことがありますが、21.3からルーターは独自のパッケージに移動されました: [`sanic-routing`](https://pypi.org/project/sanic-routing) この変化は、今のところ続く可能性が高い。 このリリース以降、最低限必要なバージョンは 0.7.0 です。 + +## 知っておくべきこと + +詳細は [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html) をご覧ください。 注目すべき新機能や破損した機能、そしてアップグレードする機能... + +### `StreamingHTTPResponse` の廃止予定 + +`StreamingHTTPResponse` の使用は非推奨となり、21.12 リリースで削除されます。 これは、 `Streaming.response.stream` と `sanic.response.file_stream` の両方に影響を与えます。 + +正確な移行パスはまだ決まっていませんが、`sanic.response.stream` と `sanic.response.file_stream` は、何らかの形でコンビニエンス演算子としてv21.12 に存在し続けます。 9月リリースまでに最終決定を迎えることを望んでいるので、この夏を通じて詳細をご覧ください。 + +### `CompositionView` の廃止予定 + +`CompositionView` の使用は非推奨となり、21.12 で削除されます。 + +### パスパラメータ型の非推奨: `string` と `number` + +先に進むには、`string` と `number` の代わりに、パスパラメータの型に `str` と `float` を使用します。 + +```python +@app.get("//") +async def handler(request, foo: str, bar: float): +... +``` + +既存の `string` と `number` 型はエイリアスされ、引き続き動作しますが、v21.12 で削除されます。 + +### バージョン0.7ルーターのアップグレード + +これには数多くのバグ修正が含まれており、v0.6 よりも広範囲のエッジケースをより優雅に処理します。 サポートされていないパターンがあれば、format@@0(https://github.com/sanic-org/sanic-routing/issues) を報告してください。 `sanic-routing` [release notes](https://github.com/sanic-org/sanic-routing/releases) で解決されたいくつかの問題を見ることができます。 + +### `eof()`でインラインストリーミング + +バージョン21.3にはformat@@0(https://sanic.dev/ja/guide/release-notes/v21.3.html#what-to-know)が含まれています。 導入されたパターンがデフォルトになります (下記参照)。 便宜上、新しい `response.eof()` メソッドが含まれています。 最終的なデータがクライアントにプッシュされると呼び出されます。 + +```python +@app.route("/") +async def test(request): + response = await request.respond(content_type="text/csv") + await response.send("foo,") + await response.send("bar") + await response.eof() + return response +``` + +### 新しいパスパラメータタイプ: `slug` + +次のように、適切な一致を持つ`スラグ`として動的パスセグメントを指定できます。 + +```python +@app.get("/articles/") +async def article(request, article_slug: str): +... +``` + +スラッグは小文字または数字で構成されている必要があります。 ハイフン(`-`)を含むことができますが、最初の文字にすることはできません。 + +``` +this-is-a-slug +with-123-is-also-a-slug +111-at-start-is-a-slug +NOT-a-slug +-NOT-a-slug +``` + +### より厳密なアプリケーションとブループリント名、および廃止予定 + +アプリケーションと `Blueprint` インスタンスは、より厳しい要件セットに準拠する必要があります。 + +1. 英数字のみで構成されます +2. ハイフン(`-`)またはアンダースコア(`_`)を含めることができます +3. 文字(大文字または小文字)で始まる必要があります + +命名規則は Python の変数命名規則に似ており、ハイフン(`-`)を追加しました。 + +より緩い標準は廃止されました。 21.12 以降、非準拠は起動時エラーとなります。 + +### `Route` オブジェクトへの新しいアクセス: `route.uri` + +v21.3 の `Route` オブジェクトには `uri` 属性がなくなりました。 代わりに、`route.path` がクローズされました。 しかし、`sanic-routing`の仕組みにより、`path`プロパティは先頭の`/`を_持っていません_。 これを修正しました。先頭のスラッシュを持つ `route.uri` があります。 + +```python +route.uri == f"/{route.path}" +``` + +### IPに影響を与える`Request`オブジェクトの新しいアクセサー + +受け取ったリクエストのIPアドレスにアクセスするために、Sanicはリクエストオブジェクト`request.ip`に利便性の高いアクセサーを持っています。 これは新しいものではなく、オープンな HTTP 接続の詳細を提供する `request.conn_info` から生成されます。 + +現在のバージョンは `conn_info` オブジェクトに新しい `client_ip` アクセサーを追加します。 IPv4では違いに気付かないでしょう。 しかし、IPv6アプリケーションの場合、新しいaccessorはアドレスの「未ラップ」バージョンを提供します。 次の例を考えてみましょう: + +```python +@app.get("/") +async def handler(request): + return json( + { + "request.ip": request.ip, + "request.conn_info.client": request.conn_info.client, + "request.conn_info.client_ip": request.conn_info.client_ip, + } + ) + +app.run(sock=my_ipv6_sock) +``` + +```bash +$ curl http://\[::1\]:8000 +{ + "request.ip": "::1", + "request.conn_info.client": "[::1]", + "request.conn_info.client_ip": "::1" +} + +``` + +### 代替の `Config` と `Sanic.ctx` オブジェクト + +Sanic アプリケーションに独自の設定とコンテキストオブジェクトを渡せるようになりました。 カスタム設定\*は、`sanic.config.Config` のサブクラスでなければなりません。 コンテキストオブジェクトは何でも自由に使用でき、制限は一切ありません。 + +```python +class CustomConfig(Config): + ... + +config = CustomConfig() +app = Sanic("custom", config=config) +assert isinstance(app.config, CustomConfig) +``` + +そして... + +```python +class CustomContext: + ... + +ctx = CustomContext() +app = Sanic("custom", ctx=ctx) +assert isinstance(app.ctx, CustomContext) +``` + +### Sanic CLI の改善 + +1. 既存の機能の新しいフラグ: `--auto-reload` +2. 既存の引数の新しい短縮形フラグ +3. 新機能: `--factory` +4. 新機能: `--simple` +5. 新機能: `--reload-dir` + +#### ファクトリーアプリケーション + +ファクトリパターンに従ったアプリケーション(`sanic を返す関数)。 anic`インスタンス)、`--factory`フラグを使用して、Sanic CLIからアプリケーションを起動できるようになりました。 + +```python +from sanic import Blueprint, Sanic, text + +bp = Blueprint(__file__) + +@bp.get("/") +async def handler(request): + return text("😎") + +def create_app() -> Sanic: + app = Sanic(__file__) + app.blueprint(bp) + return app +``` + +実行できるようになりました: + +```bash +$ sanic path.to:create_app --factory +``` + +#### Sanic Simple Server + +Sanic CLI には、ディレクトリを Web サーバーとして提供するシンプルなパターンが含まれています。 ディレクトリのルートで `index.html` を探します。 + +```bash +$ sanic ./path/to/dir --simple +``` + +.. 警告:: + +``` +この機能はまだ初期の *beta* モードです。スコープ内で変更される可能性があります。 +``` + +#### 追加のリロードディレクトリ + +`debug` または `auto-reload` を使用する場合、Sanicに新しいファイルを監視するディレクトリを追加することができます。 + +```bash +sanic ... --reload-dir=/path/to/foo -reload-dir=/path/to/bar +``` + +.. tip:: + +``` +アプリケーションディレクトリにこれを含める必要はありません。Sanicはアプリケーション内のPythonファイルが変更されると自動的にリロードされます。 静的ファイルが更新されたときにアプリケーションをリッスンして更新したいときは、 `reload-dir` 引数を使う必要があります。 +``` + +### バージョン接頭辞: + +`version` を追加すると、ルートには `/v ` がプレフィックス付きです。 これは常にパスの先頭にあります。 これは、新しいことではありません。 + +```python +# /v1/my/path +app.route("/my/path", version=1) +``` + +これでプレフィックスを変更することができます(したがって、バージョンより前にパスセグメントを追加することができます)。 + +```python +# /api/v1/my/path +app.route("/my/path", version=1, version_prefix="/api/v") +``` + +引数 `version_prefix` は以下のように定義できます: + +- `app.route` と `bp.route` デコレーター (そしてすべてのコンビニエンスデコレーターも) +- `Blueprint` インスタンス +- `Blueprint.group` コンストラクター +- `BlueprintGroup` インスタンス +- `app.blueprint` 登録 + +### シグナルイベントの自動登録 + +`config.EVENT_AUTOREGISTER` を `True` に設定すると、以前にシグナルハンドラで定義されていなくても、シグナルイベントを待つことができます。 + +```python +@app.signal("do.something.start") +async def signal_handler(): + await do_something() + await app.dispatch("do.something.complete") + +# あなたのアプリの他の何か: +await app.event("do.something.complete") +``` + +### 無限に再利用可能で安定した `ブループリント` と `ブループリントグループ` + +単一の `Blueprint` は割り当てられず、複数のグループに再利用できません。 グループ自体は無限に一つ以上のグループに入れ子にすることもできます。 これにより、無限の範囲のコンポジションが可能になります。 + +### HTTPメソッド + +Sanic には `sanic.HTTPMethod` があり、これは `Enum` です。 文字列と相互に使用できます: + +```python +from sanic import Sanic, HTTPMethod + +@app.route("/", methods=["post", "PUT", HTTPMethod.PATCH]) +async def handler(...): +... +``` + +### `HTTPMethodView` の拡張 + +クラスベースのビューは、次のいずれかの方法のいずれかで添付できます。 + +**Option 1 - Existing** + +```python +class DummyView(HTTPMethodView): + ... + +app.add_route(DummyView.as_view(), "/dummy") +``` + +**Option 2 - From `attach` method** + +```python +class DummyView(HTTPMethodView): + ... + +DummyView.attach(app, "/") +``` + +**Option 3 - From class definition at `__init_subclass__`** + +```python +class DummyView(HTTPMethodView, attach=app, uri="/"): +... +``` + +オプション2と3は、CBVが別のファイルにある場合に便利です: + +```python +from sanic import Sanic, HTTPMethodView + +class DummyView(HTTPMethodView, attach=Sanic.get_app(), uri="/"): +... +``` + +## ニュース + +### Discordとサポートフォーラム + +まだコミュニティに参加していない場合は、[Discord server](https://discord.gg/FARQzAEMAA)とformat@@0(https://community.sanicframework.org/)に参加してください。 また、Twitterで[@sanicframework](https://twitter.com/sanicframework)をフォローしてください。 + +### SCO2022の投票 + +Summer :dart_island:/Winter ❄️ (あなたの半球を選んでください) が私たちの前にあります。 つまり、私たちはSCOの選挙を行うことになります。 今年は以下のようなポジションを埋めることができます: + +- 運営審議会メンバー(2年間) +- 運営審議会メンバー(2年間) +- 運営評議会メンバー(1年) +- リリース マネージャー v22 +- リリース マネージャー v22 + +[@vltr](https://github.com/vltr)はステアリング評議会で2年目を迎えます。 + +詳細を知りたい方は、SCOformat@@0(../project/scope.md#roles-and-responsibities)、またはAdam Hopkins on Discordを読んでください。 + +推薦は9月1日から開始します。 詳細については、フォーラムでご覧いただけます。 + +### 新しいプロジェクトが進行中です + +SCO傘に新しいプロジェクトを追加しました: [`sanic-ext`](https://github.com/sanic-org/sanic-ext) それはまだリリースされておらず、積極的に開発されています。 プロジェクトのゴールは最終的に[`sanic-openapi`](https://github) を置き換えることです。 om/sanic-org/sanic-openapi) は、入力検証、CORSハンドリング、HTTP自動メソッドハンドラなど、ウェブアプリケーション開発者のためのより多くの機能を提供します。 あなたが助けに興味があるなら、Discordでお知らせください。 9月リリースの前に(うまくいけば)このプロジェクトの最初のリリースを探してください。 + +## ありがとうございます + +このリリースに参加いただき、ありがとうございます: :clap: + +[@aaugustin](https://github.com/aaugustin) +[@ahopkins](https://github.com/ahopkins) +[@ajaygupta2790](https://github.com/ajaygupta2790) +[@ashleysommer](https://github.com/ashleysommer) +[@ENT8R](https://github.com/ent8r) +[@fredlllll](https://github.com/fredlllll) +[@graingert](https://github.com/graingert) +[@harshanarayana](https://github.com/harshanarayana) +[@jdraymon](https://github.com/jdraymon) +[@Kyle-Verhoog](https://github.com/kyle-verhoog) +[@sanjeevanahilan](https://github.com/sanjeevanahilan) +[@sjsadowski](https://github.com/sjsadowski) +[@Tronic](https://github.com/tronic) +[@vltr](https://github.com/vltr) +[@ZinkLu](https://github.com/zinklu) + +--- + +あなたがプロジェクトを楽しんでいるなら、貢献を検討してください。 もちろん私たちはコード貢献は大好きですが、どんな形でも貢献できます。 ドキュメントを書いたり、ユースケースを示したり、会話に参加したり、声を知らせたりすることを検討してください。format@@0(https://opencollective.com/sanic-org/) diff --git a/guide/content/ja/release-notes/2021/v21.9.md b/guide/content/ja/release-notes/2021/v21.9.md new file mode 100644 index 0000000000..d84cf34020 --- /dev/null +++ b/guide/content/ja/release-notes/2021/v21.9.md @@ -0,0 +1,240 @@ +--- +title: バージョン21.9 +--- + +# バージョン21.9 + +.. TOC:: + +## はじめに + +これはバージョン21のformat@@0(../../org/policies.md#release-schedule)の3番目のリリースです。 バージョン21は、12月の長期サポートバージョンリリースで「確定」されます。 + +## 知っておくべきこと + +詳細は [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html) をご覧ください。 注目すべき新機能や破損した機能、そしてアップグレードする機能... + +### 設定値の削除: `WEBSOCKET_READ_LIMIT`, `WEBSOCKET_WRITE_LIMIT` と `WEBSOCKET_MAX_QUEUE` + +websocket 実装の完全なオーバーホールにより、これらの設定値は削除されました。 現在、それらを交換する計画はありません。 + +### `FALLBACK_ERROR_FORMAT` のデフォルト値の非推奨。 + +エラーハンドラがアタッチされていない場合、Sanicはフォールバックフォーマットタイプに`html`を使用しました。 これは非推奨となり、v22.3 から始まる「text」に変更されます。 この値は `auto` に変更されましたが、変更前の最後の手段として HTML を引き続き使用します。 + +### `ErrorHandler.lookup` シグネチャは非推奨です + +`ErrorHandler.lookup` は2つの位置引数を\*\*必要とします。 + +```python +def lookup(self, exception, route_name: Optional[str]): +``` + +準拠していないメソッドを使用すると、ブループリント固有の例外ハンドラが適切にアタッチされなくなります。 + +### 今後の削除の通知 + +リマインダーとして、以下の項目は既に非推奨となり、バージョン 21.12LTS で削除されます。 + +- `CompositionView` +- `load_env` (代わりに `env_prefix` を使用) +- サニック・オブジェクト (アプリケーションのインスタンス、設計図、ルート) は以下の英数字でなければなりません: `^[a-zA-Z][a-zA-Z0-9_\-]*$` +- アプリケーションおよびブループリントインスタンスへのオブジェクトの任意の割り当て(代わりに`ctx`を使用してください。これの削除は21.9から21.12に行われました) + +### ウェブソケットのオーバーホール + +WebSocket接続の処理には大きなオーバーホールがありました。 [@aaughustin](https://github.com/aaughustin) [`websockets`](https://websockets.readthedocs.io/en/stable/index.html) のおかげで、Sanicがウェブソケット接続のI/Oを独自に処理できるようにする新しい実装が追加されました。 したがって、Sanicは最小のバージョンを`websockets>=10.0`にバンプしました。 + +SanicのWebSocketハンドラに関する奇妙な変更が修正されたことを除いて、ほとんどの変更は開発者にとって目立たないはずです。 たとえば、誰かが切断したときに `CancellError` を自分でキャッチできるようになりました。 + +```python +@app.websocket("/") +async def handler(request, ws: + try: + while True: + await asyncio.sleep(0.25) + except asyncio.CanceledError: + print("User closed connection") +``` + +### ビルトイン信号 + +バージョン [21.3](./v21.3.md) で [signals](../advanced/signals.md) が導入されました。 Sanicはコードベース内からシグナルイベントを**送信**します。 これにより、開発者は以前よりもずっと近いレベルでリクエスト/レスポンスサイクルをフックすることができます。 + +以前は、ロジックを注入する場合は、ミドルウェアに限定されていました。 統合された信号を_super_-middlewareと考えてください。 派遣されるイベントは次のとおりです。 + +- `http.lifycle.begin` +- `http.lifycle.complete` +- `http.lifycle.exception` +- `http.lifycle.handle` +- `http.lifycle.read_body` +- `http.lifycle.read_head` +- `http.lifycle.request` +- `http.lifycle.response` +- `http.lifycle.send` +- `http.middleware.after` +- `http.middleware.before` +- `http.routing.after` +- `http.routing.before` +- `server.init.after` +- `server.init.before` +- `server.shutdown.after` +- `server.shutdown.before` + +.. note:: + +``` +`server` は、4 つの (4) のメインサーバリスナーイベントと同じです。 実際、これらのリスナー自身が実装を知らせるための便利なラッパーになっています。 +``` + +### よりスマートな `auto` 例外フォーマット + +Sanicはエンドポイントとクライアントに基づいて適切な例外形式で応答しようとします。 たとえば、エンドポイントが `sanic.response.json` オブジェクトを常に返す場合、例外は自動的に JSON でフォーマットされます。 `text`と`html`レスポンスでも同じです。 + +さらに、ルート定義を使用してルートごとにどのフォーマッタを使用するかを明示的に制御できるようになりました。 + +```python +@app.route("/", error_format="json") +async def handler(request): + pass +``` + +### 建設計画のコピー + +設計図は新しいインスタンスにコピーできます。 これにより、ルート、ミドルウェアなど、接続されているすべてのものが転送されます。 + +```python +v1 = 青写真("Version1", version=1) + +@v1.route("/something") +def something(request): + pass + +v2 = v1.copy("Version2", version=2) + +app.blueprint(v1) +app.blueprint(v2) +``` + +``` +/v1/something +/v2/something +``` + +### 建設計画グループの便利な方法 + +設計図グループは、通常の設計図と同じ方法をすべて使用できるようになりました。 これにより、ブループリントのコピーとともに、ブループリントは非常に構成可能で柔軟性があります。 + +### ヘッダーの解析を承認する + +Sanic `Request` オブジェクトは、 `Accept` ヘッダを解析して、クライアントのコンテンツタイプ設定の順序リストを提供することができます。 アクセサリーとしてアクセスできます: + +```python +print(request.accept) +# ["*/*"] +``` + +また、ワイルドカードマッチングを処理することもできます。 たとえば、受信リクエストが含まれていると仮定します。 + +``` +承認: */* +``` + +次に、`True`を指定します。 + +```python +request.accept の "text/plain" +``` + +### デフォルトの例外メッセージ + +`SanicException`に由来する例外は、デフォルトの例外メッセージを定義できるようになりました。 これにより、同じ例外を複数の場所で再利用することがより便利で、例外が提供するメッセージを伴うDRYの問題に巻き込まれることなく、メンテナンス可能になります。 + +```python +class TeaError(SanicException): + message = "Tempest in a teapot" + +raise TeaError +``` + +### 型注釈の便利さ + +Pythonの型アノテーションを使ってパスパラメータ型を制御できるようになりました。 以下を行う代わりに: + +```python +@app.route("///") +def handler(request: Request, one: int, two: float, three: UUID): +... +``` + +これで簡単に行うことができます。 + +```python +@app.route("///") +def handler(request: Request, one: int, two: float, three: UUID): +... +``` + +どちらの例でも、同じルーティング原則が適用されます。 + +### 明示的な静的リソースタイプ + +リソースをファイルとして扱うかディレクトリとして扱うかどうかを明示的に指定できるようになりました。 + +```python +static("/", "/path/to/some/file", resource_type="file")) +``` + +## ニュース + +### `sanic-ext` のリリースと非推奨の `sanic-openapi` + +Sanicの主要な原則の一つは、独裁者ではなく、道具であることを意味するということです。 このウェブサイトのフロントページは以下のとおりです。 + +> ツールを使うことで制約がなくても構築方法を構築できます。 + +これは、(特に Web API 開発者によって) 使用される多くの一般的な機能が `sanic` リポジトリに存在しないことを意味します。 これは正当な理由がある。 無関心であることは、開発者の自由と柔軟性を提供します。 + +しかし、時々あなたは同じものを構築し、再構築する必要はありません。 Sanicはこれまで、プラグインでギャップを埋めるためにコミュニティの素晴らしいサポートを本当に頼ってきました。 + +初期から公式の `sanic-openapi` パッケージがあり、アプリケーションに基づいて OpenAPI ドキュメントを作成することができました。 しかし、そのプロジェクトは長年にわたって悩まされており、主要プロジェクトほど優先されていません。 + +v21.9のリリースから、SCOは`sanic-openapi`パッケージを非推奨にし、メンテナンスモードに移行します。 これは、現在の将来のためにそれを維持するために必要に応じて更新を引き続き取得することを意味します。 しかし、新機能拡張は一切受け付けません。 + +`sanic-ext`という新しいプロジェクトが始まっています。 このパッケージは、OAS3ドキュメントを構築する機能だけでなく、 しかし、API 開発者がアプリケーションに求める多くのギャップを埋めます。 例えば、CORSをセットアップし、必要に応じて`HEAD`と`OPTIONS`応答を自動的に有効にします。 また、標準ライブラリDataclassまたはPydanticモデルのいずれかを使用して受信データを検証することもできます。 + +グッズのリストには以下が含まれます: + +- CORS保護 +- 受信リクエストの検証 +- auto OAS3 documentation using Redoc and/or Swagger UI +- auto `HEAD` 、 `OPTIONS` 、 `TRACE` 応答 +- 依存性インジェクション +- レスポンスシリアライゼーション + +このプロジェクトは `alpha` モードになっており、変更される可能性があります。 本番環境では対応可能であると考えられていますが、引き続き機能を追加するためにAPIを変更する必要があるかもしれません。 + +詳細については、 [documentation](../../plugins/sanic-ext/getting-started.md) をチェックアウトしてください。 + +## ありがとうございます + +このリリースに参加いただき、ありがとうございます: :clap: + +[@aaugustin](https://github.com/aaugustin) +[@ahopkins](https://github.com/ahopkins) +[@ashleysommer](https://github.com/ashleysommer) +[@cansarigol3megawatt](https://github.com/cansarigol3megawatt) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@gluhar2006](https://github.com/gluhar2006) +[@komar007](https://github.com/komar007) +[@ombe1229](https://github.com/ombe1229) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@Tronic](https://github.com/tronic) +[@vltr](https://github.com/vltr) + +そして、[@miss85246](https://github.com/miss85246)と[@ZinkLu](https://github.com/ZinkLu)に感謝します。 + +--- + +あなたがプロジェクトを楽しんでいるなら、貢献を検討してください。 もちろん私たちはコード貢献は大好きですが、どんな形でも貢献できます。 ドキュメントを書いたり、ユースケースを示したり、会話に参加したり、声を知らせたりすることを検討してください。format@@0(https://opencollective.com/sanic-org/) diff --git a/guide/content/ja/release-notes/2022/v22.12.md b/guide/content/ja/release-notes/2022/v22.12.md new file mode 100644 index 0000000000..6ffe0a1e6c --- /dev/null +++ b/guide/content/ja/release-notes/2022/v22.12.md @@ -0,0 +1,190 @@ +--- +title: バージョン 22.12 (LTS) +--- + +# バージョン 22.12 (LTS) + +.. TOC:: + +## はじめに + +これはバージョン22format@@0(../../org/polices.md#release-schedule)の最終リリースです。 このようなものは **長期サポート** リリースであり、 [policies](../../org/polices.md#long-term-support-v-interim-releases) で述べられているようにサポートされます。 + +## 知っておくべきこと + +詳細は [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html) をご覧ください。 注目すべき新機能や破損した機能、そしてアップグレードする機能... + +### 🚨 _BREAKING CHANGE_ - Sanic Inspector is now an HTTP server + +Sanic v22.9 は、実行中の Sanic インスタンスの実況検査を可能にする [Inspector](./v22.9.md#inspector) を導入しました。 この機能は、TCP ソケットを開き、カスタムプロトコル上で通信する際に依存していました。 その基本的なTCPプロトコルは、その場所で完全なHTTPサービスを実行することを支持しています。 format@@0(../deployment/inspector.md) + +今回のリリースでは、新しい HTTP サーバーと更新された CLI エクスペリエンスが導入されました。 これにより、ここでハイライトされたいくつかの新機能が有効になります。 しかし、最も重要な変更点は、インスペクターのすべてのコマンドを CLI インスタンスのサブパーサーに移動することです。 + +``` +$ sanic inspect --help + + ▄███ █████ ██ ▄█▄ ██ █ █ ▄██████████ + ██ █ █ █ ██ █ █ ██ + ▀███████ ███▄ ▀ █ █ ██ ▄ █ ██ + ██ █████████ █ ██ █ █ ▄▄ + ████ ████████▀ █ █ █ ██ █ ▀██ ███████ + +Optional +======== + General: + -h, --help show this help message and exit + --host HOST, -H HOST Inspector host address [default 127.0.0.1] + --port PORT, -p PORT Inspector port [default 6457] + --secure, -s Whether to access the Inspector via TLS encryption + --api-key API_KEY, -k API_KEY Inspector authentication key + --raw Whether to output the raw response information + + Subcommands: + Run one or none of the below subcommands. Using inspect without a subcommand will fetch general information about the state of the application instance. + + Or, you can optionally follow inspect with a subcommand. If you have created a custom Inspector instance, then you can run custom commands. See https://sanic.dev/en/guide/deployment/inspector.html for more details. + + {reload,shutdown,scale,} + reload Trigger a reload of the server workers + shutdown Shutdown the application and all processes + scale Scale the number of workers + Run a custom command +``` + +#### CLIリモートアクセスが利用可能になりました + +インスペクタの `host` と `port` は、上記のようにCLIに明示的に公開されます。 以前は v22.9 ではアプリケーションインスタンスを参照して推論されていました。 この変更により、 インスペクターを本番環境のインスタンスに公開し、CLI のリモートインストールからアクセスできるようになります。 + +たとえば、ローカルの開発マシンから実行中の本番環境を確認できます。 + +``` +$ sanic inspect --host=1.2.3.4 +``` + +.. 警告:: + +``` +**本番環境** の場合は、_TLS と authentication_ が使用されていることを確認してください。 +``` + +#### TLS 暗号化が利用可能になりました + +Web トラフィックを暗号化する TLS 証明書を提供することで、リモートインスペクターのアクセスを保護できます。 + +```python +app.config.INSPECTOR_TLS_CERT = "/path/to/cert.pem" +app.config.INSPECTOR_TLS_KEY = "/path/to/key.pem" +``` + +CLI 経由で暗号化されたインストールにアクセスするには、`--secure` フラグを使用します。 + +``` +$ sanic inspect --secure +``` + +#### 認証が利用可能になりました + +リモートインスペクターへのアクセスを制御するには、API キーを使用してエンドポイントを保護できます。 + +```python +app.config.INSPECTOR_API_KEY = "Super-Secret-200" +``` + +CLI 経由で保護されたインストールにアクセスするには、`--api-key` フラグを使用します。 + +``` +$ sanic inspect --api-key=Super-Secret-200 +``` + +これは `Authorization: Bearer ` と同じです。 + +``` +$ curl http://localhost:6457 -H "Authorization: Bearer Super-Secret-200" +``` + +### 実行中のサーバーワーカーの数を調整 + +インスペクターは、ワーカープロセスの数を調整できるようになりました。 例えば、3 つのレプリカにスケーリングするには、次のコマンドを使用します。 + +``` +$ sanic inspect scale 3 +``` + +### インスペクタをカスタムコマンドで拡張 + +インスペクターは CLI にカスタムコマンドを追加できるようになりました。 詳細については、[カスタムコマンド](../deployment/inspector.md#custom-commands)を参照してください。 + +``` +$ sanic inspect foo --bar +``` + +### 失敗時に早期ワーカー終了 + +v22.9 に同梱されているプロセスマネージャーは、非常に短いスタートアップタイムアウトがありました。 これはデッドロックから守るためだった。 これは30秒に増加しました。 新しいメカニズムが追加されました起動時にワーカープロセスにクラッシュが発生した場合です + +### JSON レスポンスボディを更新するための便利なメソッドを使用して `JSONResponse` を導入する + +`sanic.response.json`便利メソッドは、`JSONResponse`という名前の新しいサブクラスを返すようになりました。 この新しいタイプには、レスポンスボディへの変更を作成後に処理するための便利な方法がいくつかあります。 + +```python +resp = json({"foo": "bar"}) +resp.update({"another": "value"}) +``` + +詳細については、[../basics/response.md#returning-json-data] を参照してください。 + +### 下流の要件の更新: `uvloop` と `websockets` + +最小値の `uvloop` を `0.15.0` に設定しました。 Sanic が `websockets` バージョン `11.0` に準拠するように変更を加えました。 + +### `ctrl+c`を強制終了する + +オペレーティングシステムをサポートする場合には、Sanic サーバが `ctrl+c` を押した時に優雅なシャットダウンを試みることが既存の動作です。 この新しいリリースでは、最初のシャットダウンが開始された後に続く「ctrl+c」に対して即座にシャットダウンを実行します。 + +### 非推奨と削除 + +1. _DEPRECATED_ - v22.9 で導入された `--inspect*` コマンドは、`inspect` として利用可能な新しいサブコマンドパーサに置き換えられました。 フラグのバージョンは v23.3 まで動作します。 交換を使用することをお勧めします。 この短い非推奨期間は標準的な2サイクルからの偏差ですが、この変更が最小限に抑えられることを願っています。 + ``` + OLD sanic ... --inspect + NEW sanic ... inspect + + OLD sanic ... --inspect-raw + NEW sanic ... inspect --raw + + OLD sanic ... --inspect-reload + NEW sanic ... inspect reload + + OLD sanic ... --inspect-shutdown + NEW sanic ... inspect shutdown + ``` + +## ニュース + +Sanic Community Organizationは2023年の新しい運営会議を率いています。 帰還者は二人、新会員は二人いる。 + +[@ahopkins](https://github.com/ahopkins) _returning_ \ +[@prryplatypus](https://github.com/prryplatypus) _returning_ \ +[@sjsadowski](https://github.com/sjsadowski) _NEW_ \ +[@Tronic](https://github.com/Tronic) _NEW_ + +2023年のリリースマネージャーは[@ahopkins](https://github.com/ahopkins)と[@sjsadowski](https://github.com/sjsadowski)です。 + +Sanicにもっと関心がある場合は、[Discordサーバー](https://discord.gg/FARQzAEMAA)でご連絡ください。 + +## ありがとうございます + +このリリースに参加いただき、ありがとうございます: :clap: + +[@aaugustin](https://github.com/aaugustin) +[@ahopkins](https://github.com/ahopkins) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@kijk2869](https://github.com/kijk2869) +[@LiraNuna](https://github.com/LiraNuna) +[@prryplatypus](https://github.com/prryplatypus) +[@sjsadowski](https://github.com/sjsadowski) +[@todddialpad](https://github.com/todddialpad) +[@Tronic](https://github.com/Tronic) + +--- + +あなたがプロジェクトを楽しんでいるなら、貢献を検討してください。 もちろん私たちはコード貢献は大好きですが、どんな形でも貢献できます。 ドキュメントを書いたり、ユースケースを示したり、会話に参加したり、声を知らせたりすることを検討してください。format@@0(https://opencollective.com/sanic-org/) diff --git a/guide/content/ja/release-notes/2022/v22.3.md b/guide/content/ja/release-notes/2022/v22.3.md new file mode 100644 index 0000000000..0728ffc1e5 --- /dev/null +++ b/guide/content/ja/release-notes/2022/v22.3.md @@ -0,0 +1,230 @@ +--- +title: バージョン22.3 +--- + +# バージョン22.3 + +.. TOC:: + +## はじめに + +これはバージョン22format@@0(../../org/polices.md#release-schedule)の最初のリリースです。 すべての標準SCOライブラリが同じリリースサイクルに入り、同じバージョンのパターンに従います。 これらのパッケージは以下のとおりです: + +- [`sanic-routing`](https://github.com/sanic-org/sanic-routing) +- [`sanic-testing`](https://github.com/sanic-org/sanic-testing) +- [`sanic-ext`](https://github.com/sanic-org/sanic-ext) + +## 知っておくべきこと + +詳細は [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html) をご覧ください。 注目すべき新機能や破損した機能、そしてアップグレードする機能... + +### アプリケーションのマルチ サービス + +Sanicサーバーには、同じプロセスで複数のアプリケーションを並行して実行できるAPIが追加されました。 これは、1つまたは複数のアプリケーションインスタンスに対して`app.prepare(...)`を呼び出すことによって行われます。 毎回ユニークなホスト/ポートの組み合わせにバインドする必要があります。 次に、`Sanic.serve()`を呼び出してアプリケーションのサービスを開始します。 + +```python +app = Sanic("One") +app2 = Sanic("Two") + +app.prepare(port=999999) +app.prepare(port=9998) +app.prepare(port=9997) +app2.prepare(port=888888) +app2.prepare(port=8887) + +Sanic.serve() +``` + +上記のスニペットには、同時に実行され、複数のポートにバインドされる2つのアプリケーションがあります。 この機能は CLI ではサポートされていません。 + +このパターンは、 `app.run(...)` の代わりに使用されます。 `app.run` は上記のパターンの省略形に過ぎず、まだ完全にサポートされていることに注意してください。 + +### 👶 _BETA FEATURE_ - 新しいパスパラメータタイプ: ファイル拡張子 + +非常に一般的なパターンは、動的にファイルを生成するルートを作成することです。 エンドポイントは、拡張子のあるファイル上で一致するように意図されています。 ファイルに一致する新しいパスパラメータがあります: ``。 + +```python +@app.get("/path/to/") +async def handler(request, filename, ext): +... +``` + +これはファイル拡張子で終わるパターンをキャッチします。 ただし、拡張子を指定し、ファイル名に他のパスパラメータ型を使用することで、これを展開することができます。 + +例えば、数字のみの`.jpg`ファイルをキャッチしたい場合: + +```python +@app.get("/path/to/") +async def handler(request, filename, ext): +... +``` + +いくつかの潜在的な例: + +| 定義 | 例 | ファイル名 | 拡張 | +| ------------------------------------------------------------------------------------ | ----------------------------------------------------------- | -------- | ---------- | +| \ | page.txt | `"page"` | `"txt"` | +| \ | cat.jpg | `"cat"` | `"jpg"` | +| \ | cat.jpg | `"cat"` | `"jpg"` | +| | 123.txt | `123` | `"txt"` | +| | 123.svg | `123` | `"svg"` | +| | 3.14.tar.gz | `3.14` | `"tar.gz"` | + +### 🚨 _BREAKING CHANGE_ - パスパラメータの空でない文字列の一致 + +動的パスパラメータは空でない文字列にのみマッチします。 + +以前は、動的な文字列パラメータ (`/` または `/`) を持つルートは、空の文字列を含む任意の文字列にマッチします。 空でない文字列にのみマッチします。 古い動作を保持するには、新しいパラメータ `/ ` を使用します。 + +```python +@app.get("/path/to/") +async def handler(request, foo) +... +``` + +### 🚨 _BREAKING CHANGE_ - `sanic.worker.GunicornWorker` が削除されました + +通常の非推奨ポリシーから離れて、Sanic サーバーをマルチサーブにアップグレードするプロセスの一部として `GunicornWorker` が削除されました。 この決定は、存在している間にも、Sanicを展開するための最適な戦略ではなかったため、主に部分的に行われました。 + +`gunicorn`を使用してSanicをデプロイしたい場合は、format@@0(https://www.uvicorn.org/#running-with-gunicorn)を使用して実行することをお勧めします。 これにより、SanicはASGIアプリケーションとして`uvicorn`を通じて効果的に実行されます。 `uvicorn`をインストールすると、このパターンにアップグレードできます。 + +``` +pip install uvicorn +``` + +次に、次のようなパターンで実行できるはずです。 + +``` +gunicorn path.to.sanic:app -k uvicorn.worker.UvicornWorker +``` + +### Authorization header parsing + +The `Authorization` header has been partly parseable for some time. `request.token`を使って、以下の2つの形式のいずれかにあるヘッダーにアクセスできます。 + +``` +Authorization: Token +Authorization: Bearer +``` + +Sanic は `BASIC` のようなより多くの資格情報型を解析できるようになりました。 + +``` +Authorization: Basic Z2lsLWJhdGVzOnBhc3N3b3JkMTiz +``` + +これは `request.credentials` としてアクセスできるようになりました。 + +```python +print(request.credentials) +# 資格情報(auth_type='Basic', token='Z2lsLWJhdGVzOnBhc3N3b3JkMTIz', _username='gil-bates', _password='password123') +``` + +### CLI引数はオプションでアプリケーションファクトリに注入されます + +Sanicは、解析されたCLI引数を使用している場合、工場に注入しようとします。 + +```python +def create_app(args): + app = Sanic("MyApp") + print(args) + return app +``` + +``` +$sanic p:create_app --factory +Namespace(module='p:create_app', factory=True, simple=False, host='127.0.0.1', port=8000, unix='', cert=None, key=None, tlshost=False, works=1, fast=False, access_log=False, debug=False, aut_reload=False, path=Path=False, motd=True, verboss=None, y_exceptions=False) +``` + +`--factory`でCLIを実行している場合は、コマンドに任意の引数を渡すこともできます。 引数`Namespace`に注入されます。 + +``` +sanic p:create_app --factory -foo=bar +Namespace(module='p:create_app', factory=True, simple=False, host='127.0.0. ', port=8000, unix='', cert=None, key=None, tls=None, tlshost=False, worker=1, fast=False, access_log=False, debug=False, auto_reload=False, path=None, dev=False, motd=True, verbosity=None, noise, noisy_exceptions=False, foo='bar') +``` + +### 新しいリローダープロセスリスナーイベント + +Sanic サーバーを自動リロードして実行する場合、リローダープロセスで _only_ リスナーをトリガーする 2 つの新しいイベントがあります。 + +- `reload_process_start` +- `reload_process_stop` + +これらは、リローダーが実行されている場合にのみトリガーされます。 + +```python +@app.reload_process_start +async def reload_start(*_): + print(">>>>>>>> reload_start <<<<<") + +@app.reload_process_stop +async def reload_stop (*_): + print(">>>>>>>>reload_stop <<<<") +``` + +### イベントループはもはやリスナーの必須引数ではありません + +リスナーの引数 `loop` は省略できます。 これらの例はどちらも期待通りに動作します: + +```python +@app.before_server_start +async def without(app): + ... + +@app.before_server_start +async def with(app, loop): +... +``` + +### 削除 - デバッグモードが自動的にリローダーを開始しません + +`--debug` または `debug=True` で実行している場合、Sanic サーバは自動的にリローダーを起動しません。 このデバッグで両方を行う機能は v21 で非推奨となり、このリリースで削除されました。 \*debug mode と auto-reload の両方を使用したい場合は、 `--dev` または `dev=True` を使用できます。 + +**dev = デバッグモード + auto reloader** + +### Deprecation - 小文字環境変数の読み込み + +Sanicは接頭辞付きの環境変数を設定値として読み込みます。 プレフィックスが一致している限り、大文字と小文字は区別されません。 ただし、キーは大文字にすべきという慣習は常にありました。 これは非推奨であり、値が大文字でない場合は警告が表示されます。 v22.9では、大文字と接頭辞付きキーのみがロードされます。 + +## ニュース + +### Packt は Sanic Web 開発に関する新しい本を公開 + +.. 列:: + +``` +[@ahopkins](https://github.com/ahopkins)による**Python Web Development の新しい本があります。 本書はSCOによって承認されており、すべての売り上げの一部は、Sanicのさらなる発展のためにSCOに直接行きます。 + + [sanicbook.com](https://sanicbook.com/) で詳細を学ぶことができます。 +``` + +.. 列:: + +``` +![Python Web Development with Sanic](https://sanicbook.com/images/SanicCoverFinal.png) +``` + +## ありがとうございます + +このリリースに参加いただき、ありがとうございます: :clap: + +[@aericson](https://github.com/aericson) +[@ahankinson](https://github.com/ahankinson) +[@ahopkins](https://github.com/ahopkins) +[@ariebovenberg](https://github.com/ariebovenberg) +[@ashleysommer](https://github.com/ashleysommer) +[@Bluenix2](https://github.com/Bluenix2) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@dotlambda](https://github.com/dotlambda) +[@eric-spitler](https://github.com/eric-spitler) +[@howzitcdf](https://github.com/howzitcdf) +[@jonra1993](https://github.com/jonra1993) +[@prryplatypus](https://github.com/prryplatypus) +[@raphaelauv](https://github.com/raphaelauv) +[@SaidBySolo](https://github.com/SaidBySolo) +[@SerGeRybakov](https://github.com/SerGeRybakov) +[@Tronic](https://github.com/Tronic) + +--- + +あなたがプロジェクトを楽しんでいるなら、貢献を検討してください。 もちろん私たちはコード貢献は大好きですが、どんな形でも貢献できます。 ドキュメントを書いたり、ユースケースを示したり、会話に参加したり、声を知らせたりすることを検討してください。format@@0(https://opencollective.com/sanic-org/) diff --git a/guide/content/ja/release-notes/2022/v22.6.md b/guide/content/ja/release-notes/2022/v22.6.md new file mode 100644 index 0000000000..1315a0a52b --- /dev/null +++ b/guide/content/ja/release-notes/2022/v22.6.md @@ -0,0 +1,173 @@ +--- +title: バージョン22.6 +--- + +# バージョン22.6 + +.. TOC:: + +## はじめに + +これはバージョン22のformat@@0(../../org/policies.md#release-schedule)の2番目のリリースです。 バージョン22は、12月の長期サポートバージョンリリースで「確定」されます。 + +## 知っておくべきこと + +詳細は [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html) をご覧ください。 注目すべき新機能や破損した機能、そしてアップグレードする機能... + +### `DEBUG`モードでの自動TLSセットアップ + +Sanic サーバーは、 [mkcert](https://github.com/FiloSottile/mkcert) または [trustme](https://github.com/python-trio/trustme) を使用して TLS 証明書を自動的にセットアップできます。 この証明書はローカル開発環境で `https://localhost` (または別のローカルアドレス) を有効にします。 これを行うには、 `mkcert` または `trustme` のいずれかを自分でインストールする必要があります。 + +.. 列:: + +```` +``` +$ sanic path.to.server:app --auto-tls --debug +``` +```` + +.. 列:: + +```` +```python +app.run(debug=True, auto_tls=True) +``` +```` + +この機能は `ASGI` モードまたは `PRODUCTION` モードで実行している場合は使用できません。 Sanic を本番環境で実行する場合、正規のベンダー経由で購入した実際の TLS 証明書を使用するか、format@@0(https://letsencrypt.org/) を使用する必要があります。 + +### HTTP/3 Server 🚀 + +2022 年6 月に、IETF は、HTTP/3 の仕様である [RFC 9114](https://www.rfc-editor.org/rfc/rfc91114.html) を最終決定し公開しました。 要するに、HTTP/3 は HTTP/1.1 や HTTP/2 とは異なるプロトコルです。なぜなら、TCPの代わりにUDP経由でHTTPを実装するからです。 新しいHTTPプロトコルは、Webページの読み込み時間を短縮し、古い標準のいくつかの問題を解決します。 format@@0(https://http3-explained.haxx.se/) の新しいウェブテクノロジーを使うことをお勧めします。 従来のツールは動作しないため、format@@0(https://curl.se/docs/http3.html)をインストールする必要があります。 + +Sanic server provides HTTP/3 support using [aioquic](https://github.com/aiortc/aioquic). **インストールする必要があります** + +``` +pip install sanic aioquic +``` + +``` +pip install sanic[http3] +``` + +HTTP/3 を開始するには、アプリケーションの実行時に明示的にリクエストする必要があります。 + +.. 列:: + +```` +``` +$ sanic path.to.server:app --http=3 +``` + +``` +$ sanic path.to.server:app -3 +``` +```` + +.. 列:: + +```` +```python +app.run(version=3) +``` +```` + +HTTP/3 と HTTP/1.1 の両方のサーバーを同時に実行するには、v22.3 で導入された [application multi-serve](./v22.3.html#application-multi-serve) を使用します。 + +.. 列:: + +```` +``` +$ sanic path.to.server:app --http=3 -http=1 +``` + +``` +$ sanic path.to.server:app -3 -1 +``` +```` + +.. 列:: + +```` +```python +app.preprepre(version=3) +app.prepre(version=1) +Sanic.serve() +``` +```` + +HTTP/3 は TLS を必要とするため、TLS 証明書なしでは HTTP/3 サーバーを起動できません。 `DEBUG`モードの場合は、format@@0(../how-to/tls.html) または `mkcert` を使用してください。 現在、HTTP/3 に対する自動的な TLS 設定は `trustme` と互換性がありません。 + +**👶 This feature is being released as an _EARLY RELEASE FEATURE_.** It is **not** yet fully compliant with the HTTP/3 specification, lacking some features like [websockets](https://websockets.spec.whatwg.org/), [webtransport](https://w3c.github.io/webtransport/), and [push responses](https://http3-explained.haxx.se/en/h3/h3-push). このリリースの意図は、既存の HTTP リクエスト/レスポンスサイクルを HTTP/3 と同等の機能にすることです。 次のいくつかのリリースでは、より多くの HTTP/3 の機能が追加され、そのための API が確定されます。 + +### 一貫性のある例外命名です + +Sanic 例外の一部は、標準の HTTP レスポンス名に準拠するように改名されました。 + +- `InvalidUsage` >> `BadRequest` +- `MethodNotSupported` >> `MethodNotAllowed` +- `ContentRangeError` >> `RangeNotSatisfiable` + +すべての古い名前はエイリアスされており、後方互換性があります。 + +### 現在のリクエスト取得 + +アプリケーション(`Sanic.get_app()`)にアクセスするためのAPIと同様に、リクエストハンドラの外部から現在のリクエストを取得するための新しいメソッドがあります。 + +```python +from sanic import Request + +Request.get_current() +``` + +### キャッシュコントロールヘッダーの設定の API サポートの改善 + +`file` レスポンスヘルパーには、 [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) ヘッダーの設定を容易にするためのいくつかのパラメータが追加されています。 + +```python +file( + ..., + last_modified=..., + max_age=..., + no_store=..., +) +``` + +### カスタムの `loads` 関数 + +Sanicがカスタムの`dumps`をグローバルに設定するのと同様に、グローバルにカスタムの`loads`を設定できるようになりました。 + +```python +from orjson import load + +app = Sanic("Test", loads=loads) +``` + +### 非推奨と削除 + +1. _削除_ - アプリケーションはもはやアプリケーションレジストリからオプトアウトされない可能性があります +2. _REMOVED_ - 一部の例外が送信された後、カスタム例外ハンドラは実行されなくなります +3. _削除_ - フォールバックエラーフォーマットは `ErrorHandler` には設定できません。`Config` で**のみ**設定する必要があります +4. _削除_ - スタートアップ用のカスタム `LOGO` の設定はもう許可されていません +5. _REMOVED_ - 古い`stream`レスポンスの利便性メソッドが削除されました +6. _REMOVED_ - `AsyncServer.init` は削除され、`AsyncServer.app.state.is_started` のエイリアスは削除されました。 + +## ありがとうございます + +このリリースに参加いただき、ありがとうございます: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@amitay87](https://github.com/amitay87) +[@ashleysommer](https://github.com/ashleysommer) +[@azimovMichael](https://github.com/azimovMichael) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@kijk2869](https://github.com/kijk2869) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@sjsadowski](https://github.com/sjsadowski) +[@timmo001](https://github.com/timmo001) +[@zozzz](https://github.com/zozzz) + +--- + +あなたがプロジェクトを楽しんでいるなら、貢献を検討してください。 もちろん私たちはコード貢献は大好きですが、どんな形でも貢献できます。 ドキュメントを書いたり、ユースケースを示したり、会話に参加したり、声を知らせたりすることを検討してください。format@@0(https://opencollective.com/sanic-org/) diff --git a/guide/content/ja/release-notes/2022/v22.9.md b/guide/content/ja/release-notes/2022/v22.9.md new file mode 100644 index 0000000000..33dc1c97bf --- /dev/null +++ b/guide/content/ja/release-notes/2022/v22.9.md @@ -0,0 +1,349 @@ +--- +title: バージョン 22.9 +--- + +# バージョン 22.9 + +.. TOC:: + +## はじめに + +これはバージョン22のformat@@0(../../org/policies.md#release-schedule)の3番目のリリースです。 バージョン22は、12月の長期サポートバージョンリリースで「確定」されます。 + +## 知っておくべきこと + +詳細は [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html) をご覧ください。 注目すべき新機能や破損した機能、そしてアップグレードする機能... + +### ⚠️ _重要_ - 新しいワーカーマネージャー🚀 + +Sanic サーバーは、どのように動作するかにより一貫性と柔軟性を提供するためにオーバーホールされています。 動機の詳細については、[PR #2499](https://github.com/sanic-org/sanic/pull/2499)を参照してください。ライブストリーム [discussion hold on YouTube](https://youtu.be/m8HCO8NK7HE) で議論されています。 + +これはASGIモードでSanicには適用されません + +#### 変更の概要 + +- ワーカーサーバーは子プロセスで**常時**実行されます。 + - 以前は、これは1対多のワーカー、およびリローダーの使用または使用によって変更される可能性がありました。 これにより、より密接に生産相手と一致する、より予測可能な開発環境につながるはずです。 +- マルチワーカーは \*_Windows_ でサポートされるようになりました。 + - SanicはWindowsでは利用できない`fork`を使って`multiprocessing`モジュールを使用していたので、これは不可能でした。 + - Sanicは常に `spawn` を使用します。 これはいくつかの顕著な違いがあります。特に `app.run` を使用してグローバルスコープで Sanic を実行している場合(下記参照: upgrade issues)。 +- アプリケーション・インスタンスは、1つまたは複数のワーカーを再起動するために使用できる新しい`multiplace`オブジェクトを持っています。 これは、例えば、リクエストによってトリガーされる可能性があります。 +- 新しいインスペクターがあり、サーバーの状態について詳細を提供できます。 +- Sanic Worker マネージャは任意のプロセスを実行できます。 + - これにより、開発者はSanic内で必要なプロセスを追加できます。 + - 利用可能なユースケース: + - ヘルスモニター、Sanic Extensions を参照してください。 + - ログキュー, 参照Sanic Extensions + - 分離プロセスのバックグラウンドワーカーキュー + - ボットのように別のアプリケーションを実行しています +- `main_process_ready`という新しいリスナーがあります。 Sanicに任意のプロセスを追加するためにのみ使用する必要があります。 +- Worker 間で共有されたオブジェクトを渡す。 + - Python では、共有メモリやパイプなどを介してプロセス間で状態を共有するオブジェクトがあります。 + - Sanicはこれらのタイプのオブジェクトを`app.shared_ctx`オブジェクトで共有できるようになりました。 + - この機能は Python の `multiprocessing` ライブラリに依存しています。 明らかに、同じ実行からインスタンス化されたSanicワーカーインスタンス間での状態の共有にのみ有効です。 これは、たとえば複数のマシンで水平スケーリングを行うための API を提供することを意味していません。 + +#### 共有コンテキストオブジェクトの追加 + +ワーカープロセス間でオブジェクトを共有するには、 `main_process_start` リスナーの中で _MUST_ が割り当てられます。 + +```python +from multiprocessing import Queue + +@app.main_process_start +async def main_process_start(app): + app.shared_ctx.queue = Queue() +``` + +`shared_ctx` 上のすべてのオブジェクトは、各ワーカプロセス内で使用できるようになります。 + +```python +@app.before_server_starts +async def before_server_starts(app): + assert isinstance(app.shared_ctx.queue, Queue) + +@app.on_request +async def on_request(request): + assert isinstance(request.app.shared_ctx.queue, Queue) + +@app.get("/") +async def handler(request): + assert isinstance(request.app.shared_ctx.queue, Queue) +``` + +\*注意: Sanicは安全でないオブジェクトを登録することを止めることはありませんが、警告することがあります。 例えば、通常のリストオブジェクトを追加しないように注意してください。 プロセス間でどのように状態を共有するかを理解しておく必要があります。 + +#### 任意のプロセスを実行中 + +Sanicは任意のプロセスを実行することができます。 `SIGINT` または `SIGTERM` OS 信号で停止することができます。 + +これらのプロセスは `main_process_ready` リスナーの中に登録する必要があります。 + +```python +@app.main_process_ready +async def ready(app: Sanic, _): + app.manager.manager.manage("MyProcess", my_process, {"foo": "bar"}) +# app.manager.manage(, , ) +``` + +#### Inspector + +Sanic ships with an optional Inspector, これは、CLI がアプリケーションの実行状態と問題コマンドを検査できるようにする特別なプロセスです。 現在、CLI が Sanic インスタンスと同じマシン上で実行されている場合にのみ動作します。 + +``` +sanic path.to:app --inspect +``` + +![Sanic inspector](https://user-images.githubusercontent.com/166269/190099384-2f2f3fae-22d5-4529-b279-8446f6b5f9bd.png) + +新しい CLI コマンドは次のとおりです。 + +``` + --inspect Inspect the state of a running instance, human readable + --inspect-raw Inspect the state of a running instance, JSON output + --trigger-reload Trigger worker processes to reload + --trigger-shutdown Trigger all processes to shutdown +``` + +これはデフォルトでは有効になっていません。 利用できるようにするには、以下を選択する必要があります: + +```python +app.config.INSPECTOR = True +``` + +\*注意: Sanic Extensionsはサーバの状態にリクエストカウンタを追加するformat@@0(../basics/app.md#custom-requests)クラスを提供します。 + +#### アプリケーションマルチプレクサ + +同じ情報と機能の多くは、アプリケーションインスタンス自体で利用できます。 アプリケーション・インスタンスには、1つ以上のワーカーを再起動できる新しい「multiplace」オブジェクトがあります。 をクリックし、現在の状態に関する情報を取得します。 + +`app.multipplaze`としてアクセスすることも、短いエイリアスの`app.m`でアクセスすることもできます。 + +```python +@app.on_request +async def print_state(request: Request): + print(request.app.m.state) +``` + +#### 潜在的なアップグレードの問題 + +`fork`から`spawn`への切り替えのため、サーバーをグローバルスコープで実行しようとするとエラーが発生します。 次のようなものが表示された場合: + +``` +sanic.exceptions.ServerError: Sanic server could not start: [Errno 98] Address already in use. +グローバルスコープで Sanic を実行していて、`if __name__ == "__main__"` ブロックの中ではない場合に発生した可能性があります。 +``` + +... 変更は簡単だ `app.run`がブロックの中にあることを確認してください。 + +```python +if __name__ == "__main__": + app.run(port=999999, dev=True) +``` + +#### 新しい機能を無効にする + +新しいプロセスマネージャーなしでSanicを実行したい場合は、レガシーランナーを簡単に使用できます。 これらのサポートは **今後削除されます**。 日付は、まだ設定されていませんが、2023年のいつかになるでしょう。 + +新しいサーバーをオプトアウトしてレガシーを使用するには、Sanicの実行方法に応じて適切な方法を選択してください。 + +.. 列:: + +``` +CLIを使用している場合... +``` + +.. 列:: + +```` +``` +sanic path.to:app --legacy +``` +```` + +.. 列:: + +``` +`app.run`を使用する場合... +``` + +.. 列:: + +```` +``` +app.run(..., legacy=True) +``` +```` + +.. 列:: + +``` +`app.prepare` の場合... +``` + +.. 列:: + +```` +``` +app.prepare(...) +Sanic.serve_legacy() +``` +```` + +同様に、Sanicを単一のプロセスで実行させることができます。 しかし、これは自動リローダーへのアクセスがないことを意味します。 + +.. 列:: + +``` +CLIを使用している場合... +``` + +.. 列:: + +```` +``` +sanic path.to:app --single-process +``` +```` + +.. 列:: + +``` +`app.run`を使用する場合... +``` + +.. 列:: + +```` +``` +app.run(..., single_process=True) +``` +```` + +.. 列:: + +``` +`app.prepare` の場合... +``` + +.. 列:: + +```` +``` +app.prepare(...) +Sanic.serve_single() +``` +```` + +### ミドルウェアの優先度 + +ミドルウェアは、定義されたタイミングに基づいて順番に実行されます。 リクエストミドルウェアは順番に実行され、レスポンスミドルウェアは逆に実行されます。 これは、例えばグローバル変数のインポート順序に厳密に基づいている場合に、不幸な影響を与える可能性があります。 + +新たに追加されたのは、strict 構文のブレークアウトであり、ミドルウェアに優先度を割り当てることです。 ミドルウェア定義の数値が高いほど、シーケンスの前に実行されます。 これは**両方**リクエストとレスポンスミドルウェアに適用されます。 + +```python +@app.on_request +async def low_priority(_): + ... + +@app.on_request(priority=10) +async def high_priority(_): +... +``` + +上記の例では、 `low_priority` が先に定義されていても、 `high_priority` が先に実行されます。 + +### カスタムの `loads` 関数 + +Sanicはアプリのインスタンス化時にformat@@0(https://sanic.readthedocs.io/en/stable/sanic/api/app.html#sanic.app.Sanic)を追加する機能をサポートしています。 同じ機能が `loads` に拡張され、デシリアライズ時に使用されます。 + +```python +from json import load + +Sanic("Test", loads=loads) +``` + +### Websocket オブジェクトがイテレーション可能になりました + +`Websocket` オブジェクトのループで `recv` を呼び出すのではなく、`for` ループで繰り返すことができます。 + +```python +from sanic import Request, Websocket + +@app.websocket("/ws") +async def ws_echo_handler(request: Request, ws: Websocket): + async msg in ws: + await ws.send(msg) +``` + +### 静的ファイルに対しては304で適切に対応します + +静的ファイルを提供する場合、Sanic サーバーはファイルを再送する代わりに、`304` レスポンスを使用して `If-Modified-Since` を使用してリクエストに適切に対応できます。 + +### ハンドラの実行をラップするための2つの新しい信号 + +リクエストハンドラの実行をラップする2つの新しい [signals](../advanced/signals.md) が追加されました。 + +- `http.handler.before` - リクエストミドルウェアの後、ルートハンドラの前に実行されます +- `http.handler.after` - ルートハンドラの後に実行されます + - _ほとんど_ 状況では、これはレスポンスミドルウェアの前に動作することを意味します。 しかし、ルートハンドラの中から`request.respond`を呼び出した場合、ミドルウェアが先に来ます。 + +### HTTPメソッド情報の新しいリクエストプロパティ + +HTTP 仕様では、安全、idempotent、およびキャッシュ可能の HTTP メソッドが定義されています。 メソッドに基づいてリクエストプロパティを特定するのに役立つブーリアンフラグで応答する新しいプロパティが追加されました。 + +```python +request.is_safe +request.is_idempotent +request.is_cacheable +``` + +### 🚨 _BREAKING 変更_ - キャンセル要求例外の改善 + +Sanicの以前のバージョンでは、`CancelledError` がキャッチされた場合、バブルが解除され、サーバーが`503`で応答する可能性があります。 これは常に望ましい結果ではなく、他の状況ではそのエラーの使用を妨げています。 結果として、Sanicはこの機能に`CancelledError`というサブクラスを使用するようになりました。 明示的に古い動作に頼らない限り、影響はほとんどないはずです。 + +これらのプロパティの詳細については、[MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) を参照してください。 + +### 新しい非推奨警告フィルター + +Sanicからの非推奨警告のレベルは、[standard library warning filter values](https://docs.python.org/3/library/warnings.html#the-warnings-filter) を使用して制御できます。 デフォルトは `"once"` です。 + +```python +app.config.DEPRECEATION_FILTER = "ignore" +``` + +### 非推奨と削除 + +1. _DEPRECATED_ - ルート名の重複は非推奨となり、v23.3で削除されます +2. _DEPRECATED_ - 重複した例外ハンドラの登録は廃止予定となり、v23.3で削除されます。 +3. _REMOVED_ - `route.ctx`はSanicが設定したものではなく、ユーザーのための空白のオブジェクトなので... + - `route.ctx.ignore_body` >> `route.extra.ignore_body` + - `route.ctx.stream` >> `route.extra.stream` + - `route.ctx.hosts` >> `route.extra.hosts` + - `route.ctx.static` >> `route.extra.static` + - `route.ctx.error_format` >> `route.extra.error_format` + - `route.ctx.websocket` >> `route.extra.websocket` +4. _削除_ - `app.debug`はREAD-ONLY +5. _削除_ - `app.is_running`が削除されました +6. _削除_ - `app.is_stopping` が削除されました +7. _削除_ - `Sanic._uvloop_setting` が削除されました +8. _REMOVED_ - プリフィックスされた環境変数は大文字でなければ無視されます + +## ありがとうございます + +このリリースに参加いただき、ありがとうございます: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@azimovMichael](https://github.com/azimovMichael) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@huntzhan](https://github.com/huntzhan) +[@monosans](https://github.com/monosans) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@seemethere](https://github.com/seemethere) +[@sjsadowski](https://github.com/sjsadowski) +[@timgates42](https://github.com/timgates42) +[@Tronic](https://github.com/Tronic) + +--- + +あなたがプロジェクトを楽しんでいるなら、貢献を検討してください。 もちろん私たちはコード貢献は大好きですが、どんな形でも貢献できます。 ドキュメントを書いたり、ユースケースを示したり、会話に参加したり、声を知らせたりすることを検討してください。format@@0(https://opencollective.com/sanic-org/) diff --git a/guide/content/ja/release-notes/2023/v23.12.md b/guide/content/ja/release-notes/2023/v23.12.md new file mode 100644 index 0000000000..46b93ab6ef --- /dev/null +++ b/guide/content/ja/release-notes/2023/v23.12.md @@ -0,0 +1,185 @@ +--- +title: バージョン 23.12 +--- + +# バージョン 23.12 (LTS) + +.. TOC:: + +## はじめに + +これはバージョン23format@@0(../../organization/policyes.md#release-schedule)の最終リリースです。 これは**長期サポート(LTS)リリース**として指定されています。これはサポートポリシーに記載されている2年間のサポートを受けることを意味します。 何か問題が発生した場合は、 [GitHub](https://github.com/sanic-org/sanic/issues/new/choose) にご注意ください。 + +## 知っておくべきこと + +[Changelog](../changelog.html) の詳細。 注目すべき新機能や破損した機能、アップグレードする機能: + +### 🎉 {span:has-text-primary:Sanic} を使用してドキュメントを作成 + +![](http://127.0.0.1:8000/assets/images/sanic-framework-logo-circle-128x128.png) + +format@@0(/ja/built-with-sanic.html) を読むことができますが、このドキュメントサイトは SHH 🤫 スタックを使用しています。 + +- [Sanic](https://sanic.dev) +- [html5tagger](https://github.com/sanic-org/html5tagger) +- [HTMX](https://htmx.org/) + +### 👶 _BETA_ Sanic インタラクティブコンソールへようこそ + +そうです、Sanicは今REPLを発送しています! + +![](/assets/images/repl.png) + +Sanic CLI を使用する場合は、`--repl` 引数を渡すことで、アプリケーション側でインタラクティブなコンソールを自動的に実行できます。 これは開発中に非常に役立ち、アプリケーションインスタンスにアクセスすることができます。 実行中のインスタンスに HTTP リクエストを送信するために有効になっている組み込みクライアントだけでなく。 + +`--dev` フラグを使用している場合、この機能はデフォルトで準対応です。 REPLは完全に実行されませんが、 それはプロセスを開始し、キーボードの``を押すことによっていつでもREPLを入力することができます。 + +\*これはまだベータモードです。 私たちは、任意の機能強化の要求や問題について私たちに知らせていただきありがとうございます。 \* + +### Python 3.12 のサポート + +サポートされているバージョンに Python 3.12 を追加しました。 + +### 任意のプロセスを起動して再起動します + +[multiplexer](../../guide/running/manager.md#access-to-the-multiplexer) を使用すると、任意または既存のプロセスを開始して再起動できるようになりました。 これにより、マルチプレクサとワーカーマネージャが動作する方法で、次の新機能が有効になりました。 + +1. `multiplexer.restart("")`は目標とされた単一のプロセスを再起動します +2. `multiplexer.manage(...)` は、 `manager.manage(...)` とまったく同じように動作する新しいメソッドです。 +3. `manage` メソッドにキーワード引数が追加されました。 + - `tracked` - プロセスが完了した後にプロセスの状態が追跡されるかどうか + - `restartable` - プロセスを再起動させるかどうか + - `auto_start` - 作成後すぐにプロセスを開始するかどうか + +```python +def task(n: int = 10, **kwargs): + print("TASK STARTED", kwargs) + for i in range(n): + print(f"Running task - Step {i+1} of {n}") + sleep(1) + +@app.get("/restart") +async def restart_handler(request: Request): + request.app.m.restart("Sanic-TEST-0") + return json({"foo": request.app.m.name}) + + +@app.get("/start") +async def start_handler(request: Request): + request.app.m.manage("NEW", task, kwargs={"n": 7}, workers=2) + return json({"foo": request.app.m.name}) + +@app.main_process_ready +def start_process(app: Sanic): + app.manager.manage("TEST", task, kwargs={"n": 3}, restartable=True) +``` + +### 優先順位付けされたリスナーと信号 + +[v22.9](../2022/v22.9.md) Sanicはミドルウェアの任意の順序を可能にするためにミドルウェアに優先順位付けを追加しました。 この同じ概念は、リスナーとシグナルにも拡張されています。 これにより、作成時に優先度番号が割り当てられ、実行タイムライン内のデフォルトの位置が上書きされます。 + +```python +@app.before_server_start(priority=3) +async def sample(app): +... +``` + +数が多いほど、優先度が高くなります。 全体的に実行の順序を決定するためのルールは次のとおりです。 + +1. 降順の優先度 +2. Blueprint リスナーより前のアプリのリスナー +3. 登録注文 + +_リスナが逆順に実行されることを覚えておいてください_ + +### Websocket シグナル + +websocketに新しい3つのシグナルを追加しました。 + +1. `websocket.handler.before` +2. `websocket.handler.after` +3. `websocket.handler.exception` + +```python +@app.signal("websocket.handler.before") +async def ws_before(request: Request, websocket: Websocket): + ... + +@app.signal("websocket.handler.after") +async def ws_after(request: Request, websocket: Websocket): + ... + +@app.signal("websocket.handler.exception") +async def ws_exception( + request: Request, websocket: Websocket, exception: Exception +): + ... +``` + +![](https://camo.githubusercontent.com/ea2894c88bedf37a4f12f1295696e8fd14bfceaa36d4452c7b7a1869d2f1cdb18/68747470733a2f2f7a692e66692f77732d7369676e616c732e706e676e67) + +### シンプルな信号 + +Sanic は常に `one.two.three` の3つの命名規則を強制しています。 ただし、単一の部分だけである単純な名前を作成できるようになりました。 + +```python +@app.signal("foo") +async def foo(): + ... +``` + +通常の信号やルートと同じように、その部分を動的にすることができます。 + +```python +@app.signal("") +async def handler(**kwargs): + print("foobar signal received") + print(kwargs) + + +@app.route("/") +async def test(request: Request): + await request.app.dispatch("foobar") + return json({"hello": "world"}) +``` + +複数の動的信号を使用する必要がある場合は、長い3つの部品形式を使用する必要があります。 + +### `event`メソッドが更新されました + +`app.event()` と `blueprint.event()` の両方に変更が加えられました。 + +- `condition` と `exclusive` はマッチ条件を制御するためのキーワードです(`signal()`メソッドと同様) +- `signal()`のように、`str`か`Enum`を渡すことができます。 +- は、`dispatch()` メソッドに渡されたコンテキストのコピーを返します。 + +### リロードトリガーが変更されたファイルを取得します + +リローダーによって変更されたファイルは現在リスナーに注入されます。 これにより、トリガーはそれらの変更されたファイルが何であったかの知識を持って何かを行うことができます。 + +```python +@app.after_reload_trigger +async def after_reload_trigger(_, changed): + print(changed) +``` + +## ありがとうございます + +このリリースに参加いただき、ありがとうございます: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@freddiewanah](https://github.com/freddiewanah) +[@gluhar2006](https://github.com/gluhar2006) +[@iAndriy](https://github.com/iAndriy) +[@MichaelHinrichs](https://github.com/MichaelHinrichs) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@sjsadowski](https://github.com/sjsadowski) +[@talljosh](https://github.com/talljosh) +[@tjni](https://github.com/tjni) +[@Tronic](https://github.com/Tronic) + +--- + +あなたがプロジェクトを楽しんでいるなら、貢献を検討してください。 もちろん私たちはコード貢献は大好きですが、どんな形でも貢献できます。 ドキュメントを書いたり、ユースケースを示したり、会話に参加したり、声を知らせたりすることを検討してください。format@@0(https://opencollective.com/sanic-org/) diff --git a/guide/content/ja/release-notes/2023/v23.3.md b/guide/content/ja/release-notes/2023/v23.3.md new file mode 100644 index 0000000000..fcdd5619a5 --- /dev/null +++ b/guide/content/ja/release-notes/2023/v23.3.md @@ -0,0 +1,421 @@ +--- +title: バージョン 23.3 +--- + +# バージョン 23.3 + +.. TOC:: + +## はじめに + +これはバージョン23format@@0(../../org/polices.md#release-schedule)の最初のリリースです。 このように、いくつかの廃止予定が含まれており、うまくいけばいくつかの_小規模な_破壊的な変更があります。 何か問題が発生した場合は、 [GitHub](https://github.com/sanic-org/sanic/issues/new/choose) にご注意ください。 + +## 知っておくべきこと + +詳細は [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html) をご覧ください。 注目すべき新機能や破損した機能、そしてアップグレードする機能... + +### より優れたトレースバックのフォーマット + +SCOは2つのプロジェクトをGitHub上のSanic名前空間に採用しました: [tracerite](https://github.com/sanic-org/tracerite) と [html5tagger](https://github.com/sanic-org/html5tagger) これらのプロジェクトは、デバッグプロセスに役立つ詳細を提供し、信じられないほどの新しいエラーページを提供するためにチームを編成します。 + +これは、DEBUGモードまたはPRODモードにかかわらず、関連する情報のみを表示するように調整されます。 + +.. 列:: + +``` +**PRODモードを使用** +![image](/assets/images/error-html-no-debug.png) +``` + +.. 列:: + +``` +**DEBUGモードを使う** +![image](/assets/images/error-html-debug.png) +``` + +ライトモードとダークモード HTMLページは利用可能で、暗黙的に使用されます。 + +### ディレクトリの基本的なファイルブラウザー + +静的ハンドラからディレクトリを提供する場合、Sanic は `directory_view=True` を使用して基本的なファイルブラウザーを表示するように設定することができます。 + +.. 列:: + +```` +```python +app.static("/uploads/", "/path/to/dir/", directory_view=True) +``` +```` + +.. 列:: + +``` +![image](/assets/images/directory-view.png) +``` + +ライトモードとダークモード HTMLページは利用可能で、暗黙的に使用されます。 + +### PythonでHTMLテンプレートを作成 + +Sanic は [html5tagger](https://github) を使用しているためです。 om/sanic-org/html5tagger) format@@0(#nicer-traceback-formatting) をボンネットの下にレンダリングします。 Pythonコードで簡単にHTMLページを生成できるようになりました。 + +.. 列:: + +```` +```python +from html5tagger import Document +from sanic import Request, Sanic, html + +app = Sanic("TestApp") + +@app.get("/") +async def handler(request: Request): + doc = Document("My Website") + doc.h1("Hello, world.") + with doc.table(id="data"): + doc.tr.th("First").th("Second").th("Third") + doc.tr.td(1).td(2).td(3) + doc.p(class_="text")("A paragraph with ") + doc.a(href="/files")("a link")(" and ").em("formatting") + return html(doc) +``` +```` + +.. 列:: + +```` +```html + + +My Website +

Hello, world.

+ + + +
First + Second + Third +
1 + 2 + 3 +
+

+ A paragraph with a link and formatting +``` +```` + +### 自動インデックス提供は静的ハンドラで利用できます + +Sanicは、静的ディレクトリを提供するときにインデックスファイルを提供するように設定できるようになりました。 + +```python +app.static("/assets/", "/path/to/some/dir", index="index.html") +``` + +上記を使用する場合、`http://example.com/assets/`へのリクエストは、そのディレクトリにある`index.html`ファイルを自動的に提供します。 + +### シンプルな CLI ターゲット + +Sanicアプリケーションでは、変数`app`をアプリケーションインスタンスとして使用するのが一般的です。 このためです CLI アプリケーションのターゲット (`sanic` コマンドの 2 番目の値) は、ターゲットに基づいてアプリケーションのインスタンスを推定しようとします。 ターゲットが `app` 変数を含むモジュールの場合は、それを使用します。 + +現在、CLI から Sanic アプリケーションを起動する方法は 4 つあります。 + +#### 1. アプリケーションインスタンス + +通常と同様に、モジュールとアプリケーションインスタンスへのパスを提供することは期待どおりに動作します。 + +```sh +sanic path.to.module:app # グローバルアプリ インスタンス +``` + +#### 2. 申請工場 + +以前は、ファクトリパターンを提供するには、 `--factory` フラグを使用する必要がありました。 これは現在省略できます。 + +```sh +sanic path.to.module:create_app # ファクトリパターン +``` + +#### 3. Sanic Simple Server を起動するパス + +同様に、Sanic シンプルなサーバー (静的ディレクトリを提供する) を起動するには、以前は `--simple` フラグを使用する必要がありました。 これを省略することができ、代わりにディレクトリへのパスを単純に提供します。 + +```sh +sanic ./path/to/directory/ # simple serve +``` + +#### 4. 変数`app`を含むPythonモジュール + +前述のように、ターゲットが `app` 変数を含むモジュールの場合。 これを使用します (変数`app`が`Sanic`インスタンスであると仮定します)。 + +```sh +appインスタンスを持つsanic path.to.module # モジュール +``` + +### Cookieの設定と削除のためのより便利な方法 + +古いクッキーのパターンはぎこちなくて不器用でした。 普通のPythonのようには見えませんでした。なぜなら、"魔法"がオンになっているからです。 + +.. 列:: + +``` +😱 これは直感的ではなく、新規参入者にとって混乱しています。 +``` + +.. 列:: + +```` +```python +response = text("このレスポンスにはクッキーがあります") +response.cookies["test"] = "It worked!" +response. ookies["test"]["domain"] = ".yummy-yummy-cookie.com" +response.cookies["test"]["httponly"] = True +``` +```` + +このプロセスをより便利にするために、新しいメソッド(完全に`Cookie`と`CookieJar`オブジェクト)が追加されました。 + +.. 列:: + +``` +:releaseed_face: ああ...もっといい。 +``` + +.. 列:: + +```` +```python +response = text("There's a cookie up in this response") +response.add_cookie( + "test", + "It worked!", + domain=".yummy-yummy-cookie.com", + httponly=True +) +``` +```` + +### より良いクッキーの互換性 + +Sanic は [cookie prefixes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookie#cookie_prefixes) のサポートを追加しました。これにより、値を持つ Cookie を読み書きしやすくなりました。 + +Cookieの設定中... + +```py +response.cookies.add_cookie("foo", "bar", host_prefix=True) +``` + +`__Host-foo`というプレフィックス付きのクッキーが作成されます。 ただし、リクエストでクッキーにアクセスする場合は、ヘッダーの存在を知らなくてもアクセスできます。 + +```py +request.cookies.get("foo") +``` + +なお、クッキーは [headers](#access-any-header-as-a-property) と同様のプロパティとしてアクセスすることもできます。 + +```python +request.cookies.foo +``` + +クッキーは `request.args` と `request.form` オブジェクトに似ており、複数の値を取得するには `getlist` を使用します。 + +```py +request.cookies.getlist("foo") +``` + +format@@0(https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#partitioned_cookie)の作成もサポートされています。 + +```py +response.cookies.add_cookie(..., partitioned=True) +``` + +### 🚨 _BREAKING CHANGE_ - より強力な `SanicException` + +Sanicは基本クラスの例外として`SanicException`をしばらく含んでいます。 これは `status_code` などを追加するために拡張できます。format@@0(http://localhost:8080/ja/guide/best-practics/exceptions.html) + +**今です**、様々な例外をすべて使うのが簡単になりました。 一般的に使用される例外は、ルートレベルモジュールから直接インポートできます。 + +```python +from sanic import NotFound, Unauthorized, BadRequest, ServerError +``` + +さらに、これらの引数はすべて例外型ごとにキーワード引数として使用できます。 + +| 引数 | タイプ | 説明 | +| --------- | ------ | ---------------------------- | +| `quiet` | `bool` | ログからのトレースバックを抑制する | +| `context` | `dict` | エラーページに表示される追加情報 _常時_ | +| `extra` | `dict` | _DEBUG_ モードでエラーページに表示される追加情報 | +| `headers` | `dict` | 返信に送信される追加ヘッダー | + +これらのどれもそれ自体の新機能ではありません。 しかし、それらの使用方法により一貫性が高いため、エラー応答を直接制御する強力な方法が作成されます。 + +```py +raise ServerError(headers={"foo": "bar"}) +``` + +これの部分は、以前は位置引数がキーワードのみであるということです。 + +format@@0(https://sanic.readthedocs.io/en/stable/sanic/api/exceptions.html#module-sanic.exceptions)にある各エラーの実装を確認することをお勧めします。 + +### 🚨 _BREAKING 変更_ - `Request.accept` 機能をよりパフォーマンスと仕様準拠に更新します。 + +`Accessor に `Accept`ヘッダを解析する機能が改善されました。 このプロパティを使用していて、その等価性操作に頼っていた場合、これは変更されました。 おそらく、`request.accept.match()\` メソッドを使用する必要があります。 + +### プロパティとして任意のヘッダーにアクセス + +ヘッダーへのアクセスを簡単にするために、ヘッダーの未処理バージョンにプロパティとしてアクセスできます。 ヘッダーの名前は、すべての小文字のプロパティの名前で、ハイフン(`-`)をアンダースコア(`_`)に切り替えます。 + +例: + +.. 列:: + +```` +``` +GET /foo/bar HTTP/1.1 +Host: localhost +User-Agent: curl/7.88.1 +X-Request-ID: 123ABC +``` +```` + +.. 列:: + +```` +```py +request.headers.host +request.headers.user_agent +request.headers.x_request_id +``` +```` + +### デフォルトで `DELETE` ボディを消費する + +デフォルトでは、 `DELETE` リクエストの本文が使用され、 `Request` オブジェクトに読み込まれるようになりました。 `POST`、`PUT`、`PATCH`リクエストのように、 `body`を利用できるようにします。 + +### `SSLContext`を作成することを直接制御するためのカスタム`CertLoader` + +独自の `SSLContext` オブジェクトを作成したい場合もあります。 これを行うには、目的のコンテキストオブジェクトを生成する `CertLoader` のサブクラスを作成できます。 + +```python +from sanic.worker.loader import CertLoader + +class MyCertLoader(CertLoader): + def load(self, app: Sanic) -> SSLContext: + ... + +app = Sanic(..., certloader_class=MyCertLoader) +``` + +### 非推奨と削除 + +1. _DEPRECATED_ - Dict-style cookieの設定 +2. _DEPRECATED_ - JSON エラーフォーマッタを使用する場合、1つの要因に対するリクエストに JSON データの存在を使用する +3. _削除_ - 非推奨の`__blueprintname__` プロパティ +4. _削除_ - ルート名の重複 +5. _削除_ - 重複した例外ハンドラーの定義 +6. _削除_ - 旗付きインスペクタCLI +7. _削除_ - レガシーサーバー (`sanic.server.serve_single` と `sanic.server.serve_multiple` を含む) +8. _REMOVED_ - バイト文字列付きのディレクトリを提供します +9. _削除_ - `Request.request_middleware_started` +10. _削除_ - `Websocket.connection` + +#### ルート名が重複している場合はもう許可されていません + +バージョン22.9では、Sanicはv23.3がルートを重複した名前で登録できるようにすることを発表しました。 次のエラーが表示された場合は、その変更によるものです。 + +> sanic.exceptions.ServerError: ルート名が重複しています: SomeApp.some_handler。 `name` パラメータを使用することで、名前を変更する必要があります。 クラスと関数名から派生した暗黙的な名前を変更することもできます。 詳細は https://sanic.dev/ja/guide/release-notes/v23.3.html#duplicated-route-names-are-no-longer-allowed を参照してください。 + +これを見ている場合は、ルートに明示的な名前を使用することをオプトインする必要があります。 + +.. 列:: + +```` +**BAD** +```python +app = Sanic("SomeApp") + +@app.get("/") +@app.get("/foo") +async def handler(request: Request): +``` +```` + +.. 列:: + +```` +**GOOD** +```python +app = Sanic("SomeApp") + +@app.get("/", name="root") +@app.get("/foo", name="foo") +async def handler(request: Request): +``` +```` + +#### レスポンスクッキー + +レスポンスCookieは互換性の目的でのみ「dict」として機能します。 バージョン 24.3 では、すべての `dict` メソッドは削除され、レスポンクッキーはオブジェクトのみになります。 + +したがって、クッキーのプロパティを設定するためにこのパターンを使用している場合は、バージョン24.3以前にアップグレードする必要があります。 + +```python +resp = HTTPResponse() +resp.cookies["foo"] = "bar" +resp.cookies["foo"]["httponly"] = True +``` + +代わりに、`add_cookie`メソッドを使用する必要があります。 + +```python +resp = HTTPResponse() +resp.add_cookie("foo", "bar", httponly=True) +``` + +#### Cookie をリクエスト + +Sanicは、RFC 仕様に準拠するために重複した Cookie キーを読み取るサポートを追加しました。 後方互換性を維持するために、`__getitem__`を使用してクッキーの値にアクセスすると、送信された最初の値を取得するために動作します。 したがって、バージョン23.3以前のバージョンでは、これは `True` になります。 + +```python +assert request.cookies["foo"] == "bar" +assert request.cookies.get("foo") == "bar" +``` + +バージョン 23.3 が `getlist` を追加しました + +```python +assert request.cookies.getlist("foo") == ["bar"] +``` + +上記のように、他のリクエストプロパティ(`request.args`、`request.form`など)にあるように、`get`と`getlist`メソッドが利用できます。 v24.3 以降、クッキーの `__getitem__` メソッドはこれらのプロパティとまったく同じように動作します。 これは、`__getitem__`が値のリストを返すことを意味します。 + +したがって、この機能を使用して1つの値だけを返す場合は、v24.3の前に以下のパターンにアップグレードしてください。 + +```python +assert request.cookies["foo"] == ["bar"] +assert request.cookies.get("foo") == "bar" +assert request.cookies.getlist("foo") == ["bar"] +``` + +## ありがとうございます + +このリリースに参加いただき、ありがとうございます: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@deounix](https://github.com/deounix) +[@Kludex](https://github.com/Kludex) +[@mbendiksen](https://github.com/mbendiksen) +[@prryplatypus](https://github.com/prryplatypus) +[@r0x0d](https://github.com/r0x0d) +[@SaidBySolo](https://github.com/SaidBySolo) +[@sjsadowski](https://github.com/sjsadowski) +[@stricaud](https://github.com/stricaud) +[@Tracyca209](https://github.com/Tracyca209) +[@Tronic](https://github.com/Tronic) + +--- + +あなたがプロジェクトを楽しんでいるなら、貢献を検討してください。 もちろん私たちはコード貢献は大好きですが、どんな形でも貢献できます。 ドキュメントを書いたり、ユースケースを示したり、会話に参加したり、声を知らせたりすることを検討してください。format@@0(https://opencollective.com/sanic-org/) diff --git a/guide/content/ja/release-notes/2023/v23.6.md b/guide/content/ja/release-notes/2023/v23.6.md new file mode 100644 index 0000000000..152329d4ba --- /dev/null +++ b/guide/content/ja/release-notes/2023/v23.6.md @@ -0,0 +1,199 @@ +--- +title: バージョン23.6 +--- + +# バージョン23.6 + +.. TOC:: + +## はじめに + +これはバージョン23のformat@@0(../../org/policies.md#release-schedule)の2番目のリリースです。 何か問題が発生した場合は、 [GitHub](https://github.com/sanic-org/sanic/issues/new/choose) にご注意ください。 + +## 知っておくべきこと + +詳細は [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html) をご覧ください。 注目すべき新機能や破損した機能、そしてアップグレードする機能... + +### Python 3.7 のサポートを削除 + +Python 3.7 は 2023-06-27 に予定されている上流の終了点に到達する予定です。 Sanic は Python 3.7 のサポートを廃止しており、Python 3.8 以降が必要です。 + +[#2777](https://github.com/sanic-org/sanic/pull/2777)を参照してください。 + +### Pypy 互換性の問題を解決する + +`os`モジュールにもう一度、SanicがPyPyPyで動くようにするための小さなパッチが追加されました。 この回避策は、欠けている `readlink` 関数 (PyPy `os` モジュールに欠けている) を `os.path.realpath` 関数と置き換えます。 + +[#2782](https://github.com/sanic-org/sanic/pull/2782)を参照してください。 + +### 設定とctxオブジェクトにカスタム入力を追加する + +`sanic.Sanic`と`sanic.Request`オブジェクトは、`config`と`ctx`オブジェクトを完全に入力するのに便利な一般的な型になりました。 + +最もシンプルな形式では、 `Sanic` オブジェクトは次のように入力されます。 + +```python +from sanic import Sanic +app = Sanic("test") +learm_type(app) # N: Revealed type is "sanic.app.Sanic[sanic.config.Config, types.SimpleNamespace]" +``` + +.. tip:: メモ + +``` +注意してください。一般的な型を使用するための *no* 要件がありません。デフォルトの型は `sanic.config.Config` と `types.SimpleNamespace` です。 この新機能は、既存の`app: Sanic`と`request: Request`を使いたい人のためのオプションにすぎません。 +``` + +これで、ジェネリクスではありますが、完全に `app.config`、`app.ctx`、および `request.ctx`オブジェクトを持つことができます。 これにより、IDEの自動補完ツールとの統合が改善され、開発者エクスペリエンスが向上します。 + +```python +from sanic import Request, Sanic +from sanic.config import Config + +class CustomConfig(Config): + pass + +class Foo: + pass + +class RequestContext: + foo: Foo + +class CustomRequest(Request[Sanic[CustomConfig, Foo], RequestContext]): + @staticmethod + def make_context() -> RequestContext: + ctx = RequestContext() + ctx.foo = Foo() + return ctx + +app = Sanic( + "test", config=CustomConfig(), ctx=Foo(), request_class=CustomRequest +) + +@app.get("/") +async def handler(request: CustomRequest): + ... +``` + +副作用として、`request.tx`は遅延初期化されています。`request.tx`が使用されていない場合のオーバーヘッドを減らす必要があります。 + +上記のスニペットでもう一つの変更点は、 `make_context` メソッドです。 この新しいメソッドは、カスタム `Request` 型で、Sanic がしばらくの間カスタムアプリケーションコンテキストオブジェクトを許可しているのと同様に、 `SimpleNamespace` とは異なるオブジェクトを注入するために使用できます。 + +詳細については、[custom typed application](../basics/app.md#custom-typed-application) および [custom typed request](../basics/app.md#custom-typed-request) を参照してください。 + +[#2785](https://github.com/sanic-org/sanic/pull/2785)を参照してください。 + +### 普遍的な例外信号 + +`"server.exception.reporting"`というサーバーの実行中に発生した**すべての**例外に新しい例外シグナルが追加されました。 これは、任意の例外が発生し、独自のタスクとして発信される普遍的な信号です。 つまり、リクエストハンドラは_ブロックせず_、ミドルウェアは_影響を受けません_。 + +これはリクエストハンドラの外で発生する可能性のある例外をキャッチする場合に便利です (シグナルなど)。 またはバックグラウンドタスクで)、一貫性のあるエラー処理エクスペリエンスをユーザーに作成するために使用することを目的としています。 + +```python +from sanic.signals import Event + +@app.signal(Event.SERVER_LIFECYCLE_EXCEPTION) +async def catch_any_exception(app: Sanic, exception: Exception): + app.ctx.my_error_reporter_utility.error(exception) +``` + +このパターンは、新しいデコレーター `@app.report_exception` で簡略化できます。 + +```python +@app.report_exception +async def catch_any_exception(app: Sanic, exception: Exception): + print("Caught exception:) +``` + +これはバックグラウンドタスクで発生し、エラー応答の操作は**しない**と指摘する必要があります。 これは、アプリケーションエラーが発生したときにトリガーされるレポート、ログ、またはその他の目的のためのものです。 + +[#2724](https://github.com/sanic-org/sanic/pull/2724)と[#2792](https://github.com/sanic-org/sanic/pull/2792)を参照してください。 + +### BPグループの前に名前を追加 + +Sanicはしばらくの間、ルート名の重複に関する警告を表示しており、 [v23.3](https://sanic.dev/ja/guide/release-notes/v23.3.html#deprecations-and-removals)でルート名の一意性を強制するようになりました。 これによりブループリントの組成が複雑になりました。 + +ブループリントグループの新しい名前プレフィックスパラメータが追加されました。 設計図やグループの入れ子を作ることで、それらを構成可能にします。 + +このスニペットに示すように、新しいパラメータ`name_prefix`が追加されました。 + +```python +bp1 = Blueprint("bp1", url_prefix="/bp1") +bp2 = Blueprint("bp2", url_prefix="/bp2") + +bp1.add_route(lambda _: ..., "/", name="route1") +bp2.add_route(lambda _: ..., "/", name="route2") + +group_a = Blueprint.group( + bp1, bp2, url_prefix="/group-a", name_prefix="group-a" +) +group_b = Blueprint.group( + bp1, bp2, url_prefix="/group-b", name_prefix="group-b" +) + +app = Sanic("TestApp") +app.blueprint(group_a) +app.blueprint(group_b) +``` + +構築されるルートは以下のようになります。 + +- `TestApp.group-a_bp1.route1` +- `TestApp.group-a_bp2.route2` +- `TestApp.group-b_bp1.route1` +- `TestApp.group-b_bp2.route2` + +[#2727](https://github.com/sanic-org/sanic/pull/272727)をご覧ください。 + +### `request.client_ip`を追加 + +Sanicはローカルとプロキシの両方のデータからクライアントのIPアドレスを提供する新しいaccessorである`request.client_ip`を導入しました。 インターネット上またはプロキシの背後で直接アプリケーションを実行できます。 これは `request.remote_addr や request.ip` と同じで、アプリケーションのデプロイ方法に関係なくクライアントの IP を提供します。 + +[#2790](https://github.com/sanic-org/sanic/pull/2790)を参照してください。 + +### デフォルトの `KEEP_ALIVE_TIMEOUT` を 120 秒に増やします。 + +デフォルトの `KEEP_ALIVE_TIMEOUT` 値が 5 秒から 120 秒に変更されました。 もちろんまだ設定可能ですが、この変更により、長いレイテンシ接続のパフォーマンスが向上するはずです。 再接続は高価で、一般的なユーザーフロー閲覧ページに5秒間隔以上適合します。 + +Sanicは、アイドル接続を迅速に閉じるために歴史的に5秒のタイムアウトを使用しています。 選択した値の**120秒**は確かにNginxのデフォルト値75より大きく、Caddyサーバーがデフォルトで持っている値と同じです。 + +[#2531](https://github.com/sanic-org/sanic/issues/2531)と +[#2681](https://github.com/sanic-org/sanic/issues/2681)に関連しています。 + +[#2670](https://github.com/sanic-org/sanic/pull/2670)を参照してください。 + +### マルチプロセッシング開始メソッドを早期に設定 + +Python が `multiprocessing` をどのように扱うかによって、 同期プリミティブを適切に作成する方法を一部のユーザが混乱させるかもしれません。 これは、Sanic が `multiprocessing` コンテキストを作成する方法によるものです。 この変更は、作成された任意のプリミティブが正しいコンテキストに適切にアタッチされるように、start メソッドを早期に設定します。 + +ほとんどのユーザーにとって、これは顕著であり、影響を与えるべきではありません。 しかし、このようなものを作りやすく、期待どおりに動作させるべきです。 + +```python +from multiprocessing import Queue + +@app.main_process_start +async def main_process_start(app): + app.shared_ctx.queue = Queue() +``` + +[#2776](https://github.com/sanic-org/sanic/pull/2776)を参照してください。 + +## ありがとうございます + +このリリースに参加いただき、ありがとうございます: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@chuckds](https://github.com/chuckds) +[@deounix](https://github.com/deounix) +[@guacs](https://github.com/guacs) +[@liamcoatman](https://github.com/liamcoatman) +[@moshe742](https://github.com/moshe742) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@Thirumalai](https://github.com/Thirumalai) +[@Tronic](https://github.com/Tronic) + +--- + +あなたがプロジェクトを楽しんでいるなら、貢献を検討してください。 もちろん私たちはコード貢献は大好きですが、どんな形でも貢献できます。 ドキュメントを書いたり、ユースケースを示したり、会話に参加したり、声を知らせたりすることを検討してください。format@@0(https://opencollective.com/sanic-org/) diff --git a/guide/content/ja/release-notes/2023/v23.9.md b/guide/content/ja/release-notes/2023/v23.9.md new file mode 100644 index 0000000000..c4cdc12904 --- /dev/null +++ b/guide/content/ja/release-notes/2023/v23.9.md @@ -0,0 +1,7 @@ +--- +title: バージョン 23.9 +--- + +# バージョン 23.9 + +_当時の状況により、v.23.9はスキップされました。 _ diff --git a/guide/content/ja/release-notes/2024/v24.12.md b/guide/content/ja/release-notes/2024/v24.12.md new file mode 100644 index 0000000000..039a3ae84c --- /dev/null +++ b/guide/content/ja/release-notes/2024/v24.12.md @@ -0,0 +1,80 @@ +--- +title: バージョン 24.12 +--- + +# バージョン 24.12 + +.. TOC:: + +## はじめに + +これはバージョン24format@@0(../../organization/policyes.md#release-schedule)の最初のリリースです。 v24のリリースケイデンスは、数年前から若干変更されている可能性があります。 最新のアップデートについては、Discordサーバーの最新情報を確認してください。 何か問題が発生した場合は、 [GitHub](https://github.com/sanic-org/sanic/issues/new/choose) にご注意ください。 + +## 知っておくべきこと + +[Changelog](../changelog.html) の詳細。 注目すべき新機能や破損した機能、アップグレードする機能: + +### 👶 _BETA_ カスタムCLIコマンド + +`sanic` CLI ユーティリティを使用すると、カスタムコマンドを呼び出すことができます。 コマンドは、以下のデコレータ構文を使用して追加できます。 + +```python +@app.command +async def foo(one, two: str, three: str = "..."): + logger.info(f"FOO {one=} {two=} {three=}") + + +@app.command +def bar(): + logger.info("BAR") + + +@app.command(name="qqq") +async def baz(): + logger.info("BAZ") +``` + +これらは `exec` コマンドを使用して呼び出されます。 + +```sh +sanic server:app exec [--arg=value] +``` + +関数の署名の引数は、引数として追加されます。 例: + +```sh +sanic server:app exec command --one=1 --two=2 --three=3 +``` + +.. 警告:: + +``` +This is in **BETA** and the functionality is subject to change in upcoming versions. +``` + +### Python 3.13 のサポートを追加 + +サポートされているバージョンに Python 3.13 を追加しました。 + +### Python 3.8 のサポートを削除 + +Python 3.8 は寿命の終わりに達しました。 Sanic は Python 3.8 のサポートを廃止しており、Python 3.9 以降が必要です。 + +### 古いレスポンスCookieアクセサリが削除されました + +v23 より前に、`Response` オブジェクトの Cookie が設定され、辞書オブジェクトとしてアクセスされました。 これは v23.3 で廃止され、新しい format@@0(../2023/v23.3.html#more-convenient-methods-for-setting-and-deleting-cookies) が追加されました。 古いパターンは削除されました。 + +## ありがとうございます + +このリリースに参加いただき、ありがとうございます: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@C5H12O5](https://github.com/C5H12O5) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@HyperKiko](https://github.com/HyperKiko) +[@imnotjames](https://github.com/imnotes) +[@pygithub.com/pygithub.com/pygithub.com/pygeek) + +--- + +あなたがプロジェクトを楽しんでいるなら、貢献を検討してください。 もちろん私たちはコード貢献は大好きですが、どんな形でも貢献できます。 ドキュメントを書いたり、ユースケースを示したり、会話に参加したり、声を知らせたりすることを検討してください。format@@0(https://opencollective.com/sanic-org/) diff --git a/guide/content/ja/release-notes/2024/v24.6.md b/guide/content/ja/release-notes/2024/v24.6.md new file mode 100644 index 0000000000..9a2a91027e --- /dev/null +++ b/guide/content/ja/release-notes/2024/v24.6.md @@ -0,0 +1,135 @@ +--- +title: バージョン 24.6 +--- + +# バージョン 24.6 + +.. TOC:: + +## はじめに + +これはバージョン24format@@0(../../organization/policyes.md#release-schedule)の最初のリリースです。 v24のリリースケイデンスは、数年前から若干変更されている可能性があります。 最新のアップデートについては、Discordサーバーの最新情報を確認してください。 何か問題が発生した場合は、 [GitHub](https://github.com/sanic-org/sanic/issues/new/choose) にご注意ください。 + +## 知っておくべきこと + +[Changelog](../changelog.html) の詳細。 注目すべき新機能や破損した機能、アップグレードする機能: + +### ログの改善 + +既定のログパターンは、ターミナルセッションからの閲覧時により開発者にやさしいものにするためにクリーンアップされています。 これには色の使用とあまり冗長な書式設定が含まれます。 + +SanicはサーバがDEBUGモードかどうかに応じて2つのわずかなバリエーションを選択します。 使用することで、常に色を削除することを選択できます。 + +```python +app.config.NO_COLOR = True +``` + +TTY端末ではなくログから色が自動的に取り除かれます。 + +Sanicは`sanic.logging.formatter.AutoFormatter` と `sanic.logging.formatter.AutoAccessFormatter` を使用して、DEBUGとPRODフォーマッタを自動的に切り替えます。 もちろん、適切な名前のフォーマッタを使用して、1つのバージョンまたは他のバージョンを強制することができます + +#### DEBUGモード中 + +```python +sanic.logging.formatter.DebugFormatter +sanic.logging.formatter.DebugAccessFormatter +``` + +![](/assets/images/logging-dev.png) + +#### PRODモード + +```python +sanic.logging.formatter.ProdFormatter +sanic.logging.formatter.ProdAccessFormatter +``` + +![](/assets/images/logging-prod.png) + +#### Legacy + +古い形式のログを好む場合は、ログフォーマッタとして保存されています: `sanic.logging.formatter.LegacyFormatter.LegacyAccessFormatter.LegacyAccessFormatter` 。 + +これらのフォーマッタを実装する一つの方法: + +```python +from sanic.log import LOGGING_CONFIG_DEFAULTS + +LOGGING_CONFIG_DEFAULTS["formatters"] = { + "generic": { + "class": "sanic.logging.formatter.LegacyFormatter" + }, + "access": { + "class": "sanic.logging.formatter.LegacyAccessFormatter" + }, +} +``` + +#### 新しいJSONフォーマット + +また、他の第三部ロギングプラットフォームとの統合のために、ログを JSON 形式で出力する新しい JSON ログフォーマッタもあります。 + +```python +from sanic.log import LOGGING_CONFIG_DEFAULTS + +LOGGING_CONFIG_DEFAULTS["formatters"] = { + "generic": { + "class": "sanic.logging.formatter.JSONFormatter" + }, + "access": { + "class": "sanic.logging.formatter.JSONAccessFormatter" + }, +} +``` + +### Unix ソケットでパスを使用する + +サーバに Unix ソケットを作成するときに、文字列ベースのパスの代わりに `pathlib.Path` オブジェクトを渡すことでそれを実行できるようになりました。 + +### カスタムルート名 + +`generate_name`メソッドは、カスタムの`Sanic`または`Blueprint`のいずれかで上書きできます。 これにより、任意にルート名を変更することができます。 + +```python +from sanic import Sanic, text, + +class Custom(Sanic): + def generate_name(self, *objects): + existing = self._generate_name(*objects) + return existing.upper() + +app = Sanic("Foo") + +@app.get("/") +async def handler(request): + return text(request.name) # FOO.HANDLER + + +return app +``` + +### 🚨 BREAKING 変更点 + +1. `Request.cookies.getlist` は常に`list`を返します。 これは、 `key` の cookie が存在しない場合、 `None` の代わりに `list` になります。 既存の動作を保持するには、`Request.cookies.getlist("something", None)`を使用してください。 + +## ありがとうございます + +このリリースに参加いただき、ありがとうございます: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@ashleysommer](https://github.com/ashleysommer) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@DeJayDev](https://github.com/DeJayDev) +[@ekzhang](https://github.com/ekzhang) +[@Huy-Ngo](https://github.com/Huy-Ngo) +[@iAndriy](https://github.com/iAndriy) +[@jakkaz](https://github.com/jakkaz) +[@Nano112](https://github.com/Nano112) +[@prryplatypus](https://github.com/prryplatypus) +[@razodactyl](https://github.com/razodactyl) +[@Tronic](https://github.com/Tronic) +[@wieczorek1990](https://github.com/wieczorek1990) + +--- + +あなたがプロジェクトを楽しんでいるなら、貢献を検討してください。 もちろん私たちはコード貢献は大好きですが、どんな形でも貢献できます。 ドキュメントを書いたり、ユースケースを示したり、会話に参加したり、声を知らせたりすることを検討してください。format@@0(https://opencollective.com/sanic-org/) diff --git a/guide/content/ja/release-notes/changelog.md b/guide/content/ja/release-notes/changelog.md new file mode 100644 index 0000000000..af4a6c37ff --- /dev/null +++ b/guide/content/ja/release-notes/changelog.md @@ -0,0 +1,1599 @@ +--- +content_class: 更新履歴 +--- + +# 更新履歴 + +🔶 現在のリリース\ +🔷 LTS リリース + +## バージョン24.12.0 🔶🔷 + +_現在のバージョン_ + +### 特徴 + +- [#3019](https://github.com/sanic-org/sanic/pull/3019) `sanic` CLI にカスタムコマンドを追加する + +### バグ修正 + +- [#2992](https://github.com/sanic-org/sanic/pull/2992) `mixins.startup.serve` UnboundLocalError を修正 +- [#3000](https://github.com/sanic-org/sanic/pull/3000) `dumps`呼び出し可能な戻り値タイプの`bytes`に対する`JSONResponse`メソッドの型迷惑を修正しました +- [#3009](https://github.com/sanic-org/sanic/pull/3009) `False`に設定すると、`SanicException.quiet`属性の処理を修正します。 +- [#3014](https://github.com/sanic-org/sanic/pull/3014) 入力内容をクリーンアップする +- [#3015](https://github.com/sanic-org/sanic/pull/3015) 該当する場合、プロセスグループ全体を倒す +- [#3016](https://github.com/sanic-org/sanic/pull/3016) HTTPMethodView クラスで get メソッドの互換性のない型注釈を修正しました + +### 非推奨と削除 + +- [#3020](https://github.com/sanic-org/sanic/pull/3020) Python 3.8 サポートを削除 + +### 開発者のインフラストラクチャ + +- [#3017](https://github.com/sanic-org/sanic/pull/3017) Cleanup setup.cfg + +### ドキュメントの改善 + +- [#3007](https://github.com/sanic-org/sanic/pull/3007) `sanic-ext`のドキュメントでtypoを修正する + +## バージョン 24.6.0 + +### 特徴 + +- [#2838](https://github.com/sanic-org/sanic/pull/2838) リクエストクッキー「getlist」を簡素化する +- [#2850](https://github.com/sanic-org/sanic/pull/2850) Unix ソケットは `pathlib.Path` を使用できるようになりました +- [#2931](https://github.com/sanic-org/sanic/pull/2931) [#2958](https://github.com/sanic-org/sanic/pull/2958) ログの改善点 +- [#2947](https://github.com/sanic-org/sanic/pull/2947) .message フィールドを空ではない例外にする +- [#2961](https://github.com/sanic-org/sanic/pull/2961) [#2964](https://github.com/sanic-org/sanic/pull/2964) カスタム名生成を許可する + +### バグ修正 + +- [#2919](https://github.com/sanic-org/sanic/pull/2919) websockets で非推奨の通知を削除する +- [#2937](https://github.com/sanic-org/sanic/pull/2937) ASGIモードで応答ストリーミングエラーを解決する +- [#2959](https://github.com/sanic-org/sanic/pull/2959) Python 3.12 deprecation notic を解決する +- [#2960](https://github.com/sanic-org/sanic/pull/2960) 騒々しい例外に対する適切な意図を確認する +- [#2970](https://github.com/sanic-org/sanic/pull/2970) [#2978](https://github.com/sanic-org/sanic/pull/2978) 3.12 の不足している依存関係を修正します。 +- [#2971](https://github.com/sanic-org/sanic/pull/2971) ミドルウェアの例外が見つからないルートでエラーが発生した問題を修正しました。 +- [#2973](https://github.com/sanic-org/sanic/pull/2973) `transport.close`と`transport.abort`のchedulingロジックを解決します。 +- [#2976](https://github.com/sanic-org/sanic/pull/2976) `secure=False`で作成されたクッキーの削除を修正 +- [#2979](https://github.com/sanic-org/sanic/pull/2979) 体長が悪い場合にエラーを投げます +- [#2980](https://github.com/sanic-org/sanic/pull/2980) ボディエンコーディングの不良時にエラーを投げる。 + +### 非推奨と削除 + +- [#2899](https://github.com/sanic-org/sanic/pull/2899) HTTPX が影響を受けない環境の REPL から誤った行を削除します +- [#2962](https://github.com/sanic-org/sanic/pull/2962) マージエンティティヘッダーの削除 + +### 開発者のインフラストラクチャ + +- [#2882](https://github.com/sanic-org/sanic/pull/2882) [#2896](https://github.com/sanic-org/sanic/pull/2896) ポート選択によるテストを改善するために動的なポートフィクスチャーを適用する +- [#2887](https://github.com/sanic-org/sanic/pull/2887) docker image builds の更新 +- [#2932](https://github.com/sanic-org/sanic/pull/2932) Ruff を使ってコードベースをクリーンアップする + +### ドキュメントの改善 + +- [#2924](https://github.com/sanic-org/sanic/pull/2924) html5tagger page +- [#2930](https://github.com/sanic-org/sanic/pull/2930) Sanic Extensions README.md +- [#2934](https://github.com/sanic-org/sanic/pull/2934) ヘルスチェック文書に文脈を追加する +- [#2936](https://github.com/sanic-org/sanic/pull/2936) ワーカーマネージャーのドキュメントを改善する +- [#2955](https://github.com/sanic-org/sanic/pull/2955) `request.md`のフォーマットが間違っているのを修正しました + +## バージョン23.12.0 🔷 + +### 特徴 + +- [#2775](https://github.com/sanic-org/sanic/pull/2775) 任意のプロセスを開始して再起動する +- [#2811](https://github.com/sanic-org/sanic/pull/2811) シャットダウン時のクリーナープロセス管理 +- [#2812](https://github.com/sanic-org/sanic/pull/2812) オープンなウェブソケットでタスクのトレースバックを抑制する +- [#2822](https://github.com/sanic-org/sanic/pull/2822) リスナーとシグナルの優先順位付け +- [#2831](https://github.com/sanic-org/sanic/pull/2831) メモリ消費量を削減 +- [#2837](https://github.com/sanic-org/sanic/pull/2837) ベアクッキーを受け入れます +- [#2841](https://github.com/sanic-org/sanic/pull/2841) `websocket.handler.` signers を追加する +- [#2805](https://github.com/sanic-org/sanic/pull/2805) 変更されたファイルを追加してトリガーリスナーをリロードする +- [#2813](https://github.com/sanic-org/sanic/pull/2813) シンプルなシグナルを許可する +- [#2827](https://github.com/sanic-org/sanic/pull/2827) `Sanic.event()`の機能性と一貫性を向上させます +- [#2851](https://github.com/sanic-org/sanic/pull/2851) 1 バイトの範囲リクエストを許可する +- [#2854](https://github.com/sanic-org/sanic/pull/2854) ウェブソケットリクエストに適した`Request.scheme` を改善 +- [#2858](https://github.com/sanic-org/sanic/pull/2858) Sanic `Request`をWebsockets `Request`にハンドシェイク変換する +- [#2859](https://github.com/sanic-org/sanic/pull/2859) `sanic` CLI に REPL を追加します +- [#2870](https://github.com/sanic-org/sanic/pull/2870) Python 3.12 サポートを追加 +- [#2875](https://github.com/sanic-org/sanic/pull/2875) マルチプロセッシングコンテキストの競合時の例外の改善 + +### バグ修正 + +- [#2803](https://github.com/sanic-org/sanic/pull/2803) MOTD の追加データ表示を修正する + +### 開発者のインフラストラクチャ + +- [#2796](https://github.com/sanic-org/sanic/pull/2796) ユニットテストケースをリファクタリングする +- [#2801](https://github.com/sanic-org/sanic/pull/2801) CPUが1つだけの場合、`test_fast`を修正する +- [#2807](https://github.com/sanic-org/sanic/pull/2807) autocsum の制約を追加する (lint issum in old package version) +- [#2808](https://github.com/sanic-org/sanic/pull/2808) GitHubアクションをリファクタリングする +- [#2814](https://github.com/sanic-org/sanic/pull/2814) git push で CI パイプラインを実行 +- [#2846](https://github.com/sanic-org/sanic/pull/2846) 古いパフォーマンステスト/ベンチマーク +- [#2848](https://github.com/sanic-org/sanic/pull/2848) Makefile cleanup +- [#2865](https://github.com/sanic-org/sanic/pull/2865) + [#2869](https://github.com/sanic-org/sanic/pull/2869) + [#2872](https://github.com/sanic-org/sanic/pull/2872) + [#2879](https://github.com/sanic-org/sanic/pull/2879) + ツールチェーンにラフを追加 +- [#2866](https://github.com/sanic-org/sanic/pull/2866) alt svc テストが明示的なバッファnbytes でローカルで実行されるように修正する +- [#2877](https://github.com/sanic-org/sanic/pull/2877) Pythonの信頼できるパブリッシャーをデプロイに使用する +- [#2882](https://github.com/sanic-org/sanic/pull/2882) テストスイート内の対象となる場所に動的ポートフィクスチャーを導入する + +### ドキュメントの改善 + +- [#2781](https://github.com/sanic-org/sanic/pull/2781) + [#2821](https://github.com/sanic-org/sanic/pull/2821) + [#2861](https://github.com/sanic-org/sanic/pull/2863) + [#2863](https://github.com/sanic-org/sanic-pull/pull/2781) + User Guide to SHH (Sanic, html5tagger, HTMX) stack +- [#2810](https://github.com/sanic-org/sanic/pull/2810) README を更新 +- [#2855](https://github.com/sanic-org/sanic/pull/2855) Discordバッジを編集する +- [#2864](https://github.com/sanic-org/sanic/pull/2864) http/https リダイレクションドキュメント内で state プロパティを使用するためのドキュメントを調整する + +## バージョン 23.9.0 + +_当時の状況により、v.23.9はスキップされました。 _ + +## バージョン 23.6.0 + +### 特徴 + +- [#2670](https://github.com/sanic-org/sanic/pull/2670) `KEEP_ALIVE_TIMEOUT`のデフォルトを120秒に増やします +- [#2716](https://github.com/sanic-org/sanic/pull/2716) ブループリントにルート上書きオプションを追加する +- [#2724](https://github.com/sanic-org/sanic/pull/2724) および [#2792](https://github.com/sanic-org/sanic/pull/2792) アプリケーションのどこでも発生するすべての例外に新しい例外信号を追加します。 +- [#2727](https://github.com/sanic-org/sanic/pull/2727) BPグループの前に名前を追加 +- [#2754](https://github.com/sanic-org/sanic/pull/2754) ミドルウェアタイプのリクエストタイプを更新 +- [#2770](https://github.com/sanic-org/sanic/pull/2770) 起動時にエラーが発生した場合の例外メッセージの改善 +- [#2776](https://github.com/sanic-org/sanic/pull/2776) マルチプロセッシングスタートメソッドを早期に設定する +- [#2785](https://github.com/sanic-org/sanic/pull/2785) 設定とctxオブジェクトにカスタム入力を追加 +- [#2790](https://github.com/sanic-org/sanic/pull/2790) `request.client_ip` を追加する + +### バグ修正 + +- [#2728](https://github.com/sanic-org/sanic/pull/2728) 意図した結果のトラバーサルを修正 +- [#2729](https://github.com/sanic-org/sanic/pull/2729) ResponseStream コンストラクターの引数が None の場合に処理する +- [#2737](https://github.com/sanic-org/sanic/pull/2737) デフォルトのコンテンツ型の型アノテーションを修正 +- [#2740](https://github.com/sanic-org/sanic/pull/2740) SanicのシリアライザをインスペクターでJSONレスポンスに使用する +- [#2760](https://github.com/sanic-org/sanic/pull/2760) ASGI モードでの `Request.get_current` のサポート +- [#2773](https://github.com/sanic-org/sanic/pull/2773) 明示的にerror_format を定義するためのブループリントルートを無視する +- [#2774](https://github.com/sanic-org/sanic/pull/2774) さまざまなレンダラーでヘッダーを解決する +- [#2782](https://github.com/sanic-org/sanic/pull/2782) pypy の互換性の問題を解決する + +### 非推奨と削除 + +- [#2777](https://github.com/sanic-org/sanic/pull/2777) Python 3.7 サポートを削除 + +### 開発者のインフラストラクチャ + +- [#2766](https://github.com/sanic-org/sanic/pull/2766) Unpin setuptools version +- [#2779](https://github.com/sanic-org/sanic/pull/2779) 有効なポートを取得するために、ループ内のテストを実行してください + +### ドキュメントの改善 + +- [#2741](https://github.com/sanic-org/sanic/pull/2741) Better documentation examples about running Sanic + From that list, the items to highlight in the release notes: + +## バージョン 23.3.0 + +### 特徴 + +- [#2545](https://github.com/sanic-org/sanic/pull/2545) 例外の init を標準化して、例外を使用してHTTPレスポンスの一貫性を高めます。 +- [#2606](https://github.com/sanic-org/sanic/pull/2606) ASGIでもUTF-8としてデコードヘッダーを解読する +- [#2646](https://github.com/sanic-org/sanic/pull/2646) ASGIリクエストと寿命の呼び出しを分離する +- [#2659](https://github.com/sanic-org/sanic/pull/2659) `empty()` を返すハンドラには `FALLBACK_ERROR_FORMAT` を使用してください。 +- [#2662](https://github.com/sanic-org/sanic/pull/2662) 基本的なファイルブラウザー (HTMLページ) と自動インデックスサービス +- [#2667](https://github.com/sanic-org/sanic/pull/2667) Nice traceback formatting (HTML page) +- [#2668](https://github.com/sanic-org/sanic/pull/2668) よりスマートなエラーページレンダリングフォーマットの選択。ヘッダーと「常識」のデフォルトに依存する +- [#2680](https://github.com/sanic-org/sanic/pull/2680) `SHUT_RDWR` でシャットダウンする前にソケットの状態を確認してください。 +- [#2687](https://github.com/sanic-org/sanic/pull/2687) `Request.accept` 機能をよりパフォーマンスと仕様に準拠するように更新します。 +- [#2696](https://github.com/sanic-org/sanic/pull/2696) ヘッダーアクセサをプロパティとして追加 + ``` + Example-Field: Foo, Bar + Example-Field: Baz + ``` + ```python + request.headers.example_field == "Foo, Bar,Baz" + ``` +- [#2700](https://github.com/sanic-org/sanic/pull/2700) Simpler CLI target + + ```sh + $ sanic path.to.module:app # global app instance + $ sanic path.to.module:create_app # factory pattern + $ sanic ./path/to/directory/ # simple serve + ``` +- [#2701](https://github.com/sanic-org/sanic/pull/2701) 管理されたプロセスの多くのワーカーを定義する API +- [#2704](https://github.com/sanic-org/sanic/pull/2704) ルーティングの動的変更に便利な機能を追加 +- [#2706](https://github.com/sanic-org/sanic/pull/2706) クッキーの作成と削除に便利なメソッドを追加する + + ```python + response = text("...") + response.add_cookie("test", "It worked!", domain=".yummy-yummy-cookie.com") + ``` +- [#2707](https://github.com/sanic-org/sanic/pull/2707) `parse_content_header` エスケープを簡素化し、RFCに準拠し、古いFFハックを削除する +- [#2710](https://github.com/sanic-org/sanic/pull/2710) リクエストURLの厳密な文字セットの扱いとescaping +- [#2711](https://github.com/sanic-org/sanic/pull/2711) デフォルトでは `DELETE` の本文を消費します +- [#2719](https://github.com/sanic-org/sanic/pull/2719) `password` を TLS コンテキストに渡すことを許可する +- [#2720](https://github.com/sanic-org/sanic/pull/2720) `RequestCancelled` でミドルウェアをスキップする +- [#2721](https://github.com/sanic-org/sanic/pull/2721) アクセスログのフォーマットを \`%s\`\` に変更する +- [#2722](https://github.com/sanic-org/sanic/pull/2722) `SSLContext` オブジェクトを直接制御するためのアプリケーションオプションとして `CertLoader` を追加します +- [#2725](https://github.com/sanic-org/sanic/pull/2725) レース状態におけるワーカー同期状態 + +### バグ修正 + +- [#2651](https://github.com/sanic-org/sanic/pull/2651) ASGI のウェブソケットをそのままバイトを渡します +- [#2697](https://github.com/sanic-org/sanic/pull/2697) `If-Modified-Since` を使用した場合、datetime aware と naive の比較を`file` で修正しました + +### 非推奨と削除 + +- [#2666](https://github.com/sanic-org/sanic/pull/2666) 非推奨の「**blueprintname**」プロパティを削除します + +### ドキュメントの改善 + +- [#2712](https://github.com/sanic-org/sanic/pull/2712) リダイレクトを作成するために `'https'` を使用した例を改善しました + +## バージョン 22.12.0 + +現在のLTSバージョン_ + +### 特徴 + +- [#2569](https://github.com/sanic-org/sanic/pull/2569) レスポンスオブジェクトを更新する際に便利なメソッドで `JSONResponse` クラスを追加する +- [#2598](https://github.com/sanic-org/sanic/pull/2598) `uvloop`の要件を`>=0.15.0`に変更します +- [#2609](https://github.com/sanic-org/sanic/pull/2609) `websockets` v11.0 との互換性を追加 +- [#2610](https://github.com/sanic-org/sanic/pull/2610) ワーカーエラー時にサーバーを早期にキルする + - デッドロックのタイムアウトを30秒に上げる +- [#2617](https://github.com/sanic-org/sanic/pull/2617) 実行中のサーバーワーカーの数 +- [#2621](https://github.com/sanic-org/sanic/pull/2621) [#2634](https://github.com/sanic-org/sanic/pull/2634) 次の`ctrl+c`に`SIGKILL`を送信してワーカーの出口を強制する +- [#2622](https://github.com/sanic-org/sanic/pull/2622) APIを追加してマルチプレクサからすべてのワーカーを再起動する +- [#2624](https://github.com/sanic-org/sanic/pull/2624) 特別に設定しない限り、全てのサブプロセスの `spawn` をデフォルトに設定します。 + ```python + from sanic import Sanic + + Sanic.start_method = "fork" + ``` +- [#2625](https://github.com/sanic-org/sanic/pull/2625) フォームデータ/マルチパートファイルアップロードの正規化 +- [#2626](https://github.com/sanic-org/sanic/pull/2626) HTTP インスペクタに移動: + - 実行中の Sanic インスタンスを検査するリモートアクセス + - TLSはインスペクターへの暗号化コールに対応しています + - API キーを使用したインスペクタの認証 + - カスタムコマンドでインスペクターを拡張できる機能 +- [#2632](https://github.com/sanic-org/sanic/pull/2632) 再起動操作の順序を制御する +- [#2633](https://github.com/sanic-org/sanic/pull/2633) リロード間隔をクラス変数に移動する +- [#2636](https://github.com/sanic-org/sanic/pull/2636) `register_middleware`メソッドに`priority`を追加します +- [#2639](https://github.com/sanic-org/sanic/pull/2639) `add_route`メソッドに`unquote`を追加します +- [#2640](https://github.com/sanic-org/sanic/pull/2640) ASGI websockets to receive `text` or `bytes` + +### バグ修正 + +- [#2607](https://github.com/sanic-org/sanic/pull/2607) ソケットの再結合を許可する前に強制的にシャットダウンする +- [#2590](https://github.com/sanic-org/sanic/pull/2590) Python 3.11以降では実際の`StrEnum`を使用してください +- [#2615](https://github.com/sanic-org/sanic/pull/2615) ミドルウェアがリクエストタイムアウトごとに1回だけ実行されることを確認する +- [#2627](https://github.com/sanic-org/sanic/pull/2627) 寿命エラーに関するクラッシュASGIアプリケーション +- [#2635](https://github.com/sanic-org/sanic/pull/2635) Windowsで低レベルのサーバー作成でエラーを解決する + +### 非推奨と削除 + +- [#2608](https://github.com/sanic-org/sanic/pull/2608) [#2630](https://github.com/sanic-org/sanic/pull/2630) シグナル条件とトリガーは `signal.extra` に保存されます。 +- [#2626](https://github.com/sanic-org/sanic/pull/2626) HTTPインスペクタに移動 + - 🚨 _BREAKING CHANGE_: インスペクターをカスタムプロトコルを持つシンプルなTCPソケットからSanicアプリに移動します。 + - _DEPRECATE_: `--inspect*` コマンドは非推奨になりました。 +- [#2628](https://github.com/sanic-org/sanic/pull/2628) 非推奨の `distutils.strtobool` を置き換え + +### 開発者のインフラストラクチャ + +- [#2612](https://github.com/sanic-org/sanic/pull/2612) CI testing for Python 3.11 + +## バージョン 22.9.1 + +### 特徴 + +- [#2585](https://github.com/sanic-org/sanic/pull/2585) アプリケーションが登録されていない場合のエラーメッセージを改善しました + +### バグ修正 + +- [#2578](https://github.com/sanic-org/sanic/pull/2578) プロセス証明書作成に証明書ローダーを追加する +- [#2591](https://github.com/sanic-org/sanic/pull/2591) `spawn`互換性のためにsentinel identityを使用しないでください +- [#2592](https://github.com/sanic-org/sanic/pull/2592) 入れ子になったブループリントグループのプロパティを修正 +- [#2595](https://github.com/sanic-org/sanic/pull/2595) 新しいワーカーの再ローダーで睡眠間隔を導入する + +### 非推奨と削除 + +### 開発者のインフラストラクチャ + +- [#2588](https://github.com/sanic-org/sanic/pull/2588) Issue フォームの Markdown テンプレート + +### ドキュメントの改善 + +- [#2556](https://github.com/sanic-org/sanic/pull/2556) v22.9 documentation +- [#2582](https://github.com/sanic-org/sanic/pull/2582) Windows 対応のドキュメントをクリーンアップする + +## バージョン 22.9.0 + +### 特徴 + +- [#2445](https://github.com/sanic-org/sanic/pull/2445) カスタムロード関数を追加 +- [#2490](https://github.com/sanic-org/sanic/pull/2490) `WebsocketImpletocol` async iterable +- [#2499](https://github.com/sanic-org/sanic/pull/2499) Sanic Server WorkerManager のリファクタリング機能 +- [#2506](https://github.com/sanic-org/sanic/pull/2506) パス解像度には`pathlib`を使用してください (静的なファイルを提供する場合) +- [#2508](https://github.com/sanic-org/sanic/pull/2508) `match` の代わりに `path.parts` を使ってください (静的なファイルを提供する場合) +- [#2513](https://github.com/sanic-org/sanic/pull/2513) リクエスト処理をキャンセルする +- [#2516](https://github.com/sanic-org/sanic/pull/2516) HTTP メソッドのリクエストプロパティを追加: + - `request.is_safe` + - `request.is_idempotent` + - `request.is_cacheable` + - _参照_ format@@0(https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) _これらが適用される時の詳細_ +- [#2522](https://github.com/sanic-org/sanic/pull/2522) ASGIに常にサーバーの場所を表示する +- [#2526](https://github.com/sanic-org/sanic/pull/2526) 適切な場合に304を返すための静的ファイルのキャッシュ管理のサポート +- [#2533](https://github.com/sanic-org/sanic/pull/2533) リファクタリング`_static_request_handler` +- [#2540](https://github.com/sanic-org/sanic/pull/2540) ハンドラ実行の前後に信号を追加する + - `http.handler.before` + - `http.handler.after` +- [#2542](https://github.com/sanic-org/sanic/pull/2542) _[redacted]_ to CLI :) +- [#2546](https://github.com/sanic-org/sanic/pull/2546) 非推奨警告フィルタを追加する +- [#2550](https://github.com/sanic-org/sanic/pull/2550) Middleware の優先度とパフォーマンスの強化 + +### バグ修正 + +- [#2495](https://github.com/sanic-org/sanic/pull/2495) 静的なファイルでディレクトリのtraversionを防止する +- [#2515](https://github.com/sanic-org/sanic/pull/2515) ブループリントの特定の静的dirsに二重スラッシュを適用しないでください + +### 非推奨と削除 + +- [#2525](https://github.com/sanic-org/sanic/pull/2525) 重複したルート名の警告はv23.3で完全に防止されます +- [#2537](https://github.com/sanic-org/sanic/pull/2537) 重複した例外に対する警告と非推奨通知はv23.3で完全に防止されます。 + +### 開発者のインフラストラクチャ + +- [#2504](https://github.com/sanic-org/sanic/pull/2504) Cleanup test suite +- [#2505](https://github.com/sanic-org/sanic/pull/2505) コントリビューションドキュメントからサポートされていないPythonバージョン番号を置き換え +- [#2530](https://github.com/sanic-org/sanic/pull/2530) インストールされたパッケージリゾルバーにtestsフォルダを含めないでください + +### ドキュメントの改善 + +- [#2502](https://github.com/sanic-org/sanic/pull/2502) いくつかのタイプミスを修正 +- [#2517](https://github.com/sanic-org/sanic/pull/2517) [#2536](https://github.com/sanic-org/sanic/pull/2536) いくつかのタイプヒントを追加する + +## バージョン 22.6.2 + +### バグ修正 + +- [#2522](https://github.com/sanic-org/sanic/pull/2522) ASGIに常にサーバーの場所を表示する + +## バージョン 22.6.1 + +### バグ修正 + +- [#2477](https://github.com/sanic-org/sanic/pull/2477) フォルダ名が ".." で終わると、Sanic static ディレクトリに失敗します。 + +## バージョン 22.6.0 + +### 特徴 + +- [#2378](https://github.com/sanic-org/sanic/pull/2378) `DEBUG`モードでHTTP/3とTLS証明書の自動生成を導入する + - 👶 _早期にリリースされる機能_: HTTP/3 を超えてサニックにサービスを提供することは早期リリース機能です。 まだHTTP/3 の仕様を完全にカバーしているわけではありませんが、代わりに、Sanic の既存の HTTP/1.1 サーバーとの機能準拠を目指しています。 Websocket、WebTransport、プッシュ応答は、まだ実装されていないいくつかの機能の例です。 + - 📦 _EXTRA要求_: すべてのHTTPクライアントがHTTP/3サーバーとインターフェースできるわけではありません。 [HTTP/3 対応クライアント](https://curl.se/docs/http3.html) をインストールする必要があります。 + - 📦 _EXTRA REQUIREMENT_: TLS の自己生成を使用するには、 [mkcert](https://github.com/FiloSottile/mkcert) または [trustme](https://github.com/python-trio/trustme)のいずれかをインストールする必要があります。 +- [#2416](https://github.com/sanic-org/sanic/pull/2416) `task.cancel`にメッセージを追加する +- [#2420](https://github.com/sanic-org/sanic/pull/2420) 標準のHTTPレスポンスタイプ(`BadRequest`, `MethodNotAllowed`, `RangeNotSatisfiable`)でより一貫した命名をするための例外エイリアスを追加します。 +- [#2432](https://github.com/sanic-org/sanic/pull/2432) `Request`オブジェクトのプロパティとしてASGI `scope`を公開します +- [#2438](https://github.com/sanic-org/sanic/pull/2438) 注釈のWebSocketクラスへの簡単なアクセス: `from sanic import Websocket` +- [#2439](https://github.com/sanic-org/sanic/pull/2439) フォームの値を読み取るための新しいAPI: `Request.get_form` +- [#2445](https://github.com/sanic-org/sanic/pull/2445) カスタムの `loads` 関数を追加する +- [#2447](https://github.com/sanic-org/sanic/pull/2447), [#2486](https://github.com/sanic-org/sanic/pull/2486) キャッシュコントロールヘッダーの設定をサポートする API を改善しました。 +- [#2453](https://github.com/sanic-org/sanic/pull/2453) 詳細フィルタリングをロガーに移動する +- [#2475](https://github.com/sanic-org/sanic/pull/2475) `Request.get_current()` を使って現在のリクエストの取得を行います。 + +### バグ修正 + +- [#2448](https://github.com/sanic-org/sanic/pull/2448) `pythonw.exe`または`sys.stdout`がない場所で実行できるように修正しました +- [#2451](https://github.com/sanic-org/sanic/pull/2451) ASGI モードで `http.lifycle.request` シグナルをトリガーします +- [#2455](https://github.com/sanic-org/sanic/pull/2455) 積み上げられたルート定義のタイピングを解決する +- [#2463](https://github.com/sanic-org/sanic/pull/2463) Python 3.7 の websocket ハンドラーの CancelledError を適切にキャッチします。 + +### 非推奨と削除 + +- [#2487](https://github.com/sanic-org/sanic/pull/2487) v22.6の非推奨と変更 + 1. オプションのアプリケーションレジストリ + 2. 応答の一部が送信された後のカスタムハンドラの実行 + 3. `ErrorHandler`でフォールバックハンドラを設定する + 4. カスタム `LOGO` 設定 + 5. `sanic.response.stream` + 6. `AsyncioServer.init` + +### 開発者のインフラストラクチャ + +- [#2449](https://github.com/sanic-org/sanic/pull/2449) `black`と`isort`の設定をクリーンアップする +- [#2479](https://github.com/sanic-org/sanic/pull/2479) フラッピーテストを修正する + +### ドキュメントの改善 + +- [#2461](https://github.com/sanic-org/sanic/pull/2461) アプリケーションの命名規格に合わせてサンプルを更新する +- [#2466](https://github.com/sanic-org/sanic/pull/2466) `Extend` の型注釈を改善しました +- [#2485](https://github.com/sanic-org/sanic/pull/2485) CLI のヘルプメッセージを改善 + +## バージョン 22.3.0 + +### 特徴 + +- [#2347](https://github.com/sanic-org/sanic/pull/2347) マルチアプリケーション・サーバー用 API + - 🚨 _BREAKING CHANGE_: 古い `sanic.worker.GunicornWorker` が \*\*削除されました。 Sanic を `gunicorn` で実行するには、`uvicorn` format@@0(https://www.uvicorn.org/#running-with-gunicorn) で実行する必要があります。 + - 🧁 _SIDE EFFECT_: 名前付きバックグラウンドタスクがPython 3.7 でもサポートされるようになりました +- [#2357](https://github.com/sanic-org/sanic/pull/2357) `Request.credentials` として `Authorization` ヘッダーを解析 +- [#2361](https://github.com/sanic-org/sanic/pull/2361) 設定オプションを追加して、アプリケーションの起動時に`Touchup`ステップをスキップします +- [#2372](https://github.com/sanic-org/sanic/pull/2372) CLIヘルプメッセージングへのアップデート +- [#2382](https://github.com/sanic-org/sanic/pull/2382) バックウォーターデバッグメッセージに警告をダウングレードする +- [#2396](https://github.com/sanic-org/sanic/pull/2396) `multidict` v0.6を許可する +- [#2401](https://github.com/sanic-org/sanic/pull/2401) アプリケーションの実行タイプに応じてCLIをアップグレード +- [#2402](https://github.com/sanic-org/sanic/pull/2402) CLI引数を工場に条件付きで注入する +- [#2413](https://github.com/sanic-org/sanic/pull/2413) 新しい開始と停止のイベントリスナを再ローダープロセスに追加します +- [#2414](https://github.com/sanic-org/sanic/pull/2414) 必要に応じてループを削除 +- [#2415](https://github.com/sanic-org/sanic/pull/2415) 不正なURL解析に対する例外の改善 +- [sanic-routing#47](https://github.com/sanic-org/sanic-routing/pull/47) Add a new extention parameter type: ``, ``, ``, ``, ``, `` + - 👶 _ベータ機能_: この機能は `path` タイプのマッチングでは動作せず、ベータ機能としてのみリリースされています。 +- [sanic-routing#57](https://github.com/sanic-org/sanic-routing/pull/57) `register_pattern`を`str`または`Pattern`に変更します。 +- [sanic-routing#58](https://github.com/sanic-org/sanic-routing/pull/58) 空でない文字列のみのデフォルトのマッチングと、新しい `strorempty` パターンタイプ + - 🚨 _BREAKING CHANGE_: 以前は動的な文字列パラメータ (`/` または `/`) を持つルートが任意の文字列にマッチします。 空の文字列も含めてね 空でない文字列と**のみ**が一致します。 古い動作を保持するには、新しいパラメータ `/ ` を使用します。 + +### バグ修正 + +- [#2373](https://github.com/sanic-org/sanic/pull/2373) websockets の `error_logger` を削除する +- [#2381](https://github.com/sanic-org/sanic/pull/2381) 新たに割り当てられた `None` をタスクレジストリに修正する +- [sanic-routing#52](https://github.com/sanic-org/sanic-routing/pull/52) regex route matchに型キャストを追加 +- [sanic-routing#60](https://github.com/sanic-org/sanic-routing/pull/60) regex route (ホスト値が異なる複数の静的ディレクトリなど)の要件を追加します。 + +### 非推奨と削除 + +- [#2362](https://github.com/sanic-org/sanic/pull/2362) 22.3 廃止と変更 + 1. `debug=True` と `--debug` do _NOT_ は自動的に `auto_reload` を実行します + 2. デフォルトのエラーレンダリングはプレーンテキストで行われます(ブラウザーは `auto` がヘッダを参照するため、HTML をデフォルトで取得します) + 3. `ErrorHandler.finalize`には`config`が必要です + 4. `ErrorHandler.lookup` には2つの位置引数が必要です + 5. 未使用の websocket プロトコル引数を削除しました +- [#2344](https://github.com/sanic-org/sanic/pull/2344) 小文字の環境変数のロードを非推奨にする + +### 開発者のインフラストラクチャ + +- [#2363](https://github.com/sanic-org/sanic/pull/2363) コードカバレッジをCodecovに戻す +- [#2405](https://github.com/sanic-org/sanic/pull/2405) `sanic-routing`の変更のためのテストのアップグレード +- [sanic-testing#35](https://github.com/sanic-org/sanic-testing/pull/35) Allow for httpx v0.22 + +### ドキュメントの改善 + +- [#2350](https://github.com/sanic-org/sanic/pull/2350) READMEのリンクを修正する +- [#2398](https://github.com/sanic-org/sanic/pull/2398) ドキュメントミドルウェアon_requestとon_response +- [#2409](https://github.com/sanic-org/sanic/pull/2409) `Request.respond`に不足しているドキュメントを追加する + +### その他 + +- [#2376](https://github.com/sanic-org/sanic/pull/2376) `ListenerMixin.listener` の入力を修正 +- [#2383](https://github.com/sanic-org/sanic/pull/2383) `asyncio.wait` で非推奨の警告をクリアする +- [#2387](https://github.com/sanic-org/sanic/pull/2387) Cleanup `__slots__` 実装 +- [#2390](https://github.com/sanic-org/sanic/pull/2390) `asyncio.get_event_loop`で非推奨の警告をクリアする + +## バージョン21.12.1 + +- [#2349](https://github.com/sanic-org/sanic/pull/2349) スタートアップ時にMOTD のみ表示する +- [#2354](https://github.com/sanic-org/sanic/pull/2354) Python 3.7 の名前引数を無視する +- [#2355](https://github.com/sanic-org/sanic/pull/2355) config.update サポートをすべての設定値に追加しました。 + +## バージョン 21.12.0 + +### 特徴 + +- [#2260](https://github.com/sanic-org/sanic/pull/2260) 初期のブループリント登録を許可して、後で追加されたオブジェクトを適用する +- [#2262](https://github.com/sanic-org/sanic/pull/2262) ノイズ例外 - すべての例外を強制的にログ収集する +- [#2264](https://github.com/sanic-org/sanic/pull/2264) オプションの `uvloop` 設定で +- [#2270](https://github.com/sanic-org/sanic/pull/2270) 複数の TLS 証明書を使用した Vhost サポート +- [#2277](https://github.com/sanic-org/sanic/pull/2277) シグナルルーティングを変更して一貫性を高めます。 + - _BREAKING CHANGE_:手動でルーティングしていた場合は、改ざんの変更があります。 シグナルルーターの「get」は100%決定的なものではなくなりました。 返される信号をループさせて、要件を適切にマッチングするための追加のステップが追加されました。 `app.dispatch` や `bp.dispatch` を使ってシグナルがディスパッチされている場合、変更はありません。 +- [#2290](https://github.com/sanic-org/sanic/pull/2290) 文脈例外を追加する +- [#2291](https://github.com/sanic-org/sanic/pull/2291) 参加コンキャットのパフォーマンスを向上させる +- [#2295](https://github.com/sanic-org/sanic/pull/2295), [#2316](https://github.com/sanic-org/sanic/pull/2331), [#2331](https://github.com/sanic-org/sanic/pull/2331) CLIとアプリケーションの状態を再構築し、`app.run`を使用するとコマンドパリティを強化します。 +- [#2302](https://github.com/sanic-org/sanic/pull/2302) 定義時にルートコンテキストを追加 +- [#2304](https://github.com/sanic-org/sanic/pull/2304) バックグラウンドタスクを管理するための名前付きタスクと新しい API +- [#2307](https://github.com/sanic-org/sanic/pull/2307) アプリの自動再読み込みで、変更されたファイルの洞察を提供する +- [#2308](https://github.com/sanic-org/sanic/pull/2308) インストールされている場合は[Sanic Extensions](https://sanicframework.org/en/plugins/sanic-ext/getting-started.html)でアプリケーションを自動的に拡張し、拡張機能にアクセスするためのファーストクラスをサポートします。 +- [#2309](https://github.com/sanic-org/sanic/pull/2309) 組み込み信号が`Enum`に変更されました +- [#2313](https://github.com/sanic-org/sanic/pull/2313) 追加の設定実装ユースケースに対応 +- [#2321](https://github.com/sanic-org/sanic/pull/2321) 環境変数水和ロジックをリファクタリングする +- [#2327](https://github.com/sanic-org/sanic/pull/2327) 単一のリクエストで複数の反応や混合反応を防ぎます +- [#2330](https://github.com/sanic-org/sanic/pull/2330) 環境変数のカスタム型キャスト。 +- [#2332](https://github.com/sanic-org/sanic/pull/2332) すべての非推奨通知を一貫性のあるものにする +- [#2335](https://github.com/sanic-org/sanic/pull/2335) アンダースコアのインスタンス名の開始を許可する + +### バグ修正 + +- [#2273](https://github.com/sanic-org/sanic/pull/2273) `websocket_handshake`を入力して割り当てを変更する +- [#2285](https://github.com/sanic-org/sanic/pull/2285) スタートアップログのIPv6表示を修正 +- [#2299](https://github.com/sanic-org/sanic/pull/2299) 例外ハンドラから `http.lifyle.response` を送信する + +### 非推奨と削除 + +- [#2306](https://github.com/sanic-org/sanic/pull/2306) 非推奨アイテムの削除 + - `Sanic` と `Blueprint` に任意のプロパティが付いていない可能性があります + - `Sanic` と `Blueprint` に準拠した名前が強制されました + - alphanumeric + `_` + `-` + - は文字または`_`で始まる必要があります + - `Sanic`の`load_env`キーワード引数 + - `sanic.exceptions.abort` + - `sanic.views.CompositionView` + - `sanic.response.StreamingHTTPResponse` + - _注意:_ `stream()` レスポンスメソッド (呼び出し可能なストリーミング関数を渡す) は廃止され、v22.6 で削除されます。 新しいスタイルのすべてのストリーミング応答をアップグレードする必要があります: https://sanicframework.org/ja/guide/advanced/streaming.html#response-stream +- [#2320](https://github.com/sanic-org/sanic/pull/2320) Configからエラーハンドラ設定用アプリインスタンスを削除します + +### 開発者のインフラストラクチャ + +- [#2251](https://github.com/sanic-org/sanic/pull/2251) dev install コマンドを変更する +- [#2286](https://github.com/sanic-org/sanic/pull/2286) コード化のしきい値を5から10に変更する +- [#2287](https://github.com/sanic-org/sanic/pull/2287) ホストテスト関数名を上書きしないように更新する +- [#2292](https://github.com/sanic-org/sanic/pull/2292) エラー時に失敗する CI +- [#2311](https://github.com/sanic-org/sanic/pull/2311), [#2324](https://github.com/sanic-org/sanic/pull/2324) PRsのドラフトテストを実行しないでください +- [#2336](https://github.com/sanic-org/sanic/pull/2336) カバレッジチェックからパスを削除 +- [#2338](https://github.com/sanic-org/sanic/pull/2338) テストのポートをクリーンアップする + +### ドキュメントの改善 + +- [#2269](https://github.com/sanic-org/sanic/pull/2269), [#2329](https://github.com/sanic-org/sanic/pull/2333) 型のクリーンアップと言語の修正 + +### その他 + +- [#2257](https://github.com/sanic-org/sanic/pull/2257), [#2294](https://github.com/sanic-org/sanic/pull/22941) [#2341](https://github.com/sanic-org/sanic/pull/2341) Python 3.10 サポートを追加 +- [#2279](https://github.com/sanic-org/sanic/pull/2279), [#2317](https://github.com/sanic-org/sanic/pull/2322) 不足している型の注釈を追加/修正する +- [#2305](https://github.com/sanic-org/sanic/pull/2305) モダンな実装を使用する例を修正しました。 + +## バージョン 21.9.3 + +_クリーンアップされた v21.9.2 の再リリース_ + +## バージョン 21.9.2 + +- [#2268](https://github.com/sanic-org/sanic/pull/2268) HTTP接続をIDLEステージで開始し、遅延やエラーメッセージを回避する +- [#2310](https://github.com/sanic-org/sanic/pull/2310) Post-FALLBACK_ERROR_FORMAT が適用されます。 + +## バージョン21.9.1 + +- [#2259](https://github.com/sanic-org/sanic/pull/2259) 非準拠ErrorHandlers を許可する + +## バージョン 21.9.0 + +### 特徴 + +- [#2158](https://github.com/sanic-org/sanic/pull/2158), [#2248](https://github.com/sanic-org/sanic/pull/2248) I/Oをwebsocketに完全にオーバーホールする +- [#2160](https://github.com/sanic-org/sanic/pull/2160) サーバーに17の新しい信号を追加し、ライフサイクルをリクエストする +- [#2162](https://github.com/sanic-org/sanic/pull/2162) 例外が発生した場合、よりスマートな `auto` フォールバックの書式設定 +- [#2184](https://github.com/sanic-org/sanic/pull/2184) ブループリントをコピーするための実装を導入する +- [#2200](https://github.com/sanic-org/sanic/pull/2200) ヘッダーの解析を許可 +- [#2207](https://github.com/sanic-org/sanic/pull/2207) 利用可能な場合はリモートアドレスを記録する +- [#2209](https://github.com/sanic-org/sanic/pull/2209) BPグループに便利な方法を追加 +- [#2216](https://github.com/sanic-org/sanic/pull/2216) SanicExceptionsにデフォルトのメッセージを追加する +- [#2225](https://github.com/sanic-org/sanic/pull/2225) パスパラメータを持つアノテーションハンドラのタイプアノテーションの利便性。 +- [#2236](https://github.com/sanic-org/sanic/pull/2236) ルートハンドラーからのFalsey(ただしNoneではない)応答を許可する +- [#2238](https://github.com/sanic-org/sanic/pull/2238) ブループリントグループに`exception`デコレータを追加する +- [#2244](https://github.com/sanic-org/sanic/pull/2244) ファイルまたはdir (例: `static(..., resource_type="file")`) +- [#2245](https://github.com/sanic-org/sanic/pull/2245) 接続タスクがキャンセルされたらHTTPループを閉じます。 + +### バグ修正 + +- [#2188](https://github.com/sanic-org/sanic/pull/2188) チャンクされたリクエストの終了を修正する +- [#2195](https://github.com/sanic-org/sanic/pull/2195) 静的リクエスト時の予期しないエラー処理を解決する +- [#2208](https://github.com/sanic-org/sanic/pull/2208) ブループリントベースの例外をより直感的にアタッチしてトリガーさせる。 +- [#2211](https://github.com/sanic-org/sanic/pull/2211) asgi アプリ呼び出しの例外処理を修正。 +- [#2213](https://github.com/sanic-org/sanic/pull/2213) 例外が記録されないバグを修正しました。 +- [#2231](https://github.com/sanic-org/sanic/pull/2231) 戦略的な場所で `abort()` を使用してタスクを終了させ、ダングルソケットを避けます。 +- [#2247](https://github.com/sanic-org/sanic/pull/2247) デバッグモードでの自動リロード状態のロギングを修正する +- [#2246](https://github.com/sanic-org/sanic/pull/2246) BPアカウントの例外ハンドラーですがルートはありません + +### 開発者のインフラストラクチャ + +- [#2194](https://github.com/sanic-org/sanic/pull/2194) 生クライアントのHTTPユニットテスト +- [#2199](https://github.com/sanic-org/sanic/pull/2199) codeclimate に切り替える +- [#2214](https://github.com/sanic-org/sanic/pull/2214) Windows テストを再開してみてください +- [#2229](https://github.com/sanic-org/sanic/pull/2229) `HttpProtocol`をベースクラスにリファクタリングします +- [#2230](https://github.com/sanic-org/sanic/pull/2230) `server.py`をマルチファイルモジュールにリファクタリングします。 + +### その他 + +- [#2173](https://github.com/sanic-org/sanic/pull/2173) 重複した依存関係とPEP 517のサポート +- [#2193](https://github.com/sanic-org/sanic/pull/2193), [#2196](https://github.com/sanic-org/sanic/pull/2196), [#2217](https://github.com/sanic-org/sanic/pull/2217) 型注釈の変更 + +## バージョン 21.6.1 + +**バグ修正** + +- [#2178](https://github.com/sanic-org/sanic/pull/2178) Update + sanic-routing to allow for better splitting of complex URI + templates +- [#2183](https://github.com/sanic-org/sanic/pull/2183) ブロックされたリクエストボディの適切な + 処理でログ内のファントム503を解決する +- [#2181](https://github.com/sanic-org/sanic/pull/2181) 例外ロギングで + 回帰を解決する +- [#2201](https://github.com/sanic-org/sanic/pull/2201) パイプライン化リクエストの + リクエスト情報 + +## バージョン 21.6.0 + +**機能** + +- [#2094](https://github.com/sanic-org/sanic/pull/2094) ハンドラ内でストリームを閉じるために + `response.eof()` メソッドを追加する + +- [#2097](https://github.com/sanic-org/sanic/pull/2097) + 大文字小文字を区別しない HTTP アップグレードヘッダー + +- [#2104](https://github.com/sanic-org/sanic/pull/2104) 明示 + CIMultDictゲッターの使用 + +- [#2109](https://github.com/sanic-org/sanic/pull/2109) 一貫性のある + エラーロガーの使用 + +- [#21114](https://github.com/sanic-org/sanic/pull/2114) 新しい + `client_ip` 接続情報インスタンスへのアクセス + +- [#2119](https://github.com/sanic-org/sanic/pull/2119) `Config` と `Sanic.ctx` のインスタンス化時の代替 + クラス + +- [#2133](https://github.com/sanic-org/sanic/pull/2133) Implement + new version of AST router + + - `alpha` と `string` param + 型を適切に区別します + - `slug` param typeを追加します。例: `` + - 非推奨の``は`` + - 非推奨の``は`` + - Adds a `route.uri` accessor + +- [#2136](https://github.com/sanic-org/sanic/pull/2136) CLI + の改善と新しいオプションのパラメータ + +- [#2137](https://github.com/sanic-org/sanic/pull/2137) URLビルダーに + `version_prefix` を追加する + +- [#2140](https://github.com/sanic-org/sanic/pull/2140) Event + autoregistration with `EVENT_AUTOREGISTER` + +- [#2146](https://github.com/sanic-org/sanic/pull/2146), + [#2147](https://github.com/sanic-org/sanic/pull/2147) Require + stricter names on `Sanic()` and `Blueprint()` + +- [#2150](https://github.com/sanic-org/sanic/pull/2150) Infinitely + reuseable and nestable `Blueprint` and `BlueprintGroup` + +- [#2154](https://github.com/sanic-org/sanic/pull/2154) + `websockets` の依存関係をminバージョンにアップグレード + +- [#2155](https://github.com/sanic-org/sanic/pull/2155) + の最大ヘッダーサイズを増加させる: `REQUEST_MAX_HEADER_SIZE` + +- [#2157](https://github.com/sanic-org/sanic/pull/2157) CLI でアプリ + ファクトリパターンを許可する + +- [#2165](https://github.com/sanic-org/sanic/pull/2165) HTTP + メソッドを列挙に変更する + +- [#2167](https://github.com/sanic-org/sanic/pull/2167) + 追加ディレクトリの自動再読み込みを許可する + +- [#2168](https://github.com/sanic-org/sanic/pull/2168) シンプルな + HTTPサーバーをCLIに追加 + +- [#2170](https://github.com/sanic-org/sanic/pull/2170) 追加の + メソッドで `HTTPMethodView` をアタッチする + +**バグ修正** + +- [#2091](https://github.com/sanic-org/sanic/pull/2091) Fix + `UserWarning` in ASGI mode for missing `__slots__` +- [#2099](https://github.com/sanic-org/sanic/pull/2099) スタティック + リクエストハンドラーのログ例外を404で修正 +- [#2110](https://github.com/sanic-org/sanic/pull/2110) + request.args.pop パラメータを不整合に削除する +- [#2107](https://github.com/sanic-org/sanic/pull/2107) Fix type + hinting for load_env +- [#2127](https://github.com/sanic-org/sanic/pull/2127) + ASGI ws サブプロトコルがリストであることを確認してください +- [#2128](https://github.com/sanic-org/sanic/pull/2128) 問題 + ブループリントの例外ハンドラーが一貫して + 適切なハンドラーにルートされない問題を修正 + +**非推奨と削除** + +- [#2156](https://github.com/sanic-org/sanic/pull/2156) + 設定値 `REQUE_SIZE` を削除する +- [#2170](https://github.com/sanic-org/sanic/pull/2170) + `CompositionView` は非推奨となり、21.12 で削除されるようになりました。 +- [#2172](https://github.com/sanic-org/sanic/pull/2170) + StreamingHTTPResponse + +**開発者のインフラストラクチャ** + +- [#2149](https://github.com/sanic-org/sanic/pull/2149) GitHub Actions を支持する + Travis CI を削除する + +**ドキュメントの改善** + +- [#2164](https://github.com/sanic-org/sanic/pull/2164) + ドキュメントのtypoを修正 +- [#2100](https://github.com/sanic-org/sanic/pull/2100) 存在しない引数の + ドキュメントを削除する + +## バージョン 21.3.2 + +**バグ修正** + +- [#2081](https://github.com/sanic-org/sanic/pull/2081) WebSocket接続で + 応答タイムアウトを無効にする +- [#2085](https://github.com/sanic-org/sanic/pull/2085) Make sure + that blueprints with no slash is maintained when applied + +## バージョン 21.3.1 + +**バグ修正** + +- [#2076](https://github.com/sanic-org/sanic/pull/2076) サブフォルダー内の静的ファイル + にアクセスできません (404) + +## バージョン 21.3.0 + +Release +Notes + +**機能** + +- [#1876](https://github.com/sanic-org/sanic/pull/1876) Unified + ストリーミングサーバー +- [#2005](https://github.com/sanic-org/sanic/pull/2005) 新しい + `Request.id` プロパティ +- [#2008](https://github.com/sanic-org/sanic/pull/2008) + Pathlib Path オブジェクトを `app.static()` helper に渡すことを許可する +- [#2010](https://github.com/sanic-org/sanic/pull/2010), + [#2031](https://github.com/sanic-org/sanic/pull/2031) New + startup-optimized router +- [#2018](https://github.com/sanic-org/sanic/pull/2018) + [#2064](https://github.com/sanic-org/sanic/pull/2064) メインサーバープロセスのリスナー +- [#2032](https://github.com/sanic-org/sanic/pull/2032) オブジェクトをリクエストするために生の + ヘッダー情報を追加する +- [#2042](https://github.com/sanic-org/sanic/pull/2042) + [#2060](https://github.com/sanic-org/sanic/pull/2060) + [#2061](https://github.com/sanic-org/sanic/pull/2061) + Signals API +- [#2043](https://github.com/sanic-org/sanic/pull/2043) SanicとBlueprintに + `__str__` と \`__repr__を追加する +- [#2047](https://github.com/sanic-org/sanic/pull/2047) ブループリントグループで + バージョン管理と厳密なスラッシュを有効にする +- [#2053](https://github.com/sanic-org/sanic/pull/2053) + `get_app` name 引数は省略可能にする +- [#2055](https://github.com/sanic-org/sanic/pull/2055) JSONエンコーダー + をアプリ経由で変更 +- [#2063](https://github.com/sanic-org/sanic/pull/2063) App と + コネクションレベルのコンテキストオブジェクト + +**バグ修正** + +- 解決する [#1420](https://github.com/sanic-org/sanic/pull/1420) + `url_for` ここで `strict_slashes` は`/`で終わるパスに対して有効です +- 解決[#1525](https://github.com/sanic-org/sanic/pull/1525) + ルーティングは特殊文字で正しくありません +- Resolve [#1653](https://github.com/sanic-org/sanic/pull/1653) ASGI + headers +- 解決する [#1722](https://github.com/sanic-org/sanic/pull/1722) + チャンクモードで curl を使う +- 解決[#1730](https://github.com/sanic-org/sanic/pull/1730) + ASGIストリーミング応答の追加コンテンツ +- 解決[#1749](https://github.com/sanic-org/sanic/pull/1749) + 壊れたミドルウェアのエッジケースを復元する +- 解決[#1785](https://github.com/sanic-org/sanic/pull/1785) + [#1804](https://github.com/sanic-org/sanic/pull/1804) Synchronous + error handler +- 解決 [#1790](https://github.com/sanic-org/sanic/pull/1790) + プロトコルエラーは非同期エラーハンドラをサポートしていません #1790 +- 解決[#1824](https://github.com/sanic-org/sanic/pull/1824) + 特定のメソッドのタイムアウト +- 解決[#1875](https://github.com/sanic-org/sanic/pull/1875) + 特定のルートから複数の + タイムアウトを返した後のすべてのルートからの応答タイムアウトエラー。 +- 解決する [#1988](https://github.com/sanic-org/sanic/pull/1988) + ボディで安全なメソッドを処理する +- [#2001](https://github.com/sanic-org/sanic/pull/2001) Cookie max-age が整数でない場合の + ValueError + +**非推奨と削除** + +- [#2007](https://github.com/sanic-org/sanic/pull/2007) \* Config + `from_envar` \* 設定で + `from_object` を使用する +- [#2009](https://github.com/sanic-org/sanic/pull/2009) Sanic + テストクライアントを独自のパッケージに削除 +- [#2036](https://github.com/sanic-org/sanic/pull/2036), + [#2037](https://github.com/sanic-org/sanic/pull/2037) Python + 3.6 のサポート +- `Request.endpoint` は `Request.name` を支持しています。 +- handler 型名プレフィックスが削除されました (静的、websocketなど) + +**開発者のインフラストラクチャ** + +- [#1995](https://github.com/sanic-org/sanic/pull/1995) + FUNDING.yml を作成 +- [#2013](https://github.com/sanic-org/sanic/pull/2013) CIパイプラインにcodeql + を追加 +- [#2038](https://github.com/sanic-org/sanic/pull/2038) Codecov + 設定の更新 +- [#2049](https://github.com/sanic-org/sanic/pull/2049) `find_packages`を使用するように + setup.pyを更新しました + +**ドキュメントの改善** + +- [#1218](https://github.com/sanic-org/sanic/pull/1218) + sanic.log.\* のドキュメントがありません +- [#1608](https://github.com/sanic-org/sanic/pull/1608) Add + documentation on calver and LTS +- [#1731](https://github.com/sanic-org/sanic/pull/1731) Support + mounting application elsewhere than at root path +- [#2006](https://github.com/sanic-org/sanic/pull/2006) + 型の注釈とdocstrings と API ドキュメンテーションの改善 +- [#2052](https://github.com/sanic-org/sanic/pull/2052) いくつかの + 例とドキュメントを修正する + +**その他** + +- `Request.route` プロパティ +- より優れた websocket サブプロトコルのサポート +- Resolve bug with middleware in Blueprint Group when passed + callable +- ブループリントとサニックの間で共通のロジックをミックスインに移動します。 +- ルート名をより一貫性のあるものに変更しました + - 要求エンドポイントはルート名です + - route names are fully namespaced +- いくつかの新しいコンビニエンスデコレータ: + - `@app.main_process_start` + - `@app.main_process_stop` + - `@app.before_server_start` + - `@app.after_server_start` + - `@app.before_server_stop` + - `@app.after_server_stop` + - `@app.on_request` + - `@app.on_response` +- `HEAD`を含まない`Allow`ヘッダーを修正しました。 +- ``` + 名が存在しない\"静的\"ルートに`url_for`で\"name\" キーワードを使用する + ``` +- 名前付きの param を使用せずに複数の `app.static()` を持つことはできません +- ファイルルート上で`url_for`の\"filename\" キーワードを使用する +- ルートデフの`unquote` (自動ではありません) +- `routes_all`はタプルです +- ハンドラの引数はkwargのみです +- `request.match_info` はキャッシュされた(計算されていない)プロパティになりました +- 不明な静的ファイル mimetype が `application/octet-stream` として送信されます。 +- `url_for`の`_host` キーワード +- ``` + が指定されていない場合はテキストとjsコンテンツタイプのデフォルトの文字セットを`utf-8`に追加します + ``` +- ルートのバージョンはstr、float、int にすることができます。 +- ルートは ctx プロパティを持ちます +- アプリには`routes_static`、`routes_dynamic`、`routes_regex`があります +- [#2044](https://github.com/sanic-org/sanic/pull/2044) コードクリーンアップ + とリファクタリングを行う +- [#2072](https://github.com/sanic-org/sanic/pull/2072) Remove + `BaseSanic` metaclass +- [#2074](https://github.com/sanic-org/sanic/pull/2074) パフォーマンス + `handle_request_` の調整 + +## バージョン 20.12.3 + +**バグ修正** + +- [#2021](https://github.com/sanic-org/sanic/pull/2021) ウェブソケットハンドラ名から + プレフィックスを削除 + +## バージョン 20.12.2 + +**依存** + +- [#2026](https://github.com/sanic-org/sanic/pull/2026) uvloop + を0.15 は Python 3.6 のサポートをドロップするため0.14 に修正しました。 +- [#2029](https://github.com/sanic-org/sanic/pull/2029) 古い + チャード要件を削除し、ハードマルチディクト要件を追加 + +## バージョン 19.12.5 + +**依存** + +- [#2025](https://github.com/sanic-org/sanic/pull/2025) uvloop + を0.15 は Python 3.6 のサポートをドロップするため0.14 に修正 +- [#2027](https://github.com/sanic-org/sanic/pull/2027) 古い + チャード要件を削除し、ハードマルチディクト要件を追加 + +## バージョン 20.12.0 + +**機能** + +- [#1993](https://github.com/sanic-org/sanic/pull/1993) + アプリレジストリを無効にする +- [#1945](https://github.com/sanic-org/sanic/pull/1945) 静的ルート + より詳細なファイルが見つからない場合 +- [#1954](https://github.com/sanic-org/sanic/pull/1954)静的 + ルートのブループリントへの登録を修正する +- [#1961](https://github.com/sanic-org/sanic/pull/1961) Python + 3.9 のサポート +- [#1962](https://github.com/sanic-org/sanic/pull/1962) Sanic CLI + upgrade +- [#1967](https://github.com/sanic-org/sanic/pull/1967) Update + aiofile version requirements +- [#1969](https://github.com/sanic-org/sanic/pull/1969) + のバージョン要件 +- [#1970](https://github.com/sanic-org/sanic/pull/1970) py.typed + ファイルを追加 +- [#1972](https://github.com/sanic-org/sanic/pull/1972) Speed + optimization in request handler +- [#1979](https://github.com/sanic-org/sanic/pull/1979) アプリ + レジストリとサニッククラスレベルのアプリ検索を追加 + +**バグ修正** + +- [#1965](https://github.com/sanic-org/sanic/pull/1965) ASGIストリーミング応答でチャンクされた + トランスポートエンコード + +**非推奨と削除** + +- [#1981](https://github.com/sanic-org/sanic/pull/1981) Cleanup and + remove deprecated code + +**開発者のインフラストラクチャ** + +- [#1956](https://github.com/sanic-org/sanic/pull/1956) load + モジュールテストを修正 +- [#1973](https://github.com/sanic-org/sanic/pull/1973) トランジション + Travisを.orgから.comへ。 +- [#1986](https://github.com/sanic-org/sanic/pull/1986) Update tox + requirements + +**ドキュメントの改善** + +- [#1951](https://github.com/sanic-org/sanic/pull/1951) + ドキュメントの改善 +- [#1983](https://github.com/sanic-org/sanic/pull/1983) testing.rst で内容が重複する + を削除 +- [#1984](https://github.com/sanic-org/sanic/pull/1984) + routing.rst のtypoを修正 + +## バージョン 20.9.1 + +**バグ修正** + +- [#1954](https://github.com/sanic-org/sanic/pull/1954) 青写真の静的 + ルート登録を修正 +- [#1957](https://github.com/sanic-org/sanic/pull/1957) ASGI ストリーミングボディの + 重複ヘッダーを削除 + +## バージョン 19.12.3 + +**バグ修正** + +- [#1959](https://github.com/sanic-org/sanic/pull/1959) ASGI ストリーミングボディの + 重複ヘッダーを削除 + +## バージョン 20.9.0 + +**機能** + +- [#1887](https://github.com/sanic-org/sanic/pull/1887) WebSocketsの + サブプロトコルを渡します (両方のsanic serverとASGI)。 +- [#1894](https://github.com/sanic-org/sanic/pull/1894) + アプリインスタンスに `test_mode` フラグを自動的に設定 +- [#1903](https://github.com/sanic-org/sanic/pull/1903) アプリの値を更新するための新しい + ユニファイドメソッドを追加する +- [#1906](https://github.com/sanic-org/sanic/pull/1906), + [#1909](https://github.com/sanic-org/sanic/pull/1909) + WEBSOCKET_PING_TIMEOUTとWEBSOCKET_PING_INTERVAL 設定 + 値 +- [#1935](https://github.com/sanic-org/sanic/pull/1935) httpx + バージョンの依存関係が更新されました。v20.12 で + 依存関係が削除される予定です。 +- [#1937](https://github.com/sanic-org/sanic/pull/1937) auto、 + text、json fallback エラーハンドラを追加しました (v21.3では、デフォルトは + フォームの html を autoに変更します) + +**バグ修正** + +- [#1897](https://github.com/sanic-org/sanic/pull/1897) Resolves + exception from unread bytes in stream + +**非推奨と削除** + +- [#1903](https://github.com/sanic-org/sanic/pull/1903) + config.from_envar, config.from_pyfile, config.from_object は + 非推奨であり、v21.3 で削除されるように設定されています。 + +**開発者のインフラストラクチャ** + +- [#1890](https://github.com/sanic-org/sanic/pull/1890), + [#1891](https://github.com/sanic-org/sanic/pull/1891) isort + 呼び出しを新しいAPIと互換性があるように更新する +- [#1893](https://github.com/sanic-org/sanic/pull/1893) setup.cfg から + バージョンセクションを削除 +- [#1924](https://github.com/sanic-org/sanic/pull/1924) + \--strict-marker を pytest に追加する + +**ドキュメントの改善** + +- [#1922](https://github.com/sanic-org/sanic/pull/1922) 明示的な + ASGI準拠をREADMEに追加する + +## バージョン 20.6.3 + +**バグ修正** + +- [#1884](https://github.com/sanic-org/sanic/pull/1884) Revert + change to multiprocessing mode + +## バージョン 20.6.2 + +**機能** + +- [#1641](https://github.com/sanic-org/sanic/pull/1641) IPv6 および UNIX ソケットに適切に実装された Socket + バインディング。 + +## バージョン 20.6.1 + +**機能** + +- [#1760](https://github.com/sanic-org/sanic/pull/1760) ウェブソケットルートにバージョン + パラメータを追加する +- [#1866](https://github.com/sanic-org/sanic/pull/1866) エントリポイントコマンドとして `sanic` + を追加する +- [#1880](https://github.com/sanic-org/sanic/pull/1880) url_for usage 用ウェブソケットのハンドラ + 名を追加する + +**バグ修正** + +- [#1776](https://github.com/sanic-org/sanic/pull/1776) + ホストパラメータのリストに関するバグ修正 +- [#1842](https://github.com/sanic-org/sanic/pull/1842) Fix static + \_handler pickling error +- [#1827](https://github.com/sanic-org/sanic/pull/1827) OSX py38 と Windows のリローダー + を修正 +- [#1848](https://github.com/sanic-org/sanic/pull/1848) Reverse + named_response_middlware execution order, to match normal response + middleware execution order +- [#1853](https://github.com/sanic-org/sanic/pull/1853) Pickle + エラーを修正 + websocket routes を含むアプリケーションをpickle しようとするとエラーが発生します + +**非推奨と削除** + +- [#1739](https://github.com/sanic-org/sanic/pull/1739) + body_bytes を本体にマージする + +**開発者のインフラストラクチャ** + +- [#1852](https://github.com/sanic-org/sanic/pull/1852) PythonナイトリーのCI test envの + 命名を修正 +- [#1857](https://github.com/sanic-org/sanic/pull/1857) Adjust + websockets version to setup.py +- [#1869](https://github.com/sanic-org/sanic/pull/1869) Wrap + run()の\"protocol\" 型アノテーションオプション\[\] + +**ドキュメントの改善** + +- [#1846](https://github.com/sanic-org/sanic/pull/1846) docs + を更新してミドルウェアの実行順序を明確にする +- [#1865](https://github.com/sanic-org/sanic/pull/1865) 文書を隠していたrst + フォーマットの問題を修正しました + +## バージョン 20.6.0 + +_リリースされましたが、意図せずPR #1880を省略したため、 +20.6.1_ に置き換えられました + +## バージョン 20.3.0 + +**機能** + +- [#1762](https://github.com/sanic-org/sanic/pull/1762) + `srv.start_serving()` と `srv.serve_forever()` を `AsyncioServer` に追加します +- [#1767](https://github.com/sanic-org/sanic/pull/1767) `hypercorn -k trio myweb.app` で Sanic + を使えるようにする +- [#1768](https://github.com/sanic-org/sanic/pull/1768) No + tracebacks on normal errors and prettier error pages +- [#1769](https://github.com/sanic-org/sanic/pull/1769) ファイル応答のコードクリーンアップ +- [#1793](https://github.com/sanic-org/sanic/pull/1793) and + [#1819](https://github.com/sanic-org/sanic/pull/1819) Upgrade + `str.format()` to f-strings +- [#1798](https://github.com/sanic-org/sanic/pull/1798) + Python 3.8 を搭載した MacOS の複数のワーカーを許可する +- [#1820](https://github.com/sanic-org/sanic/pull/1820) + content-type と content-length ヘッダーを例外で設定しないでください。 + +**バグ修正** + +- [#1748](https://github.com/sanic-org/sanic/pull/1748) Python 3.8 の `asyncio.Event` の loop + 引数を削除します +- [#1764](https://github.com/sanic-org/sanic/pull/1764) ルート + デコレータが再びスタックできるようにする +- [#1789](https://github.com/sanic-org/sanic/pull/1789) Fix tests + using hosts yielding incorrect `url_for` +- [#1808](https://github.com/sanic-org/sanic/pull/1808) Ctrl+C + とWindowsでのテスト + +**非推奨と削除** + +- [#1800](https://github.com/sanic-org/sanic/pull/1800) Begin + deprecation in way of first-class streaming, removal of + `body_init`, `body_push`, and `body_finish` +- [#1801](https://github.com/sanic-org/sanic/pull/1801) Complete + deprecation from + [#1666](https://github.com/sanic-org/sanic/pull/1666) of + dictionary context on `request` objects. +- [#1807](https://github.com/sanic-org/sanic/pull/1807) アプリから直接読み込むことのできる + サーバー設定の引数を削除する +- [#1818](https://github.com/sanic-org/sanic/pull/1818) Complete + deprecation of `app.remove_route` and `request.raw_args` + +**依存** + +- [#1794](https://github.com/sanic-org/sanic/pull/1794) `httpx` + を0.11.1 +- [#1806](https://github.com/sanic-org/sanic/pull/1806) Import + `ASGIDispatch` from top-level `httpx` (from third-party + deprecation) + +**開発者のインフラストラクチャ** + +- [#1833](https://github.com/sanic-org/sanic/pull/1833) + 壊れたドキュメントのビルドを解決する + +**ドキュメントの改善** + +- [#1755](https://github.com/sanic-org/sanic/pull/1755) + `response.empty()` の使用法 +- [#1778](https://github.com/sanic-org/sanic/pull/1778) Update + README +- [#1783](https://github.com/sanic-org/sanic/pull/1783) Fix typo +- [#1784](https://github.com/sanic-org/sanic/pull/1784) Corrected + changelog for docs move of MD to RST + ([#1691](https://github.com/sanic-org/sanic/pull/1691)) +- [#1803](https://github.com/sanic-org/sanic/pull/1803) + 設定ドキュメントをDEFAULT_CONFIGに一致させるように更新する +- [#1814](https://github.com/sanic-org/sanic/pull/1814) 更新 + getting_started.rst +- [#1821](https://github.com/sanic-org/sanic/pull/1821) Update to + deployment +- [#1822](https://github.com/sanic-org/sanic/pull/1822) ドキュメント + を20.3で変更を加えて更新する +- [#1834](https://github.com/sanic-org/sanic/pull/1834) + リスナーの順序 + +## バージョン 19.12.0 + +**バグ修正** + +- ブループリントミドルウェアアプリケーションを修正 + + Currently, any blueprint middleware registered, irrespective of + which blueprint was used to do so, was being applied to all of the + routes created by the `@app` and `@blueprint` alike. + + この変更の一環として、ブループリントベースのミドルウェアアプリケーション + は登録場所に基づいて施行されます。 + + - `@blueprint.middleware`を介してミドルウェアを登録する場合、 + はblueprintで定義されたルートにのみ適用されます。 + - If you register a middleware via `@blueprint_group.middleware` + then it will apply to all blueprint based routes that are part + of the group. + - If you define a middleware via `@app.middleware` then it will be + applied on all available routes + ([#37](https://github.com/sanic-org/sanic/issues/37)) + +- SERVER_NAME がない場合の [url_for]{.title-ref} の動作を修正 + + If the [SERVER_NAME]{.title-ref} was missing in the + [app.config]{.title-ref} entity, the [url_for]{.title-ref} on the + [request]{.title-ref} and [app]{.title-ref} were failing due to an + [AttributeError]{.title-ref}. This fix makes the availability of + [SERVER_NAME]{.title-ref} on our [app.config]{.title-ref} an + optional behavior. + ([#1707](https://github.com/sanic-org/sanic/issues/1707)) + +**ドキュメントの改善** + +- MDからRSTへドキュメントを移動 + + Moved all docs from markdown to restructured text like the rest of + the docs to unify the scheme and make it easier in the future to + update documentation. + ([#1691](https://github.com/sanic-org/sanic/issues/1691)) + +- [get]{.title-ref} と [getlist]{.title-ref} の + [request.args]{.title-ref} のドキュメントを修正 + + ``` + [getlist]{.title-ref} の使用例を追加し、 + [request.args]{.title-ref} ビヘイビア + ([#1704](https://github.com/sanic-org/sanic/issues/1704)) のドキュメント文字列を修正します。 + ``` + +## バージョン 19.6.3 + +**機能** + +- タウンクリエのサポートを有効にする + + この機能の一環として、 [towncrier]{.title-ref} は、各プルリクエストの一部として、変更ログを生成するプロセスと + 管理するメカニズムとして、 + が導入されています。 + ([#1631](https://github.com/sanic-org/sanic/issues/1631)) + +**ドキュメントの改善** + +- ドキュメンテーションインフラストラクチャの変更 + - Enable having a single common [CHANGELOG]{.title-ref} file for + both GitHub page and documentation + - Sphinix非推奨の警告を修正 + - 無効な [rst]{.title-ref} + インデントによるドキュメント警告の修正 + - Enable common contribution guidelines file across GitHub and + documentation via [CONTRIBUTING.rst]{.title-ref} + ([#1631](https://github.com/sanic-org/sanic/issues/1631)) + +## バージョン 19.6.2 + +**機能** + +- [#1562](https://github.com/sanic-org/sanic/pull/1562) Remove + `aiohttp` dependency and create new `SanicTestClient` based upon + [requests-async](https://github.com/encode/requests-async) +- [#1475](https://github.com/sanic-org/sanic/pull/1475) ASGI + サポートを追加しました (ベータ) +- [#1436](https://github.com/sanic-org/sanic/pull/1436) Add + Configure support from object string + +**バグ修正** + +- [#1587](https://github.com/sanic-org/sanic/pull/1587) Expect ヘッダーの + ハンドルを追加します。 +- [#1560](https://github.com/sanic-org/sanic/pull/1560) Allow to + disable Transfer-Encoding: chunked. +- [#1558](https://github.com/sanic-org/sanic/pull/1558) 優雅な + シャットダウンを修正。 +- [#1594](https://github.com/sanic-org/sanic/pull/1594) Strict + スラッシュ動作修正 + +**非推奨と削除** + +- [#1544](https://github.com/sanic-org/sanic/pull/1544) Drop + dependency on distutil +- [#1562](https://github.com/sanic-org/sanic/pull/1562) Python 3.5 の + サポートを終了する +- [#1568](https://github.com/sanic-org/sanic/pull/1568) + のルート削除が非推奨です。 + +.. 警告:: + +``` +Sanic will not support Python 3.5 from version 19.6 and forward. +However, version 18.12LTS will have its support period extended thru +December 2020, and therefore passing Python\'s official support version +3.5, which is set to expire in September 2020. +``` + +## バージョン 19.3 + +**機能** + +- [#1497](https://github.com/sanic-org/sanic/pull/1497) Add support + for zero-length and RFC 5987 encoded filename for + multipart/form-data requests. + +- [#1484](https://github.com/sanic-org/sanic/pull/1484) `sanic.cookies.Cookie` の + `expires` 属性の型は `datetime` 型の + に適用されます。 + +- [#1482](https://github.com/sanic-org/sanic/pull/1482) Add support + for the `stream` parameter of `sanic.Sanic.add_route()` available + to `sanic.Blueprint.add_route()`. + +- [#1481](https://github.com/sanic-org/sanic/pull/1481) `int`または`number`型のルートパラメータの + 負の値を受け入れます。 + +- [#1476](https://github.com/sanic-org/sanic/pull/1476) Deprecated + the use of `sanic.request.Request.raw_args` - it has a fundamental + flaw in which is drops repeated query string parameters. + `sanic.request.Request.query_args` が + 元のユースケースの置き換えとして追加されました。 + +- [#1472](https://github.com/sanic-org/sanic/pull/1472) Request class `repr` 実装の + 不要な `None` チェックを削除します。 この + は、``からリクエストのデフォルトの`repr`を + ``に変更します。 + +- [#1470](https://github.com/sanic-org/sanic/pull/1470) `sanic.app.create_server`に2つの新しい + パラメータを追加: + + - `return_asyncio_server` - + asyncio.Serverを返すかどうか。 + - `asyncio_server_kwargs` - kwargs は、sanic が使用しているイベントループの + `loop.create_server` に渡します。 + + > + + これは、破損した変化です。 + +- [#1499](https://github.com/sanic-org/sanic/pull/1499) ルート解像度をテストとベンチマークするテストケースのセット + を追加しました。 + +- [#1457](https://github.com/sanic-org/sanic/pull/1457) `sanic.cookies.Cookie` 内の `"max-age"`値の + 型が整数になるようになりました。 + 整数でない値は `0` に置き換えられます。 + +- [#1445](https://github.com/sanic-org/sanic/pull/1445) Added the + `endpoint` attribute to an incoming `request`, containing the name + of the handler function. + +- [#1423](https://github.com/sanic-org/sanic/pull/1423) + リクエストストリーミングを改善しました。 `request.stream` はバインドされていないキューの代わりに、バインドされたサイズのバッファ + になりました。 呼び出し側は + `await request.stream.get()` の代わりに + `await request.stream.read()` を呼び出し、本文の各部分を読み取る必要があります。 + + これは、破損した変化です。 + +**バグ修正** + +- [#1502](https://github.com/sanic-org/sanic/pull/1502) Sanicは、 + `time.time()`の先頭に追加し、毎秒1回更新して、 + 過剰な`time.time()`の呼び出しを回避します。 実装が + に観察され、メモリリークが発生する場合がありました。 プリフェッチ + の利点は無視できるように見えたため、これが削除されました。 Fixes + [#1500](https://github.com/sanic-org/sanic/pull/1500) +- [#1501](https://github.com/sanic-org/sanic/pull/1501) Fix a bug in + the auto-reloader when the process was launched as a module i.e. + `python -m init0.mod1` where the sanic server is started in + `init0/mod1.py` with `debug` enabled and imports another module in + `init0`. +- [#1376](https://github.com/sanic-org/sanic/pull/1376) `SanicTestClient` を構築する際に `port=None` + を指定することで、sanic + テストクライアントがランダムなポートにバインドできるようにします。 +- [#1399](https://github.com/sanic-org/sanic/pull/1399) Added the + ability to specify middleware on a blueprint group, so that all + routes produced from the blueprints in the group have the + middleware applied. +- [#1442](https://github.com/sanic-org/sanic/pull/1442) Allow the + the use the `SANIC_ACCESS_LOG` environment variable to + enable/disable the access log when not explicitly passed to + `app.run()`. This allows the access log to be disabled for example + when running via gunicorn. + +**開発者のインフラストラクチャ** + +- [#1529](https://github.com/sanic-org/sanic/pull/1529) Update + project PyPI credentials +- [#1515](https://github.com/sanic-org/sanic/pull/1515) linter + の問題を修正する (#1514を修正) +- [#1490](https://github.com/sanic-org/sanic/pull/1490) doc build で python + のバージョンを修正 +- [#1478](https://github.com/sanic-org/sanic/pull/1478) + setuptools のバージョンをアップグレードし、doc ビルドでネイティブの docutils を使用する +- [#1464](https://github.com/sanic-org/sanic/pull/1464) + pytestをアップグレードし、caplog 単体テストを修正する + +**ドキュメントの改善** + +- [#1516](https://github.com/sanic-org/sanic/pull/1516) Fix typo at + the exception documentation +- [#1510](https://github.com/sanic-org/sanic/pull/1510) fix typo in + Asyncio example +- [#1486](https://github.com/sanic-org/sanic/pull/1486) + ドキュメント typo +- [#1477](https://github.com/sanic-org/sanic/pull/1477) README.md で文法 + を修正 +- [#1489](https://github.com/sanic-org/sanic/pull/1489) Added + \"databases\" to the extensions list +- [#1483](https://github.com/sanic-org/sanic/pull/1483) 拡張リストに + sanic-zipkinを追加する +- [#1487](https://github.com/sanic-org/sanic/pull/1487) + リンクを削除しました。拡張機能リストからリポジトリ、Sanic-OAuthを削除しました。 +- [#1460](https://github.com/sanic-org/sanic/pull/1460) 18.12 + changelog +- [#1449](https://github.com/sanic-org/sanic/pull/1449) リクエストオブジェクトを修正する例 + を追加 +- [#1446](https://github.com/sanic-org/sanic/pull/1446) Update + README +- [#1444](https://github.com/sanic-org/sanic/pull/1444) Update + README +- [#1443](https://github.com/sanic-org/sanic/pull/1443) 新しいロゴを含む + READMEを更新 +- [#1440](https://github.com/sanic-org/sanic/pull/1440) マイナー + 型とpipインストール命令の不一致を修正 +- [#1424](https://github.com/sanic-org/sanic/pull/1424) + ドキュメントの拡張 + +注: 19.3.0 はパッケージ化目的でスキップされ、 +PyPI ではリリースされません。 + +## バージョン 18.12 + +### 18.12.0 + +- 変更点: + + - コードベースのテストカバレッジが81named@@0に向上しました。 + - Added stream_large_files and host examples in static_file + document + - Added methods to append and finish body content on Request + (#1379) + - windows ci サポート用 .appveyor.yml と統合されました + - AF_INET6 と AF_UNIX ソケットの使用に関するドキュメントを追加しました + - コードスタイルに黒/isortを採用 + - 接続失敗時にタスクをキャンセルする + - リクエストIPとポート取得ロジックを簡素化する + - load設定ファイルのconfigエラーを処理します。 + - CI用のcodecovと統合 + - 設定セクションに不足しているドキュメントを追加します。 + - Handler.log を非推奨にする + - バージョン 0.0.0.10 + の httptools 要件をピン留めしました + +- 修正: + + - `remove_entity_headers` ヘルパー関数を修正 (#1415) + - Fix TypeError when use Blueprint.group() to group blueprint + with default url_prefix, Use os.path.normpath to avoid invalid + url_prefix like api//v1 f8a6af1 Rename the `http` module to + `helpers` to prevent conflicts with the built-in Python http + library (fixes #1323) + - WindowsのUnittestsを修正 + - 健全なロガーの名前空間を修正 + - デコレータの例で不足している引用符を修正 + - 引用符で囲まれたパラメータでリダイレクトを修正 + - 最新の建設計画コードのドキュメントを修正します。 + - マークダウンリストに関するラテックス文書の構築を修正 + - app.pyでループ例外処理を修正 + - Windowsや他のプラットフォームでコンテンツの長さが一致しない問題を修正 + - 固定ファイルの範囲ヘッダーの処理を修正 (#1402) + - ロガーを修正して動作させます(#1397) + - マルチプロセッシングテストでpikcle-\>pickle型を修正 + - ブループリント内の名前付きタプルの + \"name\" セクションで渡された文字列をブループリントモジュール属性名の + 名に合わせて変更します。 This allows + blueprints to be pickled and unpickled, without errors, which + is a requirement of running Sanic in multiprocessing mode in + Windows. Added a test for pickling and unpickling blueprints + Added a test for pickling and unpickling sanic itself Added a + test for enabling multiprocessing on an app with a blueprint + (only useful to catch this bug if the tests are run on + Windows). + - ログのドキュメントを修正 + +## バージョン 0.8 + +**0.8.3** + +- 変更点: + - 所有権を組織に変更しました \'sanic-org\' + +**0.8.0** + +- 変更点: + - サーバー送信済みイベント拡張を追加 (Innokenty Lebedev) + - request_handler_task キャンセルの優美な処理 (Ashley + Sommer) + - リダイレクト前の URL をサニタイズします(保存) + - リクエストに url_bytes を追加 (johndoe46) + - py37 は travisci (ユンスタンフォード) のサポート + - OSX (garyo) への自動リローダーのサポート + - UUID ルートサポートを追加 (Volodymyr Maksymiv) + - 一時停止可能な応答ストリームを追加 (Ashley Sommer) + - スロットを要求するために弱点を追加する (vopankov) + - 廃止予定の + (yunstanford) のためにテストフィクスチャーから ubuntu 12.04 を削除します + - add_route (kinware) でストリーミングハンドラを許可する + - tox(Raphael Deem)にtravis_retryを使う + - テストクライアント用の aiohttp バージョンの更新 (yunstanford) + - 明瞭度のためのリダイレクトインポートを追加 (yingshaoxo) + - HTTP エンティティヘッダーを更新 (Arnulfo Soli's) + - register_listenerメソッドを追加 (Stephan Fitzpatrick) + - Windows用のuvloop/ujson依存関係の削除 (abuckenheimer) + - Content-length header on 204/304 responses (Arnulfo Soli's) + - WebSocketProtocol引数の拡張とドキュメントの追加 (Bob Olde + Hampsink, yunstanford) + - アルファ前からベータ版への開発状況を更新 (Maksim + Anisenkov) + - KeepAlive タイムアウトのログレベルがデバッグに変更されました (Arnulfo Soli's) + - pytest-dev/pytest#3170 (Maksim + Aniskenov) により pytest を3.3.2 にピンします + - テスト用のdocker container に Python 3.5 と 3.6 をインストールします (Shahin + Azad) + - 建設計画グループとネスト(Alias Tarhini)のサポートを追加する + - Windows用のuvloopを削除します(Aleksandr Kurlov) + - Auto Reload (Yaser Amari) + - ドキュメントの更新/修正 (複数の投稿者) +- 修正: + - 修正: Linux で auto_reload (Ashley Sommer) + - 修正:aiohttp \>= 3.3.0 (Ashley Sommer) + - 修正: windows ではデフォルトで auto_reload を無効にする (abuckenheimer) + - 修正 (1143): gunicorn (hqy) でアクセスログをオフにする + - Fix (1268): ファイル応答のステータスコードをサポート (Cosmo Borsky) + - Fix (1266): Sanic.static (Cosmo Borsky) にcontent_type フラグを追加 + - 修正: add_websocket_route + (ciscorn) からサブプロトコルパラメータがありません + - Fix (1242): CI ヘッダーのレスポンス (yunstanford) + - Fix (1237): websockets (yunstanford) のバージョン制約を追加 + - 修正 (1231): メモリリーク - 常にリソースをリリース (フィリップXu) + - Fix (1221): トランスポートが存在する場合はリクエストを真にする (Raphael + Deem) + - aiohttp\>=3.1.0(Ashley Sommer)のテストに失敗する問題を修正 + - try_everything の例を修正 (PyManiacGR, kot83) + - Fix (1158): デバッグモードで auto_reload がデフォルト(Raphel Deem) + - Fix (11136): ErrorHandler.response ハンドラの呼び出しが制限されています + (Julien Castiaux) + - 修正: raw requires bytes-like object (cloudship) + - Fix (1120): passing a list in to a route decorator\'s host arg + (Timothy Ebiuwhe) + - 修正: マルチパート/フォームデータパーサのバグ(DirkGuijt) + - Fix: Exception for missing parameter when value is null + (NyanKiyoshi) + - 修正:パラメータチェック(Howie Hu) + - Fix (1089): 名前付きパラメータと異なる + メソッドでルーティングの問題 (yunstanford) + - Fix (1085): マルチワーカーモードでの信号処理 (yunstanford) + - 修正: readme.rst (Cosven) のシングルクォート + - 修正: メソッドの誤植(Dmitry Dygalo) + - Fix: ipとポートのlog_response正しい出力 (Wiboo + Arindrarto) + - 修正 (1042): 例外処理 (Raphael Deem) + - 修正:中国語URI (Howie Hu) + - Fix (1079): self.transportが None の場合のタイムアウトバグ (Raphael + Deem) + - Fix (1074): route has slash (Raphael + Deem) がスラッシュの場合の strict_slash の修正 + - Fix (1050): Cookie キーに samesite Cookie を追加する (Raphel Deem) + - 修正 (1065): サーバ起動後に add_task を許可する (Raphael Deem) + - 修正 (1061): 許可されていない例外のダブルクォート (Raphael + Deem) + - 修正 (1062): add_task メソッドに アプリを注入する (Raphael Deem) + - Fix: update environment.yml for readthedocs (Eli Uriegas) + - Fix: Cancel request task when response timeout is triggered + (Jeong YunWon) + - Fix (1052): Method not allowed response for RFC7231 compliance + (Raphael Deem) + - 修正:IPv6 アドレスとソケットデータフォーマット(ダン・パーマー) + +注: 更新履歴は0.1から0.7の間は維持されませんでした + +## バージョン 0.1 + +**0.1.7** + +- 仕様を満たすために静的URLとディレクトリ引数を逆にしました + +**0.1.6** + +- 静的ファイル +- 遅延クッキーの読み込み中 + +**0.1.5** + +- Cookie +- 設計図のリスナーと注文 +- より速いルータを使用する +- 修正:不完全なファイルは、中程度の大きさのポストリクエストで読み込まれます +- Breaking: after_start and before_stop now pass sanic as their + first argument + +**0.1.4** + +- マルチ処理 + +**0.1.3** + +- ブループリントのサポート +- 迅速な応答処理 + +**0.1.1 - 0.1.2** + +- CI経由でピピを更新するのに苦労しています + +**0.1.0** + +- 公開 diff --git a/guide/content/ko/built-with-sanic.md b/guide/content/ko/built-with-sanic.md new file mode 100644 index 0000000000..fa5d78e1cf --- /dev/null +++ b/guide/content/ko/built-with-sanic.md @@ -0,0 +1,67 @@ +--- +title: Full Speed Ahead - How We Built This Site with Sanic +layout: main +--- + +.. attrs:: +:class: title + +``` +Full Speed Ahead: +``` + +.. attrs:: +:class: subtitle + +``` +How We Built This Site with Sanic +``` + +Welcome to our little corner of the Internet where we proudly say, "Yes, we built this with Sanic!" This isn't just a website; it's our playground, our test lab, our battlefield, and, well, our home. + +![](/assets/images/built-with-sanic.png) + +### The Story: "We Drink Our Own Champagne" + +We believe in Sanic so much that we decided to put it to the ultimate test—running our own website. It's like a chef eating at their own restaurant, only with less risk of food poisoning. + +Why? Because building a website or web application is hard. There are countless moving parts, a plethora of challenges, and the ever-present need for speed and reliability. We want to show you just one of the many ways you _could_ do it. + +In this high-stakes digital kitchen, Sanic is our secret ingredient. By deploying our own website on Sanic, we're not just showcasing its capabilities; we're stress-testing them in the real world. This is our chance to walk the walk, proving that Sanic isn't just good on paper—it's a robust, high-performance framework that can handle everything from the smallest blog to the busiest e-commerce site. + +So, here we are, sipping our own champagne, confident in the knowledge that if Sanic can run our site, it can power yours too. Cheers to coding at the speed of thought! 🥂 + +### The Setup: Digital Ocean, Ahoy! + +We launched our site on Digital Ocean's App Platform because we love high-performance cloud sailing. Think of it as having a Ferrari in the cloud—fast, sleek, but way easier to handle. + +Why go for simplicity? With a lean team and no DevOps gurus, we needed a no-fuss, straightforward solution. Digital Ocean gives us that smooth sailing platform-as-a-service (PaaS) experience. It’s perfect for our needs: easy setup, automatic deployments, and the kind of reliability that lets you sleep soundly. + +Our choice reflects our ethos: focus on your strengths and let the platform do the heavy lifting. For us, it means creating amazing web experiences with Sanic, supported by a deployment solution that's simple yet powerful. ⛵ + +### The Code: GitHub's Where It's At + +All our code is out in the open, basking in the glory of public scrutiny on GitHub. Why hide the magic? It's right there, in full view, at [our GitHub repository](https://github.com/sanic-org/sanic/tree/main/guide). Go ahead, take a peek, fork it, play with it, break it (and then kindly fix it). + +Open-source isn't just a buzzword for us; it's our ethos. It's about building something bigger than ourselves, together. Our code is a testament to collaborative innovation, a playground for development, and a real-life example of Sanic in action. + +Every line of code, every commit, reflects our journey with Sanic, showcasing how we leverage its speed and scalability. Your contributions, whether fixing a bug, suggesting a feature, or enhancing documentation, are what propel this project forward. + +So, dive in, contribute your genius, and let's keep shaping the future of web development with Sanic. Together, we're not just coding – we're creating a community-driven powerhouse. 🚀 + +### The Invitation: Write, Code, Break, Fix! + +- **Documentarians**: Love making complex stuff sound easy? Our docs are your canvas. Paint away in words! 🎨 + +- **Code Ninjas**: Find bugs? Squash 'em. Got ideas? Code 'em. Make pull requests rain! 🥷 + +- **Bug Hunters**: If you find bugs, don't just stare. Let us know. We love a good bug hunt. 🐛 + +### The Bottom Line + +We built this site with Sanic to show off what it can do. It's fast, it's fun, and it's what we use. So, if things load swiftly, pat us on the back. If they don't, well, uh... we blame cosmic rays? + +Join us in making Sanic not just good, but "I-can't-believe-it's-not-butter" good! + +Cheers, +The Sanic Team (who occasionally wear capes) diff --git a/guide/content/ko/guide/advanced/class-based-views.md b/guide/content/ko/guide/advanced/class-based-views.md new file mode 100644 index 0000000000..901e0848bf --- /dev/null +++ b/guide/content/ko/guide/advanced/class-based-views.md @@ -0,0 +1,227 @@ +# Class Based Views + +## Why use them? + +.. column:: + +``` +### The problem + +A common pattern when designing an API is to have multiple functionality on the same endpoint that depends upon the HTTP method. + +While both of these options work, they are not good design practices and may be hard to maintain over time as your project grows. +``` + +.. column:: + +```` +```python +@app.get("/foo") +async def foo_get(request): + ... + +@app.post("/foo") +async def foo_post(request): + ... + +@app.put("/foo") +async def foo_put(request): + ... + +@app.route("/bar", methods=["GET", "POST", "PATCH"]) +async def bar(request): + if request.method == "GET": + ... + + elif request.method == "POST": + ... + + elif request.method == "PATCH": + ... +``` +```` + +.. column:: + +``` +### The solution + +Class-based views are simply classes that implement response behavior to requests. They provide a way to compartmentalize handling of different HTTP request types at the same endpoint. +``` + +.. column:: + +```` +```python +from sanic.views import HTTPMethodView + +class FooBar(HTTPMethodView): + async def get(self, request): + ... + + async def post(self, request): + ... + + async def put(self, request): + ... + +app.add_route(FooBar.as_view(), "/foobar") +``` +```` + +## Defining a view + +A class-based view should subclass :class:`sanic.views.HTTPMethodView`. You can then implement class methods with the name of the corresponding HTTP method. If a request is received that has no defined method, a `405: Method not allowed` response will be generated. + +.. column:: + +``` +To register a class-based view on an endpoint, the `app.add_route` method is used. The first argument should be the defined class with the method `as_view` invoked, and the second should be the URL endpoint. + +The available methods are: + +- get +- post +- put +- patch +- delete +- head +- options +``` + +.. column:: + +```` +```python +from sanic.views import HTTPMethodView +from sanic.response import text + +class SimpleView(HTTPMethodView): + + def get(self, request): + return text("I am get method") + + # You can also use async syntax + async 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") + +app.add_route(SimpleView.as_view(), "/") +``` +```` + +## Path parameters + +.. column:: + +``` +You can use path parameters exactly as discussed in [the routing section](/guide/basics/routing.md). +``` + +.. column:: + +```` +```python +class NameView(HTTPMethodView): + + def get(self, request, name): + return text("Hello {}".format(name)) + +app.add_route(NameView.as_view(), "/") +``` +```` + +## Decorators + +As discussed in [the decorators section](/guide/best-practices/decorators.md), often you will need to add functionality to endpoints with the use of decorators. You have two options with CBV: + +1. Apply to _all_ HTTP methods in the view +2. Apply individually to HTTP methods in the view + +Let's see what the options look like: + +.. column:: + +``` +### Apply to all methods + +If you want to add any decorators to the class, you can set the `decorators` class variable. These will be applied to the class when `as_view` is called. +``` + +.. column:: + +```` +```python +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") +``` +```` + +.. column:: + +``` +### Apply to individual methods + +But if you just want to decorate some methods and not all methods, you can as shown here. +``` + +.. column:: + +```` +```python +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 do not have any decorators") + + @some_decorator_here + def patch(self, request, name): + return text("Hello I have a decorator") +``` +```` + +## Generating a URL + +.. column:: + +``` +This works just like [generating any other URL](/guide/basics/routing.md#generating-a-url), except that the class name is a part of the endpoint. +``` + +.. column:: + +```` +```python +@app.route("/") +def index(request): + url = app.url_for("SpecialClassView") + return redirect(url) + +class SpecialClassView(HTTPMethodView): + def get(self, request): + return text("Hello from the Special Class View!") + +app.add_route(SpecialClassView.as_view(), "/special_class_view") +``` +```` diff --git a/guide/content/ko/guide/advanced/commands.md b/guide/content/ko/guide/advanced/commands.md new file mode 100644 index 0000000000..8f2cf07176 --- /dev/null +++ b/guide/content/ko/guide/advanced/commands.md @@ -0,0 +1,73 @@ +# Custom CLI Commands + +.. new:: New in v24.12 + +``` +This feature was added in version 24.12 +``` + +Sanic ships with a [CLI](../running/running.html#running-via-command) for running the Sanic server. Sometimes, you may have the need to enhance that CLI to run your own custom commands. Commands are invoked using the following basic pattern: + +```sh +sanic path.to:app exec [--arg=value] +``` + +.. column:: + +``` +To enable this, you can use your `Sanic` app instance to wrap functions that can be callable from the CLI using the `@app.command` decorator. +``` + +.. column:: + +```` +```python +@app.command +async def hello(name="world"): + print(f"Hello, {name}.") +``` +```` + +.. column:: + +``` +Now, you can easily invoke this command using the `exec` action. +``` + +.. column:: + +```` +```sh +sanic path.to:app exec hello --name=Adam +``` +```` + +Command handlers can be either synchronous or asynchronous. The handler can accept any number of keyword arguments, which will be passed in from the CLI. + +.. column:: + +``` +By default, the name of the function will be the command name. You can override this by passing the `name` argument to the decorator. +``` + +.. column:: + +```` +```python +@app.command(name="greet") +async def hello(name="world"): + print(f"Hello, {name}.") +``` + +```sh +sanic path.to:app exec greet --name=Adam +``` +```` + +.. warning:: + +``` +This feature is still in **BETA** and may change in future versions. There is no type coercion or validation on the arguments passed in from the CLI, and the CLI will ignore any return values from the command handler. Future enhancements and changes are likely. +``` + +_Added in v24.12_ diff --git a/guide/content/ko/guide/advanced/proxy-headers.md b/guide/content/ko/guide/advanced/proxy-headers.md new file mode 100644 index 0000000000..ab38575482 --- /dev/null +++ b/guide/content/ko/guide/advanced/proxy-headers.md @@ -0,0 +1,576 @@ +# Proxy configuration + +When you use a reverse proxy server (e.g. nginx), the value of `request.ip` will contain the IP of a proxy, typically `127.0.0.1`. Almost always, this is **not** what you will want. + +Sanic may be configured to use proxy headers for determining the true client IP, available as `request.remote_addr`. The full external URL is also constructed from header fields _if available_. + +.. tip:: Heads up + +``` +Without proper precautions, a malicious client may use proxy headers to spoof its own IP. To avoid such issues, Sanic does not use any proxy headers unless explicitly enabled. +``` + +.. column:: + +``` +Services behind reverse proxies must configure one or more of the following [configuration values](/guide/deployment/configuration.md): + +- `FORWARDED_SECRET` +- `REAL_IP_HEADER` +- `PROXIES_COUNT` +``` + +.. column:: + +```` +```python +app.config.FORWARDED_SECRET = "super-duper-secret" +app.config.REAL_IP_HEADER = "CF-Connecting-IP" +app.config.PROXIES_COUNT = 2 +``` +```` + +## Forwarded header + +In order to use the `Forwarded` header, you should set `app.config.FORWARDED_SECRET` to a value known to the trusted proxy server. The secret is used to securely identify a specific proxy server. + +Sanic ignores any elements without the secret key, and will not even parse the header if no secret is set. + +All other proxy headers are ignored once a trusted forwarded element is found, as it already carries complete information about the client. + +To learn more about the `Forwarded` header, read the related [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded) and [Nginx](https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/) articles. + +## Traditional proxy headers + +### IP Headers + +When your proxy forwards you the IP address in a known header, you can tell Sanic what that is with the `REAL_IP_HEADER` config value. + +### X-Forwarded-For + +This header typically contains a chain of IP addresses through each layer of a proxy. Setting `PROXIES_COUNT` tells Sanic how deep to look to get an actual IP address for the client. This value should equal the _expected_ number of IP addresses in the chain. + +### Other X-headers + +If a client IP is found by one of these methods, Sanic uses the following headers for URL parts: + +- x-forwarded-proto +- x-forwarded-host +- x-forwarded-port +- x-forwarded-path +- x-scheme + +## Examples + +In the following examples, all requests will assume that the endpoint looks like this: + +```python +@app.route("/fwd") +async def forwarded(request): + return json( + { + "remote_addr": request.remote_addr, + "scheme": request.scheme, + "server_name": request.server_name, + "server_port": request.server_port, + "forwarded": request.forwarded, + } + ) +``` + +--- + +##### Example 1 + +Without configured FORWARDED_SECRET, x-headers should be respected + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=1.1.1.1, for=injected;host=", for="[::2]";proto=https;host=me.tld;path="/app/";secret=mySecret,for=broken;;secret=b0rked, for=127.0.0.3;scheme=http;port=1234' \ + -H "X-Real-IP: 127.0.0.2" \ + -H "X-Forwarded-For: 127.0.1.1" \ + -H "X-Scheme: ws" \ + -H "Host: local.site" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "127.0.0.2", + "scheme": "ws", + "server_name": "local.site", + "server_port": 80, + "forwarded": { + "for": "127.0.0.2", + "proto": "ws" + } +} +``` +```` + +--- + +##### Example 2 + +FORWARDED_SECRET now configured + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=1.1.1.1, for=injected;host=", for="[::2]";proto=https;host=me.tld;path="/app/";secret=mySecret,for=broken;;secret=b0rked, for=127.0.0.3;scheme=http;port=1234' \ + -H "X-Real-IP: 127.0.0.2" \ + -H "X-Forwarded-For: 127.0.1.1" \ + -H "X-Scheme: ws" \ + -H "Host: local.site" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "[::2]", + "scheme": "https", + "server_name": "me.tld", + "server_port": 443, + "forwarded": { + "for": "[::2]", + "proto": "https", + "host": "me.tld", + "path": "/app/", + "secret": "mySecret" + } +} +``` +```` + +--- + +##### Example 3 + +Empty Forwarded header -> use X-headers + +```sh +curl localhost:8000/fwd \ + -H "X-Real-IP: 127.0.0.2" \ + -H "X-Forwarded-For: 127.0.1.1" \ + -H "X-Scheme: ws" \ + -H "Host: local.site" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "127.0.0.2", + "scheme": "ws", + "server_name": "local.site", + "server_port": 80, + "forwarded": { + "for": "127.0.0.2", + "proto": "ws" + } +} +``` +```` + +--- + +##### Example 4 + +Header present but not matching anything + +```sh +curl localhost:8000/fwd \ + -H "Forwarded: nomatch" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": {} +} + +``` +```` + +--- + +##### Example 5 + +Forwarded header present but no matching secret -> use X-headers + +```sh +curl localhost:8000/fwd \ + -H "Forwarded: for=1.1.1.1;secret=x, for=127.0.0.1" \ + -H "X-Real-IP: 127.0.0.2" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "127.0.0.2", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "for": "127.0.0.2" + } +} +``` +```` + +--- + +##### Example 6 + +Different formatting and hitting both ends of the header + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: Secret="mySecret";For=127.0.0.4;Port=1234' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "127.0.0.4", + "scheme": "http", + "server_name": "localhost", + "server_port": 1234, + "forwarded": { + "secret": "mySecret", + "for": "127.0.0.4", + "port": 1234 + } +} +``` +```` + +--- + +##### Example 7 + +Test escapes (modify this if you see anyone implementing quoted-pairs) + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=test;quoted="\,x=x;y=\";secret=mySecret' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "test", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "for": "test", + "quoted": "\\,x=x;y=\\", + "secret": "mySecret" + } +} +``` +```` + +--- + +##### Example 8 + +Secret insulated by malformed field #1 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=test;secret=mySecret;b0rked;proto=wss;' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "test", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "for": "test", + "secret": "mySecret" + } +} +``` +```` + +--- + +##### Example 9 + +Secret insulated by malformed field #2 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=test;b0rked;secret=mySecret;proto=wss' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "", + "scheme": "wss", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "secret": "mySecret", + "proto": "wss" + } +} +``` +```` + +--- + +##### Example 10 + +Unexpected termination should not lose existing acceptable values + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: b0rked;secret=mySecret;proto=wss' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "", + "scheme": "wss", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "secret": "mySecret", + "proto": "wss" + } +} +``` +```` + +--- + +##### Example 11 + +Field normalization + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: PROTO=WSS;BY="CAFE::8000";FOR=unknown;PORT=X;HOST="A:2";PATH="/With%20Spaces%22Quoted%22/sanicApp?key=val";SECRET=mySecret' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "", + "scheme": "wss", + "server_name": "a", + "server_port": 2, + "forwarded": { + "proto": "wss", + "by": "[cafe::8000]", + "host": "a:2", + "path": "/With Spaces\"Quoted\"/sanicApp?key=val", + "secret": "mySecret" + } +} +``` +```` + +--- + +##### Example 12 + +Using "by" field as secret + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=1.2.3.4; by=_proxySecret' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "_proxySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "1.2.3.4", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "for": "1.2.3.4", + "by": "_proxySecret" + } +} + +``` +```` diff --git a/guide/content/ko/guide/advanced/signals.md b/guide/content/ko/guide/advanced/signals.md new file mode 100644 index 0000000000..971582bb1e --- /dev/null +++ b/guide/content/ko/guide/advanced/signals.md @@ -0,0 +1,400 @@ +# Signals + +Signals provide a way for one part of your application to tell another part that something happened. + +```python +@app.signal("user.registration.created") +async def send_registration_email(**context): + await send_email(context["email"], template="registration") + +@app.post("/register") +async def handle_registration(request): + await do_registration(request) + await request.app.dispatch( + "user.registration.created", + context={"email": request.json.email} + }) +``` + +## Adding a signal + +.. column:: + +``` +The API for adding a signal is very similar to adding a route. +``` + +.. column:: + +```` +```python +async def my_signal_handler(): + print("something happened") + +app.add_signal(my_signal_handler, "something.happened.ohmy") +``` +```` + +.. column:: + +``` +But, perhaps a slightly more convenient method is to use the built-in decorators. +``` + +.. column:: + +```` +```python +@app.signal("something.happened.ohmy") +async def my_signal_handler(): + print("something happened") +``` +```` + +.. column:: + +``` +If the signal requires conditions, make sure to add them while adding the handler. +``` + +.. column:: + +```` +```python +async def my_signal_handler1(): + print("something happened") + +app.add_signal( + my_signal_handler, + "something.happened.ohmy1", + conditions={"some_condition": "value"} +) + +@app.signal("something.happened.ohmy2", conditions={"some_condition": "value"}) +async def my_signal_handler2(): + print("something happened") +``` +```` + +.. column:: + +``` +Signals can also be declared on blueprints +``` + +.. column:: + +```` +```python +bp = Blueprint("foo") + +@bp.signal("something.happened.ohmy") +async def my_signal_handler(): + print("something happened") +``` +```` + +## Built-in signals + +In addition to creating a new signal, there are a number of built-in signals that are dispatched from Sanic itself. These signals exist to provide developers with more opportunities to add functionality into the request and server lifecycles. + +_Added in v21.9_ + +.. column:: + +``` +You can attach them just like any other signal to an application or blueprint instance. +``` + +.. column:: + +```` +```python +@app.signal("http.lifecycle.complete") +async def my_signal_handler(conn_info): + print("Connection has been closed") +``` +```` + +These signals are the signals that are available, along with the arguments that the handlers take, and the conditions that attach (if any). + +| Event name | Arguments | Conditions | +| -------------------------- | ------------------------------- | --------------------------------------------------------- | +| `http.routing.before` | request | | +| `http.routing.after` | request, route, kwargs, handler | | +| `http.handler.before` | request | | +| `http.handler.after` | request | | +| `http.lifecycle.begin` | conn_info | | +| `http.lifecycle.read_head` | head | | +| `http.lifecycle.request` | request | | +| `http.lifecycle.handle` | request | | +| `http.lifecycle.read_body` | body | | +| `http.lifecycle.exception` | request, exception | | +| `http.lifecycle.response` | request, response | | +| `http.lifecycle.send` | data | | +| `http.lifecycle.complete` | conn_info | | +| `http.middleware.before` | request, response | `{"attach_to": "request"}` or `{"attach_to": "response"}` | +| `http.middleware.after` | request, response | `{"attach_to": "request"}` or `{"attach_to": "response"}` | +| `server.exception.report` | app, exception | | +| `server.init.before` | app, loop | | +| `server.init.after` | app, loop | | +| `server.shutdown.before` | app, loop | | +| `server.shutdown.after` | app, loop | | + +Version 22.9 added `http.handler.before` and `http.handler.after`. + +Version 23.6 added `server.exception.report`. + +.. column:: + +``` +To make using the built-in signals easier, there is an `Enum` object that contains all of the allowed built-ins. With a modern IDE this will help so that you do not need to remember the full list of event names as strings. + +*Added in v21.12* +``` + +.. column:: + +```` +```python +from sanic.signals import Event + +@app.signal(Event.HTTP_LIFECYCLE_COMPLETE) +async def my_signal_handler(conn_info): + print("Connection has been closed") +``` +```` + +## Events + +.. column:: + +``` +Signals are based off of an _event_. An event, is simply a string in the following pattern: +``` + +.. column:: + +```` +``` +namespace.reference.action +``` +```` + +.. tip:: Events must have three parts. If you do not know what to use, try these patterns: + +``` +- `my_app.something.happened` +- `sanic.notice.hello` +``` + +### Event parameters + +.. column:: + +``` +An event can be "dynamic" and declared using the same syntax as [path parameters](../basics/routing.md#path-parameters). This allows matching based upon arbitrary values. +``` + +.. column:: + +```` +```python +@app.signal("foo.bar.") +async def signal_handler(thing): + print(f"[signal_handler] {thing=}") + +@app.get("/") +async def trigger(request): + await app.dispatch("foo.bar.baz") + return response.text("Done.") +``` +```` + +Checkout [path parameters](../basics/routing.md#path-parameters) for more information on allowed type definitions. + +.. info:: Only the third part of an event (the action) may be dynamic: + +``` +- `foo.bar.` 🆗 +- `foo..baz` ❌ +``` + +### Waiting + +.. column:: + +``` +In addition to executing a signal handler, your application can wait for an event to be triggered. +``` + +.. column:: + +```` +```python +await app.event("foo.bar.baz") +``` +```` + +.. column:: + +``` +**IMPORTANT**: waiting is a blocking function. Therefore, you likely will want this to run in a [background task](../basics/tasks.md). +``` + +.. column:: + +```` +```python +async def wait_for_event(app): + while True: + print("> waiting") + await app.event("foo.bar.baz") + print("> event found\n") + +@app.after_server_start +async def after_server_start(app, loop): + app.add_task(wait_for_event(app)) +``` +```` + +.. column:: + +``` +If your event was defined with a dynamic path, you can use `*` to catch any action. +``` + +.. column:: + +```` +```python +@app.signal("foo.bar.") + +... + +await app.event("foo.bar.*") +``` +```` + +## Dispatching + +_In the future, Sanic will dispatch some events automatically to assist developers to hook into life cycle events._ + +.. column:: + +``` +Dispatching an event will do two things: + +1. execute any signal handlers defined on the event, and +2. resolve anything that is "waiting" for the event to complete. +``` + +.. column:: + +```` +```python +@app.signal("foo.bar.") +async def foo_bar(thing): + print(f"{thing=}") + +await app.dispatch("foo.bar.baz") +``` +``` +thing=baz +``` +```` + +### Context + +.. column:: + +``` +Sometimes you may find the need to pass extra information into the signal handler. In our first example above, we wanted our email registration process to have the email address for the user. +``` + +.. column:: + +```` +```python +@app.signal("user.registration.created") +async def send_registration_email(**context): + print(context) + +await app.dispatch( + "user.registration.created", + context={"hello": "world"} +) +``` +``` +{'hello': 'world'} +``` +```` + +.. tip:: FYI + +``` +Signals are dispatched in a background task. +``` + +### Blueprints + +Dispatching blueprint signals works similar in concept to [middleware](../basics/middleware.md). Anything that is done from the app level, will trickle down to the blueprints. However, dispatching on a blueprint, will only execute the signals that are defined on that blueprint. + +.. column:: + +``` +Perhaps an example is easier to explain: +``` + +.. column:: + +```` +```python +bp = Blueprint("bp") + +app_counter = 0 +bp_counter = 0 + +@app.signal("foo.bar.baz") +def app_signal(): + nonlocal app_counter + app_counter += 1 + +@bp.signal("foo.bar.baz") +def bp_signal(): + nonlocal bp_counter + bp_counter += 1 +``` +```` + +.. column:: + +``` +Running `app.dispatch("foo.bar.baz")` will execute both signals. +``` + +.. column:: + +```` +```python +await app.dispatch("foo.bar.baz") +assert app_counter == 1 +assert bp_counter == 1 +``` +```` + +.. column:: + +``` +Running `bp.dispatch("foo.bar.baz")` will execute only the blueprint signal. +``` + +.. column:: + +```` +```python +await bp.dispatch("foo.bar.baz") +assert app_counter == 1 +assert bp_counter == 2 +``` +```` diff --git a/guide/content/ko/guide/advanced/streaming.md b/guide/content/ko/guide/advanced/streaming.md new file mode 100644 index 0000000000..646e645137 --- /dev/null +++ b/guide/content/ko/guide/advanced/streaming.md @@ -0,0 +1,169 @@ +# Streaming + +## Request streaming + +Sanic allows you to stream data sent by the client to begin processing data as the bytes arrive. + +.. column:: + +``` +When enabled on an endpoint, you can stream the request body using `await request.stream.read()`. + +That method will return `None` when the body is completed. +``` + +.. column:: + +```` +```python +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") + return text(result) +``` +```` + +.. column:: + +``` +It also can be enabled with a keyword argument in the decorator... +``` + +.. column:: + +```` +```python +@app.post("/stream", stream=True) +async def handler(request): + ... + body = await request.stream.read() + ... +``` +```` + +.. column:: + +``` +... or the `add_route()` method. +``` + +.. column:: + +```` +```python +bp.add_route( + bp_handler, + "/bp_stream", + methods=["POST"], + stream=True, +) +``` +```` + +.. tip:: FYI + +``` +Only post, put and patch decorators have stream argument. +``` + +## Response streaming + +.. column:: + +``` +Sanic allows you to stream content to the client. +``` + +.. column:: + +```` +```python +@app.route("/") +async def test(request): + response = await request.respond(content_type="text/csv") + await response.send("foo,") + await response.send("bar") + + # Optionally, you can explicitly end the stream by calling: + await response.eof() +``` +```` + +This is useful in situations where you want to stream content to the client that originates in an external service, like a database. For example, you can stream database records to the client with the asynchronous cursor that `asyncpg` provides. + +```python +@app.route("/") +async def index(request): + response = await request.respond() + conn = await asyncpg.connect(database='test') + async with conn.transaction(): + async for record in conn.cursor('SELECT generate_series(0, 10)'): + await response.send(record[0]) +``` + +You can explicitly end a stream by calling `await response.eof()`. It a convenience method to replace `await response.send("", True)`. It should be called **one time** _after_ your handler has determined that it has nothing left to send back to the client. While it is _optional_ to use with Sanic server, if you are running Sanic in ASGI mode, then you **must** explicitly terminate the stream. + +_Calling `eof` became optional in v21.6_ + +## File streaming + +.. column:: + +``` +Sanic provides `sanic.response.file_stream` function that is useful when you want to send a large file. It returns a `StreamingHTTPResponse` object and will use chunked transfer encoding by default; for this reason Sanic doesn’t add `Content-Length` HTTP header in the response. + +A typical use case might be streaming an video file. +``` + +.. column:: + +```` +```python +@app.route("/mp4") +async def handler_file_stream(request): + return await response.file_stream( + "/path/to/sample.mp4", + chunk_size=1024, + mime_type="application/metalink4+xml", + headers={ + "Content-Disposition": 'Attachment; filename="nicer_name.meta4"', + "Content-Type": "application/metalink4+xml", + }, + ) +``` +```` + +.. column:: + +``` +If you want to use the `Content-Length` header, you can disable chunked transfer encoding and add it manually simply by adding the `Content-Length` header. +``` + +.. column:: + +```` +```python +from aiofiles import os as async_os +from sanic.response import file_stream + +@app.route("/") +async def index(request): + file_path = "/srv/www/whatever.png" + + file_stat = await async_os.stat(file_path) + headers = {"Content-Length": str(file_stat.st_size)} + + return await file_stream( + file_path, + headers=headers, + ) +``` +```` diff --git a/guide/content/ko/guide/advanced/versioning.md b/guide/content/ko/guide/advanced/versioning.md new file mode 100644 index 0000000000..9225c23622 --- /dev/null +++ b/guide/content/ko/guide/advanced/versioning.md @@ -0,0 +1,189 @@ +# Versioning + +It is standard practice in API building to add versions to your endpoints. This allows you to easily differentiate incompatible endpoints when you try and change your API down the road in a breaking manner. + +Adding a version will add a `/v{version}` url prefix to your endpoints. + +The version can be a `int`, `float`, or `str`. Acceptable values: + +- `1`, `2`, `3` +- `1.1`, `2.25`, `3.0` +- `"1"`, `"v1"`, `"v1.1"` + +## Per route + +.. column:: + +``` +You can pass a version number to the routes directly. +``` + +.. column:: + +```` +```python +# /v1/text +@app.route("/text", version=1) +def handle_request(request): + return response.text("Hello world! Version 1") + +# /v2/text +@app.route("/text", version=2) +def handle_request(request): + return response.text("Hello world! Version 2") +``` +```` + +## Per Blueprint + +.. column:: + +``` +You can also pass a version number to the blueprint, which will apply to all routes in that blueprint. +``` + +.. column:: + +```` +```python +bp = Blueprint("test", url_prefix="/foo", version=1) + +# /v1/foo/html +@bp.route("/html") +def handle_request(request): + return response.html("

Hello world!

") +``` +```` + +## Per Blueprint Group + +.. column:: + +``` +In order to simplify the management of the versioned blueprints, you can provide a version number in the blueprint +group. The same will be inherited to all the blueprint grouped under it if the blueprints don't already override the +same information with a value specified while creating a blueprint instance. + +When using blueprint groups for managing the versions, the following order is followed to apply the Version prefix to +the routes being registered. + +1. Route Level configuration +2. Blueprint level configuration +3. Blueprint Group level configuration + +If we find a more pointed versioning specification, we will pick that over the more generic versioning specification +provided under the Blueprint or Blueprint Group +``` + +.. column:: + +```` +```python +from sanic.blueprints import Blueprint +from sanic.response import json + +bp1 = Blueprint( + name="blueprint-1", + url_prefix="/bp1", + version=1.25, +) +bp2 = Blueprint( + name="blueprint-2", + url_prefix="/bp2", +) + +group = Blueprint.group( + [bp1, bp2], + url_prefix="/bp-group", + version="v2", +) + +# GET /v1.25/bp-group/bp1/endpoint-1 +@bp1.get("/endpoint-1") +async def handle_endpoint_1_bp1(request): + return json({"Source": "blueprint-1/endpoint-1"}) + +# GET /v2/bp-group/bp2/endpoint-2 +@bp2.get("/endpoint-1") +async def handle_endpoint_1_bp2(request): + return json({"Source": "blueprint-2/endpoint-1"}) + +# GET /v1/bp-group/bp2/endpoint-2 +@bp2.get("/endpoint-2", version=1) +async def handle_endpoint_2_bp2(request): + return json({"Source": "blueprint-2/endpoint-2"}) +``` +```` + +## Version prefix + +As seen above, the `version` that is applied to a route is **always** the first segment in the generated URI path. Therefore, to make it possible to add path segments before the version, every place that a `version` argument is passed, you can also pass `version_prefix`. + +The `version_prefix` argument can be defined in: + +- `app.route` and `bp.route` decorators (and all the convenience decorators also) +- `Blueprint` instantiation +- `Blueprint.group` constructor +- `BlueprintGroup` instantiation +- `app.blueprint` registration + +If there are definitions in multiple places, a more specific definition overrides a more general. This list provides that hierarchy. + +The default value of `version_prefix` is `/v`. + +.. column:: + +``` +An often requested feature is to be able to mount versioned routes on `/api`. This can easily be accomplished with `version_prefix`. +``` + +.. column:: + +```` +```python +# /v1/my/path +app.route("/my/path", version=1, version_prefix="/api/v") +``` +```` + +.. column:: + +``` +Perhaps a more compelling usage is to load all `/api` routes into a single `BlueprintGroup`. +``` + +.. column:: + +```` +```python +# /v1/my/path +app = Sanic(__name__) +v2ip = Blueprint("v2ip", url_prefix="/ip", version=2) +api = Blueprint.group(v2ip, version_prefix="/api/version") + +# /api/version2/ip +@v2ip.get("/") +async def handler(request): + return text(request.ip) + +app.blueprint(api) +``` +```` + +We can therefore learn that a route's URI is: + +``` +version_prefix + version + url_prefix + URI definition +``` + +.. tip:: + +```` +Just like with `url_prefix`, it is possible to define path parameters inside a `version_prefix`. It is perfectly legitimate to do this. Just remember that every route will have that parameter injected into the handler. + +```python +version_prefix="//v" +``` +```` + +_Added in v21.6_ diff --git a/guide/content/ko/guide/advanced/websockets.md b/guide/content/ko/guide/advanced/websockets.md new file mode 100644 index 0000000000..71e5b1dbfa --- /dev/null +++ b/guide/content/ko/guide/advanced/websockets.md @@ -0,0 +1,91 @@ +# Websockets + +Sanic provides an easy to use abstraction on top of [websockets](https://websockets.readthedocs.io/en/stable/). + +## Routing + +.. column:: + +``` +Websocket handlers can be hooked up to the router similar to regular handlers. +``` + +.. column:: + +```` +```python +from sanic import Request, Websocket + +async def feed(request: Request, ws: Websocket): + pass + +app.add_websocket_route(feed, "/feed") +``` +```python +from sanic import Request, Websocket + +@app.websocket("/feed") +async def feed(request: Request, ws: Websocket): + pass +``` +```` + +## Handler + +.. column:: + +``` +Typically, a websocket handler will want to hold open a loop. + +It can then use the `send()` and `recv()` methods on the second object injected into the handler. + +This example is a simple endpoint that echos back to the client messages that it receives. +``` + +.. column:: + +```` +```python +from sanic import Request, Websocket + +@app.websocket("/feed") +async def feed(request: Request, ws: Websocket): + while True: + data = "hello!" + print("Sending: " + data) + await ws.send(data) + data = await ws.recv() + print("Received: " + data) +``` +```` + +.. column:: + +``` +You can simplify your loop by just iterating over the `Websocket` object in a for loop. + +*Added in v22.9* +``` + +.. column:: + +```` +```python +from sanic import Request, Websocket + +@app.websocket("/feed") +async def feed(request: Request, ws: Websocket): + async for msg in ws: + await ws.send(msg) +``` +```` + +## Configuration + +See [configuration section](/guide/deployment/configuration.md) for more details, however the defaults are shown below. + +```python +app.config.WEBSOCKET_MAX_SIZE = 2 ** 20 +app.config.WEBSOCKET_PING_INTERVAL = 20 +app.config.WEBSOCKET_PING_TIMEOUT = 20 +``` diff --git a/guide/content/ko/guide/basics/README.md b/guide/content/ko/guide/basics/README.md new file mode 100644 index 0000000000..25dcc52044 --- /dev/null +++ b/guide/content/ko/guide/basics/README.md @@ -0,0 +1 @@ +# Basics diff --git a/guide/content/ko/guide/basics/app.md b/guide/content/ko/guide/basics/app.md new file mode 100644 index 0000000000..4cded1f51a --- /dev/null +++ b/guide/content/ko/guide/basics/app.md @@ -0,0 +1,634 @@ +--- +title: Sanic Application +--- + +# Sanic Application + +See API docs: [sanic.app](/api/sanic.app) + +## Instance + +.. column:: + +``` +The most basic building block is the :class:`sanic.app.Sanic` instance. It is not required, but the custom is to instantiate this in a file called `server.py`. +``` + +.. column:: + +```` +```python +# /path/to/server.py + +from sanic import Sanic + +app = Sanic("MyHelloWorldApp") +``` +```` + +## Application context + +Most applications will have the need to share/reuse data or objects across different parts of the code base. Sanic helps be providing the `ctx` object on application instances. It is a free space for the developer to attach any objects or data that should existe throughout the lifetime of the application. + +.. column:: + +``` +The most common pattern is to attach a database instance to the application. +``` + +.. column:: + +```` +```python +app = Sanic("MyApp") +app.ctx.db = Database() +``` +```` + +.. column:: + +``` +While the previous example will work and is illustrative, it is typically considered best practice to attach objects in one of the two application startup [listeners](./listeners). +``` + +.. column:: + +```` +```python +app = Sanic("MyApp") + +@app.before_server_start +async def attach_db(app, loop): + app.ctx.db = Database() +``` +```` + +## App Registry + +.. column:: + +``` +When you instantiate a Sanic instance, that can be retrieved at a later time from the Sanic app registry. This can be useful, for example, if you need to access your Sanic instance from a location where it is not otherwise accessible. +``` + +.. column:: + +```` +```python +# ./path/to/server.py +from sanic import Sanic + +app = Sanic("my_awesome_server") + +# ./path/to/somewhere_else.py +from sanic import Sanic + +app = Sanic.get_app("my_awesome_server") +``` +```` + +.. column:: + +``` +If you call `Sanic.get_app("non-existing")` on an app that does not exist, it will raise :class:`sanic.exceptions.SanicException` by default. You can, instead, force the method to return a new instance of Sanic with that name. +``` + +.. column:: + +```` +```python +app = Sanic.get_app( + "non-existing", + force_create=True, +) +``` +```` + +.. column:: + +``` +If there is **only one** Sanic instance registered, then calling `Sanic.get_app()` with no arguments will return that instance +``` + +.. column:: + +```` +```python +Sanic("My only app") + +app = Sanic.get_app() +``` +```` + +## Configuration + +.. column:: + +``` +Sanic holds the configuration in the `config` attribute of the `Sanic` instance. Configuration can be modified **either** using dot-notation **OR** like a dictionary. +``` + +.. column:: + +```` +```python +app = Sanic('myapp') + +app.config.DB_NAME = 'appdb' +app.config['DB_USER'] = 'appuser' + +db_settings = { + 'DB_HOST': 'localhost', + 'DB_NAME': 'appdb', + 'DB_USER': 'appuser' +} +app.config.update(db_settings) +``` +```` + +.. note:: Heads up + +```` +Config keys _should_ be uppercase. But, this is mainly by convention, and lowercase will work most of the time. +```python +app.config.GOOD = "yay!" +app.config.bad = "boo" +``` +```` + +There is much [more detail about configuration](../running/configuration.md) later on. + +## Factory pattern + +Many of the examples in these docs will show the instantiation of the :class:`sanic.app.Sanic` instance in a file called `server.py` in the "global scope" (i.e. not inside a function). This is a common pattern for very simple "hello world" style applications, but it is often beneficial to use a factory pattern instead. + +A "factory" is just a function that returns an instance of the object you want to use. This allows you to abstract the instantiation of the object, but also may make it easier to isolate the application instance. + +.. column:: + +``` +A super simple factory pattern could look like this: +``` + +.. column:: + +```` +```python +# ./path/to/server.py +from sanic import Sanic +from .path.to.config import MyConfig +from .path.to.some.blueprint import bp + + +def create_app(config=MyConfig) -> Sanic: + app = Sanic("MyApp", config=config) + app.blueprint(bp) + return app +``` +```` + +.. column:: + +``` +When we get to running Sanic later, you will learn that the Sanic CLI can detect this pattern and use it to run your application. +``` + +.. column:: + +```` +```sh +sanic path.to.server:create_app +``` +```` + +## Customization + +The Sanic application instance can be customized for your application needs in a variety of ways at instantiation. + +For complete details, see the [API docs](/api/sanic.app). + +### Custom configuration + +.. column:: + +``` +This simplest form of custom configuration would be to pass your own object directly into that Sanic application instance + +If you create a custom configuration object, it is *highly* recommended that you subclass the :class:`sanic.config.Config` option to inherit its behavior. You could use this option for adding properties, or your own set of custom logic. + +*Added in v21.6* +``` + +.. column:: + +```` +```python +from sanic.config import Config + +class MyConfig(Config): + FOO = "bar" + +app = Sanic(..., config=MyConfig()) +``` +```` + +.. column:: + +``` +A useful example of this feature would be if you wanted to use a config file in a form that differs from what is [supported](../running/configuration.md#using-sanicupdateconfig). +``` + +.. column:: + +```` +```python +from sanic import Sanic, text +from sanic.config import Config + +class TomlConfig(Config): + def __init__(self, *args, path: str, **kwargs): + super().__init__(*args, **kwargs) + + with open(path, "r") as f: + self.apply(toml.load(f)) + + def apply(self, config): + self.update(self._to_uppercase(config)) + + def _to_uppercase(self, obj: Dict[str, Any]) -> Dict[str, Any]: + retval: Dict[str, Any] = {} + for key, value in obj.items(): + upper_key = key.upper() + if isinstance(value, list): + retval[upper_key] = [ + self._to_uppercase(item) for item in value + ] + elif isinstance(value, dict): + retval[upper_key] = self._to_uppercase(value) + else: + retval[upper_key] = value + return retval + +toml_config = TomlConfig(path="/path/to/config.toml") +app = Sanic(toml_config.APP_NAME, config=toml_config) +``` +```` + +### Custom context + +.. column:: + +``` +By default, the application context is a [`SimpleNamespace()`](https://docs.python.org/3/library/types.html#types.SimpleNamespace) that allows you to set any properties you want on it. However, you also have the option of passing any object whatsoever instead. + +*Added in v21.6* +``` + +.. column:: + +```` +```python +app = Sanic(..., ctx=1) +``` + +```python +app = Sanic(..., ctx={}) +``` + +```python +class MyContext: + ... + +app = Sanic(..., ctx=MyContext()) +``` +```` + +### Custom requests + +.. column:: + +``` +It is sometimes helpful to have your own `Request` class, and tell Sanic to use that instead of the default. One example is if you wanted to modify the default `request.id` generator. + + + +.. note:: Important + + It is important to remember that you are passing the *class* not an instance of the class. +``` + +.. column:: + +```` +```python +import time + +from sanic import Request, Sanic, text + +class NanoSecondRequest(Request): + @classmethod + def generate_id(*_): + return time.time_ns() + +app = Sanic(..., request_class=NanoSecondRequest) + +@app.get("/") +async def handler(request): + return text(str(request.id)) +``` +```` + +### Custom error handler + +.. column:: + +``` +See [exception handling](../best-practices/exceptions.md#custom-error-handling) for more +``` + +.. column:: + +```` +```python +from sanic.handlers import ErrorHandler + +class CustomErrorHandler(ErrorHandler): + def default(self, request, exception): + ''' handles errors that have no error handlers assigned ''' + # You custom error handling logic... + return super().default(request, exception) + +app = Sanic(..., error_handler=CustomErrorHandler()) +``` +```` + +### Custom dumps function + +.. column:: + +``` +It may sometimes be necessary or desirable to provide a custom function that serializes an object to JSON data. +``` + +.. column:: + +```` +```python +import ujson + +dumps = partial(ujson.dumps, escape_forward_slashes=False) +app = Sanic(__name__, dumps=dumps) +``` +```` + +.. column:: + +``` +Or, perhaps use another library or create your own. +``` + +.. column:: + +```` +```python +from orjson import dumps + +app = Sanic("MyApp", dumps=dumps) +``` +```` + +### Custom loads function + +.. column:: + +``` +Similar to `dumps`, you can also provide a custom function for deserializing data. + +*Added in v22.9* +``` + +.. column:: + +```` +```python +from orjson import loads + +app = Sanic("MyApp", loads=loads) +``` +```` + +### Custom typed application + +Beginning in v23.6, the correct type annotation of a default Sanic application instance is: + +```python +sanic.app.Sanic[sanic.config.Config, types.SimpleNamespace] +``` + +It refers to two generic types: + +1. The first is the type of the configuration object. It defaults to :class:`sanic.config.Config`, but can be any subclass of that. +2. The second is the type of the application context. It defaults to [`SimpleNamespace()`](https://docs.python.org/3/library/types.html#types.SimpleNamespace), but can be **any object** as show above. + +Let's look at some examples of how the type will change. + +.. column:: + +``` +Consider this example where we pass a custom subclass of :class:`sanic.config.Config` and a custom context object. +``` + +.. column:: + +```` +```python +from sanic import Sanic +from sanic.config import Config + +class CustomConfig(Config): + pass + +app = Sanic("test", config=CustomConfig()) +reveal_type(app) # N: Revealed type is "sanic.app.Sanic[main.CustomConfig, types.SimpleNamespace]" +``` +``` +sanic.app.Sanic[main.CustomConfig, types.SimpleNamespace] +``` +```` + +.. column:: + +``` +Similarly, when passing a custom context object, the type will change to reflect that. +``` + +.. column:: + +```` +```python +from sanic import Sanic + +class Foo: + pass + +app = Sanic("test", ctx=Foo()) +reveal_type(app) # N: Revealed type is "sanic.app.Sanic[sanic.config.Config, main.Foo]" +``` +``` +sanic.app.Sanic[sanic.config.Config, main.Foo] +``` +```` + +.. column:: + +``` +Of course, you can set both the config and context to custom types. +``` + +.. column:: + +```` +```python +from sanic import Sanic +from sanic.config import Config + +class CustomConfig(Config): + pass + +class Foo: + pass + +app = Sanic("test", config=CustomConfig(), ctx=Foo()) +reveal_type(app) # N: Revealed type is "sanic.app.Sanic[main.CustomConfig, main.Foo]" +``` +``` +sanic.app.Sanic[main.CustomConfig, main.Foo] +``` +```` + +This pattern is particularly useful if you create a custom type alias for your application instance so that you can use it to annotate listeners and handlers. + +```python +# ./path/to/types.py +from sanic.app import Sanic +from sanic.config import Config +from myapp.context import MyContext +from typing import TypeAlias + +MyApp = TypeAlias("MyApp", Sanic[Config, MyContext]) +``` + +```python +# ./path/to/listeners.py +from myapp.types import MyApp + +def add_listeners(app: MyApp): + @app.before_server_start + async def before_server_start(app: MyApp): + # do something with your fully typed app instance + await app.ctx.db.connect() +``` + +```python +# ./path/to/server.py +from myapp.types import MyApp +from myapp.context import MyContext +from myapp.config import MyConfig +from myapp.listeners import add_listeners + +app = Sanic("myapp", config=MyConfig(), ctx=MyContext()) +add_listeners(app) +``` + +_Added in v23.6_ + +### Custom typed request + +Sanic also allows you to customize the type of the request object. This is useful if you want to add custom properties to the request object, or be able to access your custom properties of a typed application instance. + +The correct, default type of a Sanic request instance is: + +```python +sanic.request.Request[ + sanic.app.Sanic[sanic.config.Config, types.SimpleNamespace], + types.SimpleNamespace +] +``` + +It refers to two generic types: + +1. The first is the type of the application instance. It defaults to `sanic.app.Sanic[sanic.config.Config, types.SimpleNamespace]`, but can be any subclass of that. +2. The second is the type of the request context. It defaults to `types.SimpleNamespace`, but can be **any object** as show above in [custom requests](#custom-requests). + +Let's look at some examples of how the type will change. + +.. column:: + +``` +Expanding upon the full example above where there is a type alias for a customized application instance, we can also create a custom request type so that we can access those same type annotations. + +Of course, you do not need type aliases for this to work. We are only showing them here to cut down on the amount of code shown. +``` + +.. column:: + +```` +```python +from sanic import Request +from myapp.types import MyApp +from types import SimpleNamespace + +def add_routes(app: MyApp): + @app.get("/") + async def handler(request: Request[MyApp, SimpleNamespace]): + # do something with your fully typed app instance + results = await request.app.ctx.db.query("SELECT * FROM foo") +``` +```` + +.. column:: + +``` +Perhaps you have a custom request object that generates a custom context object. You can type annotate it to properly access those properties with your IDE as shown here. +``` + +.. column:: + +```` +```python +from sanic import Request, Sanic +from sanic.config import Config + +class CustomConfig(Config): + pass + +class Foo: + pass + +class RequestContext: + foo: Foo + +class CustomRequest(Request[Sanic[CustomConfig, Foo], RequestContext]): + @staticmethod + def make_context() -> RequestContext: + ctx = RequestContext() + ctx.foo = Foo() + return ctx + +app = Sanic( + "test", config=CustomConfig(), ctx=Foo(), request_class=CustomRequest +) + +@app.get("/") +async def handler(request: CustomRequest): + # Full access to typed: + # - custom application configuration object + # - custom application context object + # - custom request context object + pass +``` +```` + +See more information in the [custom request context](./request#custom-request-context) section. + +_Added in v23.6_ diff --git a/guide/content/ko/guide/basics/cookies.md b/guide/content/ko/guide/basics/cookies.md new file mode 100644 index 0000000000..a4d60b504c --- /dev/null +++ b/guide/content/ko/guide/basics/cookies.md @@ -0,0 +1,122 @@ +# Cookies + +## Reading + +.. column:: + +``` +Cookies can be accessed via the `Request` object’s `cookies` dictionary. +``` + +.. column:: + +```` +```python +@app.route("/cookie") +async def test(request): + test_cookie = request.cookies.get("test") + return text(f"Test cookie: {test_cookie}") +``` +```` + +.. tip:: FYI + +``` +💡 The `request.cookies` object is one of a few types that is a dictionary with each value being a `list`. This is because HTTP allows a single key to be reused to send multiple values. + +Most of the time you will want to use the `.get()` method to access the first element and not a `list`. If you do want a `list` of all items, you can use `.getlist()`. + +*Added in v23.3* +``` + +## Writing + +.. column:: + +``` +When returning a response, cookies can be set on the `Response` object: `response.cookies`. This object is an instance of `CookieJar` which is a special sort of dictionary that automatically will write the response headers for you. +``` + +.. column:: + +```` +```python +@app.route("/cookie") +async def test(request): + response = text("There's a cookie up in this response") + response.add_cookie( + "test", + "It worked!", + domain=".yummy-yummy-cookie.com", + httponly=True + ) + return response +``` +```` + +Response cookies can be set like dictionary values and have the following parameters available: + +- `path: str` - The subset of URLs to which this cookie applies. Defaults to `/`. +- `domain: str` - Specifies the domain for which the cookie is valid. An explicitly specified domain must always start with a dot. +- `max_age: int` - Number of seconds the cookie should live for. +- `expires: datetime` - The time for the cookie to expire on the client’s browser. Usually it is better to use max-age instead. +- `secure: bool` - Specifies whether the cookie will only be sent via HTTPS. Defaults to `True`. +- `httponly: bool` - Specifies whether the cookie cannot be read by JavaScript. +- `samesite: str` - Available values: Lax, Strict, and None. Defaults to `Lax`. +- `comment: str` - A comment (metadata). +- `host_prefix: bool` - Whether to add the `__Host-` prefix to the cookie. +- `secure_prefix: bool` - Whether to add the `__Secure-` prefix to the cookie. +- `partitioned: bool` - Whether to mark the cookie as partitioned. + +To better understand the implications and usage of these values, it might be helpful to read the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) on [setting cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie). + +.. tip:: FYI + +``` +By default, Sanic will set the `secure` flag to `True` to ensure that cookies are only sent over HTTPS as a sensible default. This should not be impactful for local development since secure cookies over HTTP should still be sent to `localhost`. For more information, you should read the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies) on [secure cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#Secure). +``` + +## Deleting + +.. column:: + +``` +Cookies can be removed semantically or explicitly. +``` + +.. column:: + +```` +```python +@app.route("/cookie") +async def test(request): + response = text("Time to eat some cookies muahaha") + + # This cookie will be set to expire in 0 seconds + response.delete_cookie("eat_me") + + # This cookie will self destruct in 5 seconds + response.add_cookie("fast_bake", "Be quick!", max_age=5) + + return response +``` + +*Don't forget to add `path` or `domain` if needed!* +```` + +## Eating + +.. column:: + +``` +Sanic likes cookies +``` + +.. column:: + +``` +.. attrs:: + :class: is-size-1 has-text-centered + + 🍪 +``` diff --git a/guide/content/ko/guide/basics/handlers.md b/guide/content/ko/guide/basics/handlers.md new file mode 100644 index 0000000000..90a73ec7a9 --- /dev/null +++ b/guide/content/ko/guide/basics/handlers.md @@ -0,0 +1,217 @@ +# Handlers + +The next important building block are your _handlers_. These are also sometimes called "views". + +In Sanic, a handler is any callable that takes at least a :class:`sanic.request.Request` instance as an argument, and returns either an :class:`sanic.response.HTTPResponse` instance, or a coroutine that does the same. + +.. column:: + +``` +Huh? 😕 + +It is a **function**; either synchronous or asynchronous. + +The job of the handler is to respond to an endpoint and do something. This is where the majority of your business logic will go. +``` + +.. column:: + +```` +```python +def i_am_a_handler(request): + return HTTPResponse() + +async def i_am_ALSO_a_handler(request): + return HTTPResponse() +``` +```` + +Two more important items to note: + +1. You almost _never_ will want to use :class:`sanic.response.HTTPresponse` directly. It is much simpler to use one of the [convenience methods](./response#methods). + + - `from sanic import json` + - `from sanic import html` + - `from sanic import redirect` + - _etc_ +2. As we will see in [the streaming section](../advanced/streaming#response-streaming), you do not always need to return an object. If you use this lower-level API, you can control the flow of the response from within the handler, and a return object is not used. + +.. tip:: Heads up + +``` +If you want to learn more about encapsulating your logic, checkout [class based views](../advanced/class-based-views.md). For now, we will continue forward with just function-based views. +``` + +### A simple function-based handler + +The most common way to create a route handler is to decorate the function. It creates a visually simple identification of a route definition. We'll learn more about [routing soon](./routing.md). + +.. column:: + +``` +Let's look at a practical example. + +- We use a convenience decorator on our app instance: `@app.get()` +- And a handy convenience method for generating out response object: `text()` + +Mission accomplished 💪 +``` + +.. column:: + +```` +```python +from sanic import text + +@app.get("/foo") +async def foo_handler(request): + return text("I said foo!") +``` +```` + +--- + +## A word about _async_... + +.. column:: + +``` +It is entirely possible to write handlers that are synchronous. + +In this example, we are using the _blocking_ `time.sleep()` to simulate 100ms of processing time. Perhaps this represents fetching data from a DB, or a 3rd-party website. + +Using four (4) worker processes and a common benchmarking tool: + +- **956** requests in 30.10s +- Or, about **31.76** requests/second +``` + +.. column:: + +```` +```python +@app.get("/sync") +def sync_handler(request): + time.sleep(0.1) + return text("Done.") +``` +```` + +.. column:: + +``` +Just by changing to the asynchronous alternative `asyncio.sleep()`, we see an incredible change in performance. 🚀 + +Using the same four (4) worker processes: + +- **115,590** requests in 30.08s +- Or, about **3,843.17** requests/second + +.. attrs:: + :class: is-size-2 + + 🤯 +``` + +.. column:: + +```` +```python +@app.get("/async") +async def async_handler(request): + await asyncio.sleep(0.1) + return text("Done.") +``` +```` + +Okay... this is a ridiculously overdramatic result. And any benchmark you see is inherently very biased. This example is meant to over-the-top show the benefit of `async/await` in the web world. Results will certainly vary. Tools like Sanic and other async Python libraries are not magic bullets that make things faster. They make them _more efficient_. + +In our example, the asynchronous version is so much better because while one request is sleeping, it is able to start another one, and another one, and another one, and another one... + +But, this is the point! Sanic is fast because it takes the available resources and squeezes performance out of them. It can handle many requests concurrently, which means more requests per second. + +.. tip:: A common mistake! + +``` +Don't do this! You need to ping a website. What do you use? `pip install your-fav-request-library` 🙈 + +Instead, try using a client that is `async/await` capable. Your server will thank you. Avoid using blocking tools, and favor those that play well in the asynchronous ecosystem. If you need recommendations, check out [Awesome Sanic](https://github.com/mekicha/awesome-sanic). + +Sanic uses [httpx](https://www.python-httpx.org/) inside of its testing package (sanic-testing) 😉. +``` + +--- + +## A fully annotated handler + +For those that are using type annotations... + +```python +from sanic.response import HTTPResponse, text +from sanic.request import Request + +@app.get("/typed") +async def typed_handler(request: Request) -> HTTPResponse: + return text("Done.") +``` + +## Naming your handlers + +All handlers are named automatically. This is useful for debugging, and for generating URLs in templates. When not specified, the name that will be used is the name of the function. + +.. column:: + +``` +For example, this handler will be named `foo_handler`. +``` + +.. column:: + +```` +```python +# Handler name will be "foo_handler" +@app.get("/foo") +async def foo_handler(request): + return text("I said foo!") +``` +```` + +.. column:: + +``` +However, you can override this by passing the `name` argument to the decorator. +``` + +.. column:: + +```` +```python +# Handler name will be "foo" +@app.get("/foo", name="foo") +async def foo_handler(request): + return text("I said foo!") +``` +```` + +.. column:: + +``` +In fact, as you will, there may be times when you **MUST** supply a name. For example, if you use two decorators on the same function, you will need to supply a name for at least one of them. + +If you do not, you will get an error and your app will not start. Names **must** be unique within your app. +``` + +.. column:: + +```` +```python +# Two handlers, same function, +# different names: +# - "foo_arg" +# - "foo" +@app.get("/foo/", name="foo_arg") +@app.get("/foo") +async def foo(request, arg=None): + return text("I said foo!") +``` +```` diff --git a/guide/content/ko/guide/basics/headers.md b/guide/content/ko/guide/basics/headers.md new file mode 100644 index 0000000000..1e85f926d2 --- /dev/null +++ b/guide/content/ko/guide/basics/headers.md @@ -0,0 +1,249 @@ +# Headers + +Request and response headers are available in the `Request` and `HTTPResponse` objects, respectively. They make use of the [`multidict` package](https://multidict.readthedocs.io/en/stable/multidict.html#cimultidict) that allows a single key to have multiple values. + +.. tip:: FYI + +``` +Header keys are converted to *lowercase* when parsed. Capitalization is not considered for headers. +``` + +## Request + +Sanic does attempt to do some normalization on request headers before presenting them to the developer, and also make some potentially meaningful extractions for common use cases. + +.. column:: + +``` +#### Tokens + +Authorization tokens in the form `Token ` or `Bearer ` are extracted to the request object: `request.token`. +``` + +.. column:: + +```` +```python +@app.route("/") +async def handler(request): + return text(request.token) +``` + +```sh +curl localhost:8000 \ + -H "Authorization: Token ABCDEF12345679" +ABCDEF12345679 +``` + +```sh +curl localhost:8000 \ + -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c +``` +```` + +### Proxy headers + +Sanic has special handling for proxy headers. See the [proxy headers](/guide/advanced/proxy-headers.md) section for more details. + +### Host header and dynamic URL construction + +.. column:: + +``` +The *effective host* is available via `request.host`. This is not necessarily the same as the host header, as it prefers proxy-forwarded host and can be forced by the server name setting. + +Webapps should generally use this accessor so that they can function the same no matter how they are deployed. The actual host header, if needed, can be found via `request.headers` + +The effective host is also used in dynamic URL construction via `request.url_for`, which uses the request to determine the external address of a handler. + +.. tip:: Be wary of malicious clients + + These URLs can be manipulated by sending misleading host headers. `app.url_for` should be used instead if this is a concern. +``` + +.. column:: + +```` +```python +app.config.SERVER_NAME = "https://example.com" + +@app.route("/hosts", name="foo") +async def handler(request): + return json( + { + "effective host": request.host, + "host header": request.headers.get("host"), + "forwarded host": request.forwarded.get("host"), + "you are here": request.url_for("foo"), + } + ) +``` + +```sh +curl localhost:8000/hosts +{ + "effective host": "example.com", + "host header": "localhost:8000", + "forwarded host": null, + "you are here": "https://example.com/hosts" +} +``` +```` + +### Other headers + +.. column:: + +``` +All request headers are available on `request.headers`, and can be accessed in dictionary form. Capitalization is not considered for headers, and can be accessed using either uppercase or lowercase keys. +``` + +.. column:: + +```` +```python +@app.route("/") +async def handler(request): + return json( + { + "foo_weakref": request.headers["foo"], + "foo_get": request.headers.get("Foo"), + "foo_getone": request.headers.getone("FOO"), + "foo_getall": request.headers.getall("fOo"), + "all": list(request.headers.items()), + } + ) +``` + +```sh +curl localhost:9999/headers -H "Foo: one" -H "FOO: two"|jq +{ + "foo_weakref": "one", + "foo_get": "one", + "foo_getone": "one", + "foo_getall": [ + "one", + "two" + ], + "all": [ + [ + "host", + "localhost:9999" + ], + [ + "user-agent", + "curl/7.76.1" + ], + [ + "accept", + "*/*" + ], + [ + "foo", + "one" + ], + [ + "foo", + "two" + ] + ] +} +``` +```` + +.. tip:: FYI + +``` +💡 The request.headers object is one of a few types that is a dictionary with each value being a list. This is because HTTP allows a single key to be reused to send multiple values. + +Most of the time you will want to use the .get() or .getone() methods to access the first element and not a list. If you do want a list of all items, you can use .getall(). +``` + +### Request ID + +.. column:: + +``` +Often it is convenient or necessary to track a request by its `X-Request-ID` header. You can easily access that as: `request.id`. +``` + +.. column:: + +```` +```python +@app.route("/") +async def handler(request): + return text(request.id) +``` + +```sh +curl localhost:8000 \ + -H "X-Request-ID: ABCDEF12345679" +ABCDEF12345679 +``` +```` + +## Response + +Sanic will automatically set the following response headers (when appropriate) for you: + +- `content-length` +- `content-type` +- `connection` +- `transfer-encoding` + +In most circumstances, you should never need to worry about setting these headers. + +.. column:: + +``` +Any other header that you would like to set can be done either in the route handler, or a response middleware. +``` + +.. column:: + +```` +```python +@app.route("/") +async def handler(request): + return text("Done.", headers={"content-language": "en-US"}) + +@app.middleware("response") +async def add_csp(request, response): + response.headers["content-security-policy"] = "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';base-uri 'self';form-action 'self'" +``` +```` + +.. column:: + +``` +A common [middleware](middleware.md) you might want is to add a `X-Request-ID` header to every response. As stated above: `request.id` will provide the ID from the incoming request. But, even if no ID was supplied in the request headers, one will be automatically supplied for you. + +[See API docs for more details](https://sanic.readthedocs.io/en/latest/sanic/api_reference.html#sanic.request.Request.id) +``` + +.. column:: + +```` +```python +@app.route("/") +async def handler(request): + return text(str(request.id)) + +@app.on_response +async def add_request_id_header(request, response): + response.headers["X-Request-ID"] = request.id +``` + +```sh +curl localhost:8000 -i +HTTP/1.1 200 OK +X-Request-ID: 805a958e-9906-4e7a-8fe0-cbe83590431b +content-length: 36 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +805a958e-9906-4e7a-8fe0-cbe83590431b +``` +```` diff --git a/guide/content/ko/guide/basics/listeners.md b/guide/content/ko/guide/basics/listeners.md new file mode 100644 index 0000000000..d30d8fb88e --- /dev/null +++ b/guide/content/ko/guide/basics/listeners.md @@ -0,0 +1,326 @@ +# Listeners + +Sanic provides you with eight (8) opportunities to inject an operation into the life cycle of your application server. This does not include the [signals](../advanced/signals.md), which allow further injection customization. + +There are two (2) that run **only** on your main Sanic process (ie, once per call to `sanic server.app`.) + +- `main_process_start` +- `main_process_stop` + +There are also two (2) that run **only** in a reloader process if auto-reload has been turned on. + +- `reload_process_start` +- `reload_process_stop` + +_Added `reload_process_start` and `reload_process_stop` in v22.3_ + +There are four (4) that enable you to execute startup/teardown code as your server starts or closes. + +- `before_server_start` +- `after_server_start` +- `before_server_stop` +- `after_server_stop` + +The life cycle of a worker process looks like this: + +.. mermaid:: + +``` +sequenceDiagram +autonumber +participant Process +participant Worker +participant Listener +participant Handler +Note over Process: sanic server.app +loop + Process->>Listener: @app.main_process_start + Listener->>Handler: Invoke event handler +end +Process->>Worker: Run workers +loop Start each worker + loop + Worker->>Listener: @app.before_server_start + Listener->>Handler: Invoke event handler + end + Note over Worker: Server status: started + loop + Worker->>Listener: @app.after_server_start + Listener->>Handler: Invoke event handler + end + Note over Worker: Server status: ready +end +Process->>Worker: Graceful shutdown +loop Stop each worker + loop + Worker->>Listener: @app.before_server_stop + Listener->>Handler: Invoke event handler + end + Note over Worker: Server status: stopped + loop + Worker->>Listener: @app.after_server_stop + Listener->>Handler: Invoke event handler + end + Note over Worker: Server status: closed +end +loop + Process->>Listener: @app.main_process_stop + Listener->>Handler: Invoke event handler +end +Note over Process: exit +``` + +The reloader process live outside of this worker process inside of a process that is responsible for starting and stopping the Sanic processes. Consider the following example: + +```python +@app.reload_process_start +async def reload_start(*_): + print(">>>>>> reload_start <<<<<<") + +@app.main_process_start +async def main_start(*_): + print(">>>>>> main_start <<<<<<") + +@app.before_server_start +async def before_start(*_): + print(">>>>>> before_start <<<<<<") +``` + +If this application were run with auto-reload turned on, the `reload_start` function would be called once when the reloader process starts. The `main_start` function would also be called once when the main process starts. **HOWEVER**, the `before_start` function would be called once for each worker process that is started, and subsequently every time that a file is saved and the worker is restarted. + +## Attaching a listener + +.. column:: + +``` +The process to setup a function as a listener is similar to declaring a route. + +The currently running `Sanic()` instance is injected into the listener. +``` + +.. column:: + +```` +```python +async def setup_db(app): + app.ctx.db = await db_setup() + +app.register_listener(setup_db, "before_server_start") +``` +```` + +.. column:: + +``` +The `Sanic` app instance also has a convenience decorator. +``` + +.. column:: + +```` +```python +@app.listener("before_server_start") +async def setup_db(app): + app.ctx.db = await db_setup() +``` +```` + +.. column:: + +``` +Prior to v22.3, both the application instance and the current event loop were injected into the function. However, only the application instance is injected by default. If your function signature will accept both, then both the application and the loop will be injected as shown here. +``` + +.. column:: + +```` +```python +@app.listener("before_server_start") +async def setup_db(app, loop): + app.ctx.db = await db_setup() +``` +```` + +.. column:: + +``` +You can shorten the decorator even further. This is helpful if you have an IDE with autocomplete. +``` + +.. column:: + +```` +```python +@app.before_server_start +async def setup_db(app): + app.ctx.db = await db_setup() +``` +```` + +## Order of execution + +Listeners are executed in the order they are declared during startup, and reverse order of declaration during teardown + +| | Phase | Order | +| --------------------- | --------------- | ------------- | +| `main_process_start` | main startup | regular 🙂 ⬇️ | +| `before_server_start` | worker startup | regular 🙂 ⬇️ | +| `after_server_start` | worker startup | regular 🙂 ⬇️ | +| `before_server_stop` | worker shutdown | 🙃 ⬆️ reverse | +| `after_server_stop` | worker shutdown | 🙃 ⬆️ reverse | +| `main_process_stop` | main shutdown | 🙃 ⬆️ reverse | + +Given the following setup, we should expect to see this in the console if we run two workers. + +.. column:: + +```` +```python +@app.listener("before_server_start") +async def listener_1(app, loop): + print("listener_1") + +@app.before_server_start +async def listener_2(app, loop): + print("listener_2") + +@app.listener("after_server_start") +async def listener_3(app, loop): + print("listener_3") + +@app.after_server_start +async def listener_4(app, loop): + print("listener_4") + +@app.listener("before_server_stop") +async def listener_5(app, loop): + print("listener_5") + +@app.before_server_stop +async def listener_6(app, loop): + print("listener_6") + +@app.listener("after_server_stop") +async def listener_7(app, loop): + print("listener_7") + +@app.after_server_stop +async def listener_8(app, loop): + print("listener_8") +``` +```` + +.. column:: + +```` +```bash +[pid: 1000000] [INFO] Goin' Fast @ http://127.0.0.1:9999 +[pid: 1000000] [INFO] listener_0 +[pid: 1111111] [INFO] listener_1 +[pid: 1111111] [INFO] listener_2 +[pid: 1111111] [INFO] listener_3 +[pid: 1111111] [INFO] listener_4 +[pid: 1111111] [INFO] Starting worker [1111111] +[pid: 1222222] [INFO] listener_1 +[pid: 1222222] [INFO] listener_2 +[pid: 1222222] [INFO] listener_3 +[pid: 1222222] [INFO] listener_4 +[pid: 1222222] [INFO] Starting worker [1222222] +[pid: 1111111] [INFO] Stopping worker [1111111] +[pid: 1222222] [INFO] Stopping worker [1222222] +[pid: 1222222] [INFO] listener_6 +[pid: 1222222] [INFO] listener_5 +[pid: 1222222] [INFO] listener_8 +[pid: 1222222] [INFO] listener_7 +[pid: 1111111] [INFO] listener_6 +[pid: 1111111] [INFO] listener_5 +[pid: 1111111] [INFO] listener_8 +[pid: 1111111] [INFO] listener_7 +[pid: 1000000] [INFO] listener_9 +[pid: 1000000] [INFO] Server Stopped +``` +In the above example, notice how there are three processes running: + +- `pid: 1000000` - The *main* process +- `pid: 1111111` - Worker 1 +- `pid: 1222222` - Worker 2 + +*Just because our example groups all of one worker and then all of another, in reality since these are running on separate processes, the ordering between processes is not guaranteed. But, you can be sure that a single worker will **always** maintain its order.* +```` + +.. tip:: FYI + +``` +The practical result of this is that if the first listener in `before_server_start` handler setups a database connection, listeners that are registered after it can rely upon that connection being alive both when they are started and stopped. +``` + +### Priority + +In v23.12, the `priority` keyword argument was added to listeners. This allows for fine-tuning the order of execution of listeners. The default priority is `0`. Listeners with a higher priority will be executed first. Listeners with the same priority will be executed in the order they were registered. Furthermore, listeners attached to the `app` instance will be executed before listeners attached to a `Blueprint` instance. + +Overall the rules for deciding the order of execution are as follows: + +1. Priority in descending order +2. Application listeners before Blueprint listeners +3. Registration order + +.. column:: + +```` +As an example, consider the following, which will print: + +```bash +third +bp_third +second +bp_second +first +fourth +bp_first +``` +```` + +.. column:: + +```` +```python +@app.before_server_start +async def first(app): + print("first") + +@app.listener("before_server_start", priority=2) +async def second(app): + print("second") + +@app.before_server_start(priority=3) +async def third(app): + print("third") + +@bp.before_server_start +async def bp_first(app): + print("bp_first") + +@bp.listener("before_server_start", priority=2) +async def bp_second(app): + print("bp_second") + +@bp.before_server_start(priority=3) +async def bp_third(app): + print("bp_third") + +@app.before_server_start +async def fourth(app): + print("fourth") + +app.blueprint(bp) +``` +```` + +## ASGI Mode + +If you are running your application with an ASGI server, then make note of the following changes: + +- `reload_process_start` and `reload_process_stop` will be **ignored** +- `main_process_start` and `main_process_stop` will be **ignored** +- `before_server_start` will run as early as it can, and will be before `after_server_start`, but technically, the server is already running at that point +- `after_server_stop` will run as late as it can, and will be after `before_server_stop`, but technically, the server is still running at that point diff --git a/guide/content/ko/guide/basics/middleware.md b/guide/content/ko/guide/basics/middleware.md new file mode 100644 index 0000000000..7352f175ca --- /dev/null +++ b/guide/content/ko/guide/basics/middleware.md @@ -0,0 +1,279 @@ +# Middleware + +Whereas listeners allow you to attach functionality to the lifecycle of a worker process, middleware allows you to attach functionality to the lifecycle of an HTTP stream. + +```python +@app.on_request +async def example(request): + print("I execute before the handler.") +``` + +You can execute middleware either _before_ the handler is executed, or _after_. + +```python +@app.on_response +async def example(request, response): + print("I execute after the handler.") +``` + +.. mermaid:: + +``` +sequenceDiagram +autonumber +participant Worker +participant Middleware +participant MiddlewareHandler +participant RouteHandler +Note over Worker: Incoming HTTP request +loop + Worker->>Middleware: @app.on_request + Middleware->>MiddlewareHandler: Invoke middleware handler + MiddlewareHandler-->>Worker: Return response (optional) +end +rect rgba(255, 13, 104, .1) +Worker->>RouteHandler: Invoke route handler +RouteHandler->>Worker: Return response +end +loop + Worker->>Middleware: @app.on_response + Middleware->>MiddlewareHandler: Invoke middleware handler + MiddlewareHandler-->>Worker: Return response (optional) +end +Note over Worker: Deliver response +``` + +## Attaching middleware + +.. column:: + +``` +This should probably look familiar by now. All you need to do is declare when you would like the middleware to execute: on the `request` or on the `response`. +``` + +.. column:: + +```` +```python +async def extract_user(request): + request.ctx.user = await extract_user_from_request(request) + +app.register_middleware(extract_user, "request") +``` +```` + +.. column:: + +``` +Again, the `Sanic` app instance also has a convenience decorator. +``` + +.. column:: + +```` +```python +@app.middleware("request") +async def extract_user(request): + request.ctx.user = await extract_user_from_request(request) +``` +```` + +.. column:: + +``` +Response middleware receives both the `request` and `response` arguments. +``` + +.. column:: + +```` +```python +@app.middleware('response') +async def prevent_xss(request, response): + response.headers["x-xss-protection"] = "1; mode=block" +``` +```` + +.. column:: + +``` +You can shorten the decorator even further. This is helpful if you have an IDE with autocomplete. + +This is the preferred usage, and is what we will use going forward. +``` + +.. column:: + +```` +```python +@app.on_request +async def extract_user(request): + ... + +@app.on_response +async def prevent_xss(request, response): + ... +``` +```` + +## Modification + +Middleware can modify the request or response parameter it is given, _as long as it does not return it_. + +.. column:: + +``` +#### Order of execution + +1. Request middleware: `add_key` +2. Route handler: `index` +3. Response middleware: `prevent_xss` +4. Response middleware: `custom_banner` +``` + +.. column:: + +```` +```python +@app.on_request +async def add_key(request): + # Arbitrary data may be stored in request context: + request.ctx.foo = "bar" + +@app.on_response +async def custom_banner(request, response): + response.headers["Server"] = "Fake-Server" + +@app.on_response +async def prevent_xss(request, response): + response.headers["x-xss-protection"] = "1; mode=block" + +@app.get("/") +async def index(request): + return text(request.ctx.foo) + +``` +```` + +.. column:: + +``` +You can modify the `request.match_info`. A useful feature that could be used, for example, in middleware to convert `a-slug` to `a_slug`. +``` + +.. column:: + +```` +```python +@app.on_request +def convert_slug_to_underscore(request: Request): + request.match_info["slug"] = request.match_info["slug"].replace("-", "_") + +@app.get("/") +async def handler(request, slug): + return text(slug) +``` +``` +$ curl localhost:9999/foo-bar-baz +foo_bar_baz +``` +```` + +## Responding early + +.. column:: + +``` +If middleware returns a `HTTPResponse` object, the request will stop processing and the response will be returned. If this occurs to a request before the route handler is reached, the handler will **not** be called. Returning a response will also prevent any further middleware from running. + +``` + +.. tip:: + +``` +You can return a `None` value to stop the execution of the middleware handler to allow the request to process as normal. This can be useful when using early return to avoid processing requests inside of that middleware handler. +``` + +.. column:: + +```` +```python +@app.on_request +async def halt_request(request): + return text("I halted the request") + +@app.on_response +async def halt_response(request, response): + return text("I halted the response") +``` +```` + +## Order of execution + +Request middleware is executed in the order declared. Response middleware is executed in **reverse order**. + +Given the following setup, we should expect to see this in the console. + +.. column:: + +```` +```python +@app.on_request +async def middleware_1(request): + print("middleware_1") + +@app.on_request +async def middleware_2(request): + print("middleware_2") + +@app.on_response +async def middleware_3(request, response): + print("middleware_3") + +@app.on_response +async def middleware_4(request, response): + print("middleware_4") + +@app.get("/handler") +async def handler(request): + print("~ handler ~") + return text("Done.") +``` +```` + +.. column:: + +```` +```bash +middleware_1 +middleware_2 +~ handler ~ +middleware_4 +middleware_3 +[INFO][127.0.0.1:44788]: GET http://localhost:8000/handler 200 5 +``` +```` + +### Middleware priority + +.. column:: + +``` +You can modify the order of execution of middleware by assigning it a higher priority. This happens inside of the middleware definition. The higher the value, the earlier it will execute relative to other middleware. The default priority for middleware is `0`. +``` + +.. column:: + +```` +```python +@app.on_request +async def low_priority(request): + ... + +@app.on_request(priority=99) +async def high_priority(request): + ... +``` +```` + +_Added in v22.9_ diff --git a/guide/content/ko/guide/basics/request.md b/guide/content/ko/guide/basics/request.md new file mode 100644 index 0000000000..def760cd80 --- /dev/null +++ b/guide/content/ko/guide/basics/request.md @@ -0,0 +1,476 @@ +# Request + +See API docs: [sanic.request](/api/sanic.request) + +The :class:`sanic.request.Request` instance contains **a lot** of helpful information available on its parameters. Refer to the [API documentation](https://sanic.readthedocs.io/) for full details. + +As we saw in the section on [handlers](./handlers), the first argument in a route handler is usually the :class:`sanic.request.Request` object. Because Sanic is an async framework, the handler will run inside of a [`asyncio.Task`](https://docs.python.org/3/library/asyncio-task.html#asyncio.Task) and will be scheduled by the event loop. This means that the handler will be executed in an isolated context and the request object will be unique to that handler's task. + +.. column:: + +``` +By convention, the argument is named `request`, but you can name it whatever you want. The name of the argument is not important. Both of the following handlers are valid. +``` + +.. column:: + +```` +```python +@app.get("/foo") +async def typical_use_case(request): + return text("I said foo!") +``` + +```python +@app.get("/foo") +async def atypical_use_case(req): + return text("I said foo!") +``` +```` + +.. column:: + +``` +Annotating a request object is super simple. + +``` + +.. column:: + +```` +```python +from sanic.request import Request +from sanic.response import text + +@app.get("/typed") +async def typed_handler(request: Request): + return text("Done.") +``` +```` + +.. tip:: + +``` +For your convenience, assuming you are using a modern IDE, you should leverage type annotations to help with code completion and documentation. This is especially helpful when using the `request` object as it has **MANY** properties and methods. + +To see the full list of available properties and methods, refer to the [API documentation](/api/sanic.request). +``` + +## Body + +The `Request` object allows you to access the content of the request body in a few different ways. + +### JSON + +.. column:: + +``` +**Parameter**: `request.json` +**Description**: The parsed JSON object +``` + +.. column:: + +```` +```bash +$ curl localhost:8000 -d '{"foo": "bar"}' +``` + +```python +>>> print(request.json) +{'foo': 'bar'} +``` +```` + +### Raw + +.. column:: + +``` +**Parameter**: `request.body` +**Description**: The raw bytes from the request body +``` + +.. column:: + +```` +```bash +$ curl localhost:8000 -d '{"foo": "bar"}' +``` + +```python +>>> print(request.body) +b'{"foo": "bar"}' +``` +```` + +### Form + +.. column:: + +``` +**Parameter**: `request.form` +**Description**: The form data + +.. tip:: FYI + + The `request.form` object is one of a few types that is a dictionary with each value being a list. This is because HTTP allows a single key to be reused to send multiple values. + + Most of the time you will want to use the `.get()` method to access the first element and not a list. If you do want a list of all items, you can use `.getlist()`. +``` + +.. column:: + +```` +```bash +$ curl localhost:8000 -d 'foo=bar' +``` + +```python +>>> print(request.body) +b'foo=bar' + +>>> print(request.form) +{'foo': ['bar']} + +>>> print(request.form.get("foo")) +bar + +>>> print(request.form.getlist("foo")) +['bar'] +``` +```` + +### Uploaded + +.. column:: + +``` +**Parameter**: `request.files` +**Description**: The files uploaded to the server + +.. tip:: FYI + + The `request.files` object is one of a few types that is a dictionary with each value being a list. This is because HTTP allows a single key to be reused to send multiple values. + + Most of the time you will want to use the `.get()` method to access the first element and not a list. If you do want a list of all items, you can use `.getlist()`. +``` + +.. column:: + +```` +```bash +$ curl -F 'my_file=@/path/to/TEST' http://localhost:8000 +``` + +```python +>>> print(request.body) +b'--------------------------cb566ad845ad02d3\r\nContent-Disposition: form-data; name="my_file"; filename="TEST"\r\nContent-Type: application/octet-stream\r\n\r\nhello\n\r\n--------------------------cb566ad845ad02d3--\r\n' + +>>> print(request.files) +{'my_file': [File(type='application/octet-stream', body=b'hello\n', name='TEST')]} + +>>> print(request.files.get("my_file")) +File(type='application/octet-stream', body=b'hello\n', name='TEST') + +>>> print(request.files.getlist("my_file")) +[File(type='application/octet-stream', body=b'hello\n', name='TEST')] +``` +```` + +## Context + +### Request context + +The `request.ctx` object is your playground to store whatever information you need to about the request. This lives only for the duration of the request and is unique to the request. + +This can be constrasted with the `app.ctx` object which is shared across all requests. Be careful not to confuse them! + +The `request.ctx` object by default is a `SimpleNamespace` object allowing you to set arbitrary attributes on it. Sanic will not use this object for anything, so you are free to use it however you want without worrying about name clashes. + +### Typical use case + +This is often used to store items like authenticated user details. We will get more into [middleware](./middleware.md) later, but here is a simple example. + +```python +@app.on_request +async def run_before_handler(request): + request.ctx.user = await fetch_user_by_token(request.token) + +@app.route('/hi') +async def hi_my_name_is(request): + if not request.ctx.user: + return text("Hmm... I don't know you) + return text(f"Hi, my name is {request.ctx.user.name}") +``` + +As you can see, the `request.ctx` object is a great place to store information that you need to access in multiple handlers making your code more DRY and easier to maintain. But, as we will learn in the [middleware section](./middleware.md), you can also use it to store information from one middleware that will be used in another. + +### Connection context + +.. column:: + +``` +Often times your API will need to serve multiple concurrent (or consecutive) requests to the same client. This happens, for example, very often with progressive web apps that need to query multiple endpoints to get data. + +The HTTP protocol calls for an easing of overhead time caused by the connection with the use of [keep alive headers](../deployment/configuration.md#keep-alive-timeout). + +When multiple requests share a single connection, Sanic provides a context object to allow those requests to share state. +``` + +.. column:: + +```` +```python +@app.on_request +async def increment_foo(request): + if not hasattr(request.conn_info.ctx, "foo"): + request.conn_info.ctx.foo = 0 + request.conn_info.ctx.foo += 1 + +@app.get("/") +async def count_foo(request): + return text(f"request.conn_info.ctx.foo={request.conn_info.ctx.foo}") +``` + +```bash +$ curl localhost:8000 localhost:8000 localhost:8000 +request.conn_info.ctx.foo=1 +request.conn_info.ctx.foo=2 +request.conn_info.ctx.foo=3 +``` +```` + +.. warning:: + +``` +While this looks like a convenient place to store information between requests by a single HTTP connection, do not assume that all requests on a single connection came from a single end user. This is because HTTP proxies and load balancers can multiplex multiple connections into a single connection to your server. + +**DO NOT** use this to store information about a single user. Use the `request.ctx` object for that. +``` + +### Custom Request Objects + +As dicussed in [application customization](./app.md#custom-requests), you can create a subclass of :class:`sanic.request.Request` to add additional functionality to the request object. This is useful for adding additional attributes or methods that are specific to your application. + +.. column:: + +``` +For example, imagine your application sends a custom header that contains a user ID. You can create a custom request object that will parse that header and store the user ID for you. +``` + +.. column:: + +```` +```python +from sanic import Sanic, Request + +class CustomRequest(Request): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.user_id = self.headers.get("X-User-ID") + +app = Sanic("Example", request_class=CustomRequest) +``` +```` + +.. column:: + +``` +Now, in your handlers, you can access the `user_id` attribute. +``` + +.. column:: + +```` +```python +@app.route("/") +async def handler(request: CustomRequest): + return text(f"User ID: {request.user_id}") +``` +```` + +### Custom Request Context + +By default, the request context (`request.ctx`) is a [`Simplenamespace`](https://docs.python.org/3/library/types.html#types.SimpleNamespace) object allowing you to set arbitrary attributes on it. While this is super helpful to reuse logic across your application, it can be difficult in the development experience since the IDE will not know what attributes are available. + +To help with this, you can create a custom request context object that will be used instead of the default `SimpleNamespace`. This allows you to add type hints to the context object and have them be available in your IDE. + +.. column:: + +``` +Start by subclassing the :class:`sanic.request.Request` class to create a custom request type. Then, you will need to add a `make_context()` method that returns an instance of your custom context object. *NOTE: the `make_context` method should be a static method.* +``` + +.. column:: + +```` +```python +from sanic import Sanic, Request +from types import SimpleNamespace + +class CustomRequest(Request): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.ctx.user_id = self.headers.get("X-User-ID") + + @staticmethod + def make_context() -> CustomContext: + return CustomContext() + +@dataclass +class CustomContext: + user_id: str = None +``` +```` + +.. note:: + +``` +This is a Sanic poweruser feature that makes it super convenient in large codebases to have typed request context objects. It is of course not required, but can be very helpful. +``` + +_Added in v23.6_ + +## Parameters + +.. column:: + +``` +Values that are extracted from the path parameters are injected into the handler as argumets, or more specifically as keyword arguments. There is much more detail about this in the [Routing section](./routing.md). +``` + +.. column:: + +```` +```python +@app.route('/tag/') +async def tag_handler(request, tag): + return text("Tag - {}".format(tag)) + +# or, explicitly as keyword arguments +@app.route('/tag/') +async def tag_handler(request, *, tag): + return text("Tag - {}".format(tag)) +``` +```` + +## Arguments + +There are two attributes on the `request` instance to get query parameters: + +- `request.args` +- `request.query_args` + +These allow you to access the query parameters from the request path (the part after the `?` in the URL). + +### Typical use case + +In most use cases, you will want to use the `request.args` object to access the query parameters. This will be the parsed query string as a dictionary. + +This is by far the most common pattern. + +.. column:: + +``` +Consider the example where we have a `/search` endpoint with a `q` parameter that we want to use to search for something. +``` + +.. column:: + +```` +```python +@app.get("/search") +async def search(request): + query = request.args.get("q") + if not query: + return text("No query string provided") + return text(f"Searching for: {query}") +``` +```` + +### Parsing the query string + +Sometimes, however, you may want to access the query string as a raw string or as a list of tuples. For this, you can use the `request.query_string` and `request.query_args` attributes. + +It also should be noted that HTTP allows multiple values for a single key. Although `request.args` may seem like a regular dictionary, it is actually a special type that allows for multiple values for a single key. You can access this by using the `request.args.getlist()` method. + +- `request.query_string` - The raw query string +- `request.query_args` - The parsed query string as a list of tuples +- `request.args` - The parsed query string as a _special_ dictionary + - `request.args.get()` - Get the first value for a key (behaves like a regular dictionary) + - `request.args.getlist()` - Get all values for a key + +```sh +curl "http://localhost:8000?key1=val1&key2=val2&key1=val3" +``` + +```python +>>> print(request.args) +{'key1': ['val1', 'val3'], 'key2': ['val2']} + +>>> print(request.args.get("key1")) +val1 + +>>> print(request.args.getlist("key1")) +['val1', 'val3'] + +>>> print(request.query_args) +[('key1', 'val1'), ('key2', 'val2'), ('key1', 'val3')] + +>>> print(request.query_string) +key1=val1&key2=val2&key1=val3 + +``` + +.. tip:: FYI + +``` +The `request.args` object is one of a few types that is a dictionary with each value being a list. This is because HTTP allows a single key to be reused to send multiple values. + +Most of the time you will want to use the `.get()` method to access the first element and not a list. If you do want a list of all items, you can use `.getlist()`. +``` + +## Current request getter + +Sometimes you may find that you need access to the current request in your application in a location where it is not accessible. A typical example might be in a `logging` format. You can use `Request.get_current()` to fetch the current request (if any). + +Remember, the request object is confined to the single [`asyncio.Task`](https://docs.python.org/3/library/asyncio-task.html#asyncio.Task) that is running the handler. If you are not in that task, there is no request object. + +```python +import logging + +from sanic import Request, Sanic, json +from sanic.exceptions import SanicException +from sanic.log import LOGGING_CONFIG_DEFAULTS + +LOGGING_FORMAT = ( + "%(asctime)s - (%(name)s)[%(levelname)s][%(host)s]: " + "%(request_id)s %(request)s %(message)s %(status)d %(byte)d" +) + +old_factory = logging.getLogRecordFactory() + +def record_factory(*args, **kwargs): + record = old_factory(*args, **kwargs) + record.request_id = "" + + try: + request = Request.get_current() + except SanicException: + ... + else: + record.request_id = str(request.id) + + return record + +logging.setLogRecordFactory(record_factory) + + +LOGGING_CONFIG_DEFAULTS["formatters"]["access"]["format"] = LOGGING_FORMAT +app = Sanic("Example", log_config=LOGGING_CONFIG_DEFAULTS) +``` + +In this example, we are adding the `request.id` to every access log message. + +_Added in v22.6_ diff --git a/guide/content/ko/guide/basics/response.md b/guide/content/ko/guide/basics/response.md new file mode 100644 index 0000000000..5efe8c3ad3 --- /dev/null +++ b/guide/content/ko/guide/basics/response.md @@ -0,0 +1,299 @@ +# Response + +All [handlers](./handlers.md) _usually_ return a response object, and [middleware](./middleware.md) may optionally return a response object. + +To clarify that statement: + +- unless the handler is a streaming endpoint handling its own pattern for sending bytes to the client, the return value must be an instance of :class:`sanic.response.HTTPResponse` (to learn more about this exception see [streaming responses](../advanced/streaming.md#response-streaming)). In **most** use cases, you will need to return a response. +- if a middleware does return a response object, that will be used instead of whatever the handler would do (see [middleware](./middleware.md) to learn more). + +A most basic handler would look like the following. The :class:`sanic.response.HTTPResponse` object will allow you to set the status, body, and headers to be returned to the client. + +```python +from sanic import HTTPResponse, Sanic + +app = Sanic("TestApp") + +@app.route("") +def handler(_): + return HTTPResponse() +``` + +However, usually it is easier to use one of the convenience methods discussed below. + +## Methods + +The easiest way to generate a response object is to use one of the convenience functions. + +### Text + +.. column:: + +``` +**Default Content-Type**: `text/plain; charset=utf-8` +**Description**: Returns plain text +``` + +.. column:: + +```` +```python +from sanic import text + +@app.route("/") +async def handler(request): + return text("Hi 😎") +``` +```` + +### HTML + +.. column:: + +``` +**Default Content-Type**: `text/html; charset=utf-8` +**Description**: Returns an HTML document +``` + +.. column:: + +```` +```python +from sanic import html + +@app.route("/") +async def handler(request): + return html('
Hi 😎
') +``` +```` + +### JSON + +.. column:: + +``` +**Default Content-Type**: `application/json` +**Description**: Returns a JSON document +``` + +.. column:: + +```` +```python +from sanic import json + +@app.route("/") +async def handler(request): + return json({"foo": "bar"}) +``` +```` + +By default, Sanic ships with [`ujson`](https://github.com/ultrajson/ultrajson) as its JSON encoder of choice. If `ujson` is not installed, it will fall back to the standard library `json` module. + +It is super simple to change this if you want. + +```python +from sanic import json +from orjson import dumps + +json({"foo": "bar"}, dumps=dumps) +``` + +You may additionally declare which implementation to use globally across your application at initialization: + +```python +from orjson import dumps + +app = Sanic(..., dumps=dumps) +``` + +### File + +.. column:: + +``` +**Default Content-Type**: N/A +**Description**: Returns a file +``` + +.. column:: + +```` +```python +from sanic import file + +@app.route("/") +async def handler(request): + return await file("/path/to/whatever.png") +``` +```` + +Sanic will examine the file, and try and guess its mime type and use an appropriate value for the content type. You could be explicit, if you would like: + +```python +file("/path/to/whatever.png", mime_type="image/png") +``` + +You can also choose to override the file name: + +```python +file("/path/to/whatever.png", filename="super-awesome-incredible.png") +``` + +### File Streaming + +.. column:: + +``` +**Default Content-Type**: N/A +**Description**: Streams a file to a client, useful when streaming large files, like a video +``` + +.. column:: + +```` +```python +from sanic.response import file_stream + +@app.route("/") +async def handler(request): + return await file_stream("/path/to/whatever.mp4") +``` +```` + +Like the `file()` method, `file_stream()` will attempt to determine the mime type of the file. + +### Raw + +.. column:: + +``` +**Default Content-Type**: `application/octet-stream` +**Description**: Send raw bytes without encoding the body +``` + +.. column:: + +```` +```python +from sanic import raw + +@app.route("/") +async def handler(request): + return raw(b"raw bytes") +``` +```` + +### Redirect + +.. column:: + +``` +**Default Content-Type**: `text/html; charset=utf-8` +**Description**: Send a `302` response to redirect the client to a different path +``` + +.. column:: + +```` +```python +from sanic import redirect + +@app.route("/") +async def handler(request): + return redirect("/login") +``` +```` + +### Empty + +.. column:: + +``` +**Default Content-Type**: N/A +**Description**: For responding with an empty message as defined by [RFC 2616](https://tools.ietf.org/search/rfc2616#section-7.2.1) +``` + +.. column:: + +```` +```python +from sanic import empty + +@app.route("/") +async def handler(request): + return empty() +``` + +Defaults to a `204` status. +```` + +## Default status + +The default HTTP status code for the response is `200`. If you need to change it, it can be done by the response method. + +```python +@app.post("/") +async def create_new(request): + new_thing = await do_create(request) + return json({"created": True, "id": new_thing.thing_id}, status=201) +``` + +## Returning JSON data + +Starting in v22.12, When you use the `sanic.json` convenience method, it will return a subclass of `HTTPResponse` called :class:`sanic.response.types.JSONResponse`. This object will +have several convenient methods available to modify common JSON body. + +```python +from sanic import json + +resp = json(...) +``` + +- `resp.set_body()` - Set the body of the JSON object to the value passed +- `resp.append()` - Append a value to the body like `list.append` (only works if the root JSON is an array) +- `resp.extend()` - Extend a value to the body like `list.extend` (only works if the root JSON is an array) +- `resp.update()` - Update the body with a value like `dict.update` (only works if the root JSON is an object) +- `resp.pop()` - Pop a value like `list.pop` or `dict.pop` (only works if the root JSON is an array or an object) + +.. warning:: + +``` +The raw Python object is stored on the `JSONResponse` object as `raw_body`. While it is safe to overwrite this value with a new one, you should **not** attempt to mutate it. You should instead use the methods listed above. +``` + +```python +resp = json({"foo": "bar"}) + +# This is OKAY +resp.raw_body = {"foo": "bar", "something": "else"} + +# This is better +resp.set_body({"foo": "bar", "something": "else"}) + +# This is also works well +resp.update({"something": "else"}) + +# This is NOT OKAY +resp.raw_body.update({"something": "else"}) +``` + +```python +# Or, even treat it like a list +resp = json(["foo", "bar"]) + +# This is OKAY +resp.raw_body = ["foo", "bar", "something", "else"] + +# This is better +resp.extend(["something", "else"]) + +# This is also works well +resp.append("something") +resp.append("else") + +# This is NOT OKAY +resp.raw_body.append("something") +``` + +_Added in v22.9_ diff --git a/guide/content/ko/guide/basics/routing.md b/guide/content/ko/guide/basics/routing.md new file mode 100644 index 0000000000..109d9eb2df --- /dev/null +++ b/guide/content/ko/guide/basics/routing.md @@ -0,0 +1,927 @@ +# Routing + +.. column:: + +``` +So far we have seen a lot of this decorator in different forms. + +But what is it? And how do we use it? +``` + +.. column:: + +```` +```python +@app.route("/stairway") + ... + +@app.get("/to") + ... + +@app.post("/heaven") + ... +``` +```` + +## Adding a route + +.. column:: + +``` +The most basic way to wire up a handler to an endpoint is with `app.add_route()`. + +See [API docs](https://sanic.readthedocs.io/en/stable/sanic/api_reference.html#sanic.app.Sanic.url_for) for more details. +``` + +.. column:: + +```` +```python +async def handler(request): + return text("OK") + +app.add_route(handler, "/test") +``` +```` + +.. column:: + +``` +By default, routes are available as an HTTP `GET` call. You can change a handler to respond to one or more HTTP methods. +``` + +.. column:: + +```` +```python +app.add_route( + handler, + '/test', + methods=["POST", "PUT"], +) +``` +```` + +.. column:: + +``` +Using the decorator syntax, the previous example is identical to this. +``` + +.. column:: + +```` +```python +@app.route('/test', methods=["POST", "PUT"]) +async def handler(request): + return text('OK') +``` +```` + +## HTTP methods + +Each of the standard HTTP methods has a convenience decorator. + +### GET + +```python +@app.get('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET) + +### POST + +```python +@app.post('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) + +### PUT + +```python +@app.put('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) + +### PATCH + +```python +@app.patch('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) + +### DELETE + +```python +@app.delete('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE) + +### HEAD + +```python +@app.head('/test') +async def handler(request): + return empty() +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) + +### OPTIONS + +```python +@app.options('/test') +async def handler(request): + return empty() +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS) + +.. warning:: + +```` +By default, Sanic will **only** consume the incoming request body on non-safe HTTP methods: `POST`, `PUT`, `PATCH`, `DELETE`. If you want to receive data in the HTTP request on any other method, you will need to do one of the following two options: + +**Option #1 - Tell Sanic to consume the body using `ignore_body`** +```python +@app.request("/path", ignore_body=False) +async def handler(_): + ... +``` + +**Option #2 - Manually consume the body in the handler using `receive_body`** +```python +@app.get("/path") +async def handler(request: Request): + await request.receive_body() +``` +```` + +## Path parameters + +.. column:: + +``` +Sanic allows for pattern matching, and for extracting values from URL paths. These parameters are then injected as keyword arguments in the route handler. +``` + +.. column:: + +```` +```python +@app.get("/tag/") +async def tag_handler(request, tag): + return text("Tag - {}".format(tag)) +``` +```` + +.. column:: + +``` +You can declare a type for the parameter. This will be enforced when matching, and also will type cast the variable. +``` + +.. column:: + +```` +```python +@app.get("/foo/") +async def uuid_handler(request, foo_id: UUID): + return text("UUID - {}".format(foo_id)) +``` +```` + +.. column:: + +``` +For some standard types like `str`, `int`, and `UUID`, Sanic can infer the path parameter type from the function signature. This means that it may not always be necessary to include the type in the path parameter definition. +``` + +.. column:: + +```` +```python +@app.get("/foo/") # Notice there is no :uuid in the path parameter +async def uuid_handler(request, foo_id: UUID): + return text("UUID - {}".format(foo_id)) +``` +```` + +### Supported types + +### `str` + +.. column:: + +``` +**Regular expression applied**: `r"[^/]+"` +**Cast type**: `str` +**Example matches**: + +- `/path/to/Bob` +- `/path/to/Python%203` + +Beginning in v22.3 `str` will *not* match on empty strings. See `strorempty` for this behavior. +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +### `strorempty` + +.. column:: + +``` +**Regular expression applied**: `r"[^/]*"` +**Cast type**: `str` +**Example matches**: + +- `/path/to/Bob` +- `/path/to/Python%203` +- `/path/to/` + +Unlike the `str` path parameter type, `strorempty` can also match on an empty string path segment. + +*Added in v22.3* +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +### `int` + +.. column:: + +``` +**Regular expression applied**: `r"-?\d+"` +**Cast type**: `int` +**Example matches**: + +- `/path/to/10` +- `/path/to/-10` + +_Does not match float, hex, octal, etc_ +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: int): + ... +``` +```` + +### `float` + +.. column:: + +``` +**Regular expression applied**: `r"-?(?:\d+(?:\.\d*)?|\.\d+)"` +**Cast type**: `float` +**Example matches**: + +- `/path/to/10` +- `/path/to/-10` +- `/path/to/1.5` +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: float): + ... +``` +```` + +### `alpha` + +.. column:: + +``` +**Regular expression applied**: `r"[A-Za-z]+"` +**Cast type**: `str` +**Example matches**: + +- `/path/to/Bob` +- `/path/to/Python` + +_Does not match a digit, or a space or other special character_ +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +### `slug` + +.. column:: + +``` +**Regular expression applied**: `r"[a-z0-9]+(?:-[a-z0-9]+)*"` +**Cast type**: `str` +**Example matches**: + +- `/path/to/some-news-story` +- `/path/to/or-has-digits-123` + +*Added in v21.6* +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, article: str): + ... +``` +```` + +### `path` + +.. column:: + +``` +**Regular expression applied**: `r"[^/].*?"` +**Cast type**: `str` +**Example matches**: +- `/path/to/hello` +- `/path/to/hello.txt` +- `/path/to/hello/world.txt` +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +.. warning:: + +``` +Because this will match on `/`, you should be careful and thoroughly test your patterns that use `path` so they do not capture traffic intended for another endpoint. Additionally, depending on how you use this type, you may be creating a path traversal vulnerability in your application. It is your job to protect your endpoint against this, but feel free to ask in our community channels for help if you need it :) +``` + +### `ymd` + +.. column:: + +``` +**Regular expression applied**: `r"^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))"` +**Cast type**: `datetime.date` +**Example matches**: + +- `/path/to/2021-03-28` +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: datetime.date): + ... +``` +```` + +### `uuid` + +.. column:: + +``` +**Regular expression applied**: `r"[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}"` +**Cast type**: `UUID` +**Example matches**: + +- `/path/to/123a123a-a12a-1a1a-a1a1-1a12a1a12345` +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: UUID): + ... +``` +```` + +### ext + +.. column:: + +``` +**Regular expression applied**: n/a +**Cast type**: *varies* +**Example matches**: +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str, ext: str): + ... +``` +```` + +| definition | example | filename | extension | +| ------------------------------------------------------------------------------------ | ----------------------------------------------------------- | -------- | ---------- | +| \ | page.txt | `"page"` | `"txt"` | +| \ | cat.jpg | `"cat"` | `"jpg"` | +| \ | cat.jpg | `"cat"` | `"jpg"` | +| | 123.txt | `123` | `"txt"` | +| | 123.svg | `123` | `"svg"` | +| | 3.14.tar.gz | `3.14` | `"tar.gz"` | + +File extensions can be matched using the special `ext` parameter type. It uses a special format that allows you to specify other types of parameter types as the file name, and one or more specific extensions as shown in the example table above. + +It does _not_ support the `path` parameter type. + +_Added in v22.3_ + +### regex + +.. column:: + +``` +**Regular expression applied**: _whatever you insert_ +**Cast type**: `str` +**Example matches**: + +- `/path/to/2021-01-01` + +This gives you the freedom to define specific matching patterns for your use case. + +In the example shown, we are looking for a date that is in `YYYY-MM-DD` format. +``` + +.. column:: + +```` +```python +@app.route(r"/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +### Regex Matching + +More often than not, compared with complex routing, the above example is too simple, and we use a completely different routing matching pattern, so here we will explain the advanced usage of regex matching in detail. + +Sometimes, you want to match a part of a route: + +```text +/image/123456789.jpg +``` + +If you wanted to match the file pattern, but only capture the numeric portion, you need to do some regex fun 😄: + +```python +app.route(r"/image/\d+)\.jpg>") +``` + +Further, these should all be acceptable: + +```python +@app.get(r"/") # matching on the full pattern +@app.get(r"/") # defining a single matching group +@app.get(r"/[a-z]{3}).txt>") # defining a single named matching group +@app.get(r"/[a-z]{3}).(?:txt)>") # defining a single named matching group, with one or more non-matching groups +``` + +Also, if using a named matching group, it must be the same as the segment label. + +```python +@app.get(r"/\d+).jpg>") # OK +@app.get(r"/\d+).jpg>") # NOT OK +``` + +For more regular usage methods, please refer to [Regular expression operations](https://docs.python.org/3/library/re.html) + +## Generating a URL + +.. column:: + +``` +Sanic provides a method to generate URLs based on the handler method name: `app.url_for()`. This is useful if you want to avoid hardcoding url paths into your app; instead, you can just reference the handler name. +``` + +.. column:: + +```` +```python +@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/') +async def post_handler(request, post_id): + ... +``` +```` + +.. column:: + +``` +You can pass any arbitrary number of keyword arguments. Anything that is _not_ a request parameter will be implemented as a part of the query string. +``` + +.. column:: + +```` +```python +assert app.url_for( + "post_handler", + post_id=5, + arg_one="one", + arg_two="two", +) == "/posts/5?arg_one=one&arg_two=two" +``` +```` + +.. column:: + +``` +Also supported is passing multiple values for a single query key. +``` + +.. column:: + +```` +```python +assert app.url_for( + "post_handler", + post_id=5, + arg_one=["one", "two"], +) == "/posts/5?arg_one=one&arg_one=two" +``` +```` + +### Special keyword arguments + +See API Docs for more details. + +```python +app.url_for("post_handler", post_id=5, arg_one="one", _anchor="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) +# '//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) +# '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") +# 'http://another_server:8888/posts/5?arg_one=one&arg_one=two&arg_two=2#anchor' +``` + +### Customizing a route name + +.. column:: + +``` +A custom route name can be used by passing a `name` argument while registering the route. +``` + +.. column:: + +```` +```python +@app.get("/get", name="get_handler") +def handler(request): + return text("OK") +``` +```` + +.. column:: + +``` +Now, use this custom name to retrieve the URL +``` + +.. column:: + +```` +```python +assert app.url_for("get_handler", foo="bar") == "/get?foo=bar" +``` +```` + +## Websockets routes + +.. column:: + +``` +Websocket routing works similar to HTTP methods. +``` + +.. column:: + +```` +```python +async def handler(request, ws): + message = "Start" + while True: + await ws.send(message) + message = await ws.recv() + +app.add_websocket_route(handler, "/test") +``` +```` + +.. column:: + +``` +It also has a convenience decorator. +``` + +.. column:: + +```` +```python +@app.websocket("/test") +async def handler(request, ws): + message = "Start" + while True: + await ws.send(message) + message = await ws.recv() +``` +```` + +Read the [websockets section](/guide/advanced/websockets.md) to learn more about how they work. + +## Strict slashes + +.. column:: + +``` +Sanic routes can be configured to strictly match on whether or not there is a trailing slash: `/`. This can be configured at a few levels and follows this order of precedence: + +1. Route +2. Blueprint +3. BlueprintGroup +4. Application +``` + +.. column:: + +```` +```python +# provide default strict_slashes value for all routes +app = Sanic(__file__, strict_slashes=True) +``` + +```python +# overwrite strict_slashes value for specific route +@app.get("/get", strict_slashes=False) +def handler(request): + return text("OK") +``` + +```python +# it also works for blueprints +bp = Blueprint(__file__, strict_slashes=True) + +@bp.get("/bp/get", strict_slashes=False) +def handler(request): + return text("OK") +``` + +```python +bp1 = Blueprint(name="bp1", url_prefix="/bp1") +bp2 = Blueprint( + name="bp2", + url_prefix="/bp2", + strict_slashes=False, +) + +# This will enforce strict slashes check on the routes +# under bp1 but ignore bp2 as that has an explicitly +# set the strict slashes check to false +group = Blueprint.group([bp1, bp2], strict_slashes=True) +``` +```` + +## Static files + +.. column:: + +``` +In order to serve static files from Sanic, use `app.static()`. + +The order of arguments is important: + +1. Route the files will be served from +2. Path to the files on the server + +See [API docs](https://sanic.readthedocs.io/en/stable/sanic/api/app.html#sanic.app.Sanic.static) for more details. +``` + +.. column:: + +```` +```python +app.static("/static/", "/path/to/directory/") +``` +```` + +.. tip:: + +``` +It is generally best practice to end your directory paths with a trailing slash (`/this/is/a/directory/`). This removes ambiguity by being more explicit. +``` + +.. column:: + +``` +You can also serve individual files. +``` + +.. column:: + +```` +```python +app.static("/", "/path/to/index.html") +``` +```` + +.. column:: + +``` +It is also sometimes helpful to name your endpoint +``` + +.. column:: + +```` +```python +app.static( + "/user/uploads/", + "/path/to/uploads/", + name="uploads", +) +``` +```` + +.. column:: + +``` +Retrieving the URLs works similar to handlers. But, we can also add the `filename` argument when we need a specific file inside a directory. +``` + +.. column:: + +```` +```python +assert app.url_for( + "static", + name="static", + filename="file.txt", +) == "/static/file.txt" +``` +```python +assert app.url_for( + "static", + name="uploads", + filename="image.png", +) == "/user/uploads/image.png" + +``` +```` + +.. tip:: + +```` +If you are going to have multiple `static()` routes, then it is *highly* suggested that you manually name them. This will almost certainly alleviate potential hard to discover bugs. + +```python +app.static("/user/uploads/", "/path/to/uploads/", name="uploads") +app.static("/user/profile/", "/path/to/profile/", name="profile_pics") +``` +```` + +#### Auto index serving + +.. column:: + +``` +If you have a directory of static files that should be served by an index page, you can provide the filename of the index. Now, when reaching that directory URL, the index page will be served. +``` + +.. column:: + +```` +```python +app.static("/foo/", "/path/to/foo/", index="index.html") +``` +```` + +_Added in v23.3_ + +#### File browser + +.. column:: + +``` +When serving a directory from a static handler, Sanic can be configured to show a basic file browser instead using `directory_view=True`. +``` + +.. column:: + +```` +```python +app.static("/uploads/", "/path/to/dir", directory_view=True) +``` +```` + +You now have a browsable directory in your web browser: + +![image](/assets/images/directory-view.png) + +_Added in v23.3_ + +## Route context + +.. column:: + +``` +When a route is defined, you can add any number of keyword arguments with a `ctx_` prefix. These values will be injected into the route `ctx` object. +``` + +.. column:: + +```` +```python +@app.get("/1", ctx_label="something") +async def handler1(request): + ... + +@app.get("/2", ctx_label="something") +async def handler2(request): + ... + +@app.get("/99") +async def handler99(request): + ... + +@app.on_request +async def do_something(request): + if request.route.ctx.label == "something": + ... +``` +```` + +_Added in v21.12_ diff --git a/guide/content/ko/guide/basics/tasks.md b/guide/content/ko/guide/basics/tasks.md new file mode 100644 index 0000000000..dd37abf98e --- /dev/null +++ b/guide/content/ko/guide/basics/tasks.md @@ -0,0 +1,166 @@ +--- +title: Background tasks +--- + +# Background tasks + +## Creating Tasks + +It is often desirable and very convenient to make usage of [tasks](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task) in async Python. Sanic provides a convenient method to add tasks to the currently **running** loop. It is somewhat similar to `asyncio.create_task`. For adding tasks before the 'App' loop is running, see next section. + +```python +async def notify_server_started_after_five_seconds(): + await asyncio.sleep(5) + print('Server successfully started!') + +app.add_task(notify_server_started_after_five_seconds()) +``` + +.. column:: + +``` +Sanic will attempt to automatically inject the app, passing it as an argument to the task. +``` + +.. column:: + +```` +```python +async def auto_inject(app): + await asyncio.sleep(5) + print(app.name) + +app.add_task(auto_inject) +``` +```` + +.. column:: + +``` +Or you can pass the `app` argument explicitly. +``` + +.. column:: + +```` +```python +async def explicit_inject(app): + await asyncio.sleep(5) + print(app.name) + +app.add_task(explicit_inject(app)) +``` +```` + +## Adding tasks before `app.run` + +It is possible to add background tasks before the App is run ie. before `app.run`. To add a task before the App is run, it is recommended to not pass the coroutine object (ie. one created by calling the `async` callable), but instead just pass the callable and Sanic will create the coroutine object on **each worker**. Note: the tasks that are added such are run as `before_server_start` jobs and thus run on every worker (and not in the main process). This has certain consequences, please read [this comment](https://github.com/sanic-org/sanic/issues/2139#issuecomment-868993668) on [this issue](https://github.com/sanic-org/sanic/issues/2139) for further details. + +To add work on the main process, consider adding work to [`@app.main_process_start`](./listeners.md). Note: the workers won't start until this work is completed. + +.. column:: + +``` +Example to add a task before `app.run` +``` + +.. column:: + +```` +```python +async def slow_work(): + ... + +async def even_slower(num): + ... + +app = Sanic(...) +app.add_task(slow_work) # Note: we are passing the callable and not coroutine object ... +app.add_task(even_slower(10)) # ... or we can call the function and pass the coroutine. +app.run(...) +``` +```` + +## Named tasks + +.. column:: + +``` +When creating a task, you can ask Sanic to keep track of it for you by providing a `name`. +``` + +.. column:: + +```` +```python +app.add_task(slow_work, name="slow_task") +``` +```` + +.. column:: + +``` +You can now retrieve that task instance from anywhere in your application using `get_task`. +``` + +.. column:: + +```` +```python +task = app.get_task("slow_task") +``` +```` + +.. column:: + +``` +If that task needs to be cancelled, you can do that with `cancel_task`. Make sure that you `await` it. +``` + +.. column:: + +```` +```python +await app.cancel_task("slow_task") +``` +```` + +.. column:: + +``` +All registered tasks can be found in the `app.tasks` property. To prevent cancelled tasks from filling up, you may want to run `app.purge_tasks` that will clear out any completed or cancelled tasks. +``` + +.. column:: + +```` +```python +app.purge_tasks() +``` +```` + +This pattern can be particularly useful with `websockets`: + +```python +async def receiver(ws): + while True: + message = await ws.recv() + if not message: + break + print(f"Received: {message}") + +@app.websocket("/feed") +async def feed(request, ws): + task_name = f"receiver:{request.id}" + request.app.add_task(receiver(ws), name=task_name) + try: + while True: + await request.app.event("my.custom.event") + await ws.send("A message") + finally: + # When the websocket closes, let's cleanup the task + await request.app.cancel_task(task_name) + request.app.purge_tasks() +``` + +_Added in v21.12_ diff --git a/guide/content/ko/guide/best-practices/blueprints.md b/guide/content/ko/guide/best-practices/blueprints.md new file mode 100644 index 0000000000..7a3571058c --- /dev/null +++ b/guide/content/ko/guide/best-practices/blueprints.md @@ -0,0 +1,521 @@ +# Blueprints + +## Overview + +Blueprints are objects that can be used for sub-routing within an application. Instead of adding routes to the application instance, blueprints define similar methods for adding routes, which are then registered with the application in a flexible and pluggable manner. + +Blueprints are especially useful for larger applications, where your application logic can be broken down into several groups or areas of responsibility. + +## Creating and registering + +.. column:: + +``` +First, you must create a blueprint. It has a very similar API as the `Sanic()` app instance with many of the same decorators. +``` + +.. column:: + +```` +```python +# ./my_blueprint.py +from sanic.response import json +from sanic import Blueprint + +bp = Blueprint("my_blueprint") + +@bp.route("/") +async def bp_root(request): + return json({"my": "blueprint"}) +``` +```` + +.. column:: + +``` +Next, you register it with the app instance. +``` + +.. column:: + +```` +```python +from sanic import Sanic +from my_blueprint import bp + +app = Sanic(__name__) +app.blueprint(bp) +``` +```` + +Blueprints also have the same `websocket()` decorator and `add_websocket_route` method for implementing websockets. + +.. column:: + +``` +Beginning in v21.12, a Blueprint may be registered before or after adding objects to it. Previously, only objects attached to the Blueprint at the time of registration would be loaded into application instance. +``` + +.. column:: + +```` +```python +app.blueprint(bp) + +@bp.route("/") +async def bp_root(request): + ... +``` +```` + +## Copying + +.. column:: + +``` +Blueprints along with everything that is attached to them can be copied to new instances using the `copy()` method. The only required argument is to pass it a new `name`. However, you could also use this to override any of the values from the old blueprint. +``` + +.. column:: + +```` +```python +v1 = Blueprint("Version1", version=1) + +@v1.route("/something") +def something(request): + pass + +v2 = v1.copy("Version2", version=2) + +app.blueprint(v1) +app.blueprint(v2) +``` + +``` +Available routes: +/v1/something +/v2/something + +``` +```` + +_Added in v21.9_ + +## Blueprint groups + +Blueprints may also be registered as part of a list or tuple, where the registrar will recursively cycle through any sub-sequences of blueprints and register them accordingly. The Blueprint.group method is provided to simplify this process, allowing a ‘mock’ backend directory structure mimicking what’s seen from the front end. Consider this (quite contrived) example: + +```text +api/ +├──content/ +│ ├──authors.py +│ ├──static.py +│ └──__init__.py +├──info.py +└──__init__.py +app.py +``` + +.. column:: + +``` +#### First blueprint +``` + +.. column:: + +```` +```python +# api/content/authors.py +from sanic import Blueprint + +authors = Blueprint("content_authors", url_prefix="/authors") +``` +```` + +.. column:: + +``` +#### Second blueprint +``` + +.. column:: + +```` +```python +# api/content/static.py +from sanic import Blueprint + +static = Blueprint("content_static", url_prefix="/static") +``` +```` + +.. column:: + +``` +#### Blueprint group +``` + +.. column:: + +```` +```python +# api/content/__init__.py +from sanic import Blueprint +from .static import static +from .authors import authors + +content = Blueprint.group(static, authors, url_prefix="/content") +``` +```` + +.. column:: + +``` +#### Third blueprint +``` + +.. column:: + +```` +```python +# api/info.py +from sanic import Blueprint + +info = Blueprint("info", url_prefix="/info") +``` +```` + +.. column:: + +``` +#### Another blueprint group +``` + +.. column:: + +```` +```python +# api/__init__.py +from sanic import Blueprint +from .content import content +from .info import info + +api = Blueprint.group(content, info, url_prefix="/api") +``` +```` + +.. column:: + +``` +#### Main server + +All blueprints are now registered +``` + +.. column:: + +```` +```python +# app.py +from sanic import Sanic +from .api import api + +app = Sanic(__name__) +app.blueprint(api) +``` +```` + +### Blueprint group prefixes and composability + +As shown in the code above, when you create a group of blueprints you can extend the URL prefix of all the blueprints in the group by passing the `url_prefix` argument to the `Blueprint.group` method. This is useful for creating a mock directory structure for your API. + +In addition, there is a `name_prefix` argument that can be used to make blueprints reusable and composable. The is specifically necessary when applying a single blueprint to multiple groups. By doing this, the blueprint will be registered with a unique name for each group, which allows the blueprint to be registered multiple times and have its routes each properly named with a unique identifier. + +.. column:: + +``` +Consider this example. The routes built will be named as follows: +- `TestApp.group-a_bp1.route1` +- `TestApp.group-a_bp2.route2` +- `TestApp.group-b_bp1.route1` +- `TestApp.group-b_bp2.route2` +``` + +.. column:: + +```` +```python +bp1 = Blueprint("bp1", url_prefix="/bp1") +bp2 = Blueprint("bp2", url_prefix="/bp2") + +bp1.add_route(lambda _: ..., "/", name="route1") +bp2.add_route(lambda _: ..., "/", name="route2") + +group_a = Blueprint.group( + bp1, bp2, url_prefix="/group-a", name_prefix="group-a" +) +group_b = Blueprint.group( + bp1, bp2, url_prefix="/group-b", name_prefix="group-b" +) + +app = Sanic("TestApp") +app.blueprint(group_a) +app.blueprint(group_b) +``` +```` + +_Name prefixing added in v23.6_ + +## Middleware + +.. column:: + +``` +Blueprints can also have middleware that is specifically registered for its endpoints only. +``` + +.. column:: + +```` +```python +@bp.middleware +async def print_on_request(request): + print("I am a spy") + +@bp.middleware("request") +async def halt_request(request): + return text("I halted the request") + +@bp.middleware("response") +async def halt_response(request, response): + return text("I halted the response") +``` +```` + +.. column:: + +``` +Similarly, using blueprint groups, it is possible to apply middleware to an entire group of nested blueprints. +``` + +.. column:: + +```` +```python +bp1 = Blueprint("bp1", url_prefix="/bp1") +bp2 = Blueprint("bp2", url_prefix="/bp2") + +@bp1.middleware("request") +async def bp1_only_middleware(request): + print("applied on Blueprint : bp1 Only") + +@bp1.route("/") +async def bp1_route(request): + return text("bp1") + +@bp2.route("/") +async def bp2_route(request, param): + return text(param) + +group = Blueprint.group(bp1, bp2) + +@group.middleware("request") +async def group_middleware(request): + print("common middleware applied for both bp1 and bp2") + +# Register Blueprint group under the app +app.blueprint(group) +``` +```` + +## Exceptions + +.. column:: + +``` +Just like other [exception handling](./exceptions.md), you can define blueprint specific handlers. +``` + +.. column:: + +```` +```python +@bp.exception(NotFound) +def ignore_404s(request, exception): + return text("Yep, I totally found the page: {}".format(request.url)) +``` +```` + +## Static files + +.. column:: + +``` +Blueprints can also have their own static handlers +``` + +.. column:: + +```` +```python +bp = Blueprint("bp", url_prefix="/bp") +bp.static("/web/path", "/folder/to/serve") +bp.static("/web/path", "/folder/to/server", name="uploads") +``` +```` + +.. column:: + +``` +Which can then be retrieved using `url_for()`. See [routing](/guide/basics/routing.md) for more information. +``` + +.. column:: + +```` +```python +>>> print(app.url_for("static", name="bp.uploads", filename="file.txt")) +'/bp/web/path/file.txt' +``` +```` + +## Listeners + +.. column:: + +``` +Blueprints can also implement [listeners](/guide/basics/listeners.md). +``` + +.. column:: + +```` +```python +@bp.listener("before_server_start") +async def before_server_start(app, loop): + ... + +@bp.listener("after_server_stop") +async def after_server_stop(app, loop): + ... +``` +```` + +## Versioning + +As discussed in the [versioning section](/guide/advanced/versioning.md), blueprints can be used to implement different versions of a web API. + +.. column:: + +``` +The `version` will be prepended to the routes as `/v1` or `/v2`, etc. +``` + +.. column:: + +```` +```python +auth1 = Blueprint("auth", url_prefix="/auth", version=1) +auth2 = Blueprint("auth", url_prefix="/auth", version=2) +``` +```` + +.. column:: + +``` +When we register our blueprints on the app, the routes `/v1/auth` and `/v2/auth` will now point to the individual blueprints, which allows the creation of sub-sites for each API version. +``` + +.. column:: + +```` +```python +from auth_blueprints import auth1, auth2 + +app = Sanic(__name__) +app.blueprint(auth1) +app.blueprint(auth2) +``` +```` + +.. column:: + +``` +It is also possible to group the blueprints under a `BlueprintGroup` entity and version multiple of them together at the +same time. +``` + +.. column:: + +```` +```python +auth = Blueprint("auth", url_prefix="/auth") +metrics = Blueprint("metrics", url_prefix="/metrics") + +group = Blueprint.group(auth, metrics, version="v1") + +# This will provide APIs prefixed with the following URL path +# /v1/auth/ and /v1/metrics +``` +```` + +## Composable + +A `Blueprint` may be registered to multiple groups, and each of `BlueprintGroup` itself could be registered and nested further. This creates a limitless possibility `Blueprint` composition. + +_Added in v21.6_ + +.. column:: + +``` +Take a look at this example and see how the two handlers are actually mounted as five (5) distinct routes. +``` + +.. column:: + +```` +```python +app = Sanic(__name__) +blueprint_1 = Blueprint("blueprint_1", url_prefix="/bp1") +blueprint_2 = Blueprint("blueprint_2", url_prefix="/bp2") +group = Blueprint.group( + blueprint_1, + blueprint_2, + version=1, + version_prefix="/api/v", + url_prefix="/grouped", + strict_slashes=True, +) +primary = Blueprint.group(group, url_prefix="/primary") + +@blueprint_1.route("/") +def blueprint_1_default_route(request): + return text("BP1_OK") + +@blueprint_2.route("/") +def blueprint_2_default_route(request): + return text("BP2_OK") + +app.blueprint(group) +app.blueprint(primary) +app.blueprint(blueprint_1) + +# The mounted paths: +# /api/v1/grouped/bp1/ +# /api/v1/grouped/bp2/ +# /api/v1/primary/grouped/bp1 +# /api/v1/primary/grouped/bp2 +# /bp1 + +``` +```` + +## Generating a URL + +When generating a url with `url_for()`, the endpoint name will be in the form: + +```text +{blueprint_name}.{handler_name} +``` diff --git a/guide/content/ko/guide/best-practices/decorators.md b/guide/content/ko/guide/best-practices/decorators.md new file mode 100644 index 0000000000..3f1bfdf6c0 --- /dev/null +++ b/guide/content/ko/guide/best-practices/decorators.md @@ -0,0 +1,195 @@ +# Decorators + +One of the best ways to create a consistent and DRY web API is to make use of decorators to remove functionality from the handlers, and make it repeatable across your views. + +.. column:: + +``` +Therefore, it is very common to see a Sanic view handler with several decorators on it. +``` + +.. column:: + +```` +```python +@app.get("/orders") +@authorized("view_order") +@validate_list_params() +@inject_user() +async def get_order_details(request, params, user): + ... +``` +```` + +## Example + +Here is a starter template to help you create decorators. + +In this example, let’s say you want to check that a user is authorized to access a particular endpoint. You can create a decorator that wraps a handler function, checks a request if the client is authorized to access a resource, and sends the appropriate response. + +```python +from functools import wraps +from sanic.response import json + +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 = await 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"}) +``` + +## Templates + +Decorators are **fundamental** to building applications with Sanic. They increase the portability and maintainablity of your code. + +In paraphrasing the Zen of Python: "[decorators] are one honking great idea -- let's do more of those!" + +To make it easier to implement them, here are three examples of copy/pastable code to get you started. + +.. column:: + +``` +Don't forget to add these import statements. Although it is *not* necessary, using `@wraps` helps keep some of the metadata of your function intact. [See docs](https://docs.python.org/3/library/functools.html#functools.wraps). Also, we use the `isawaitable` pattern here to allow the route handlers to by regular or asynchronous functions. +``` + +.. column:: + +```` +```python +from inspect import isawaitable +from functools import wraps +``` +```` + +### With args + +.. column:: + +```` +Often, you will want a decorator that will *always* need arguments. Therefore, when it is implemented you will always be calling it. + +```python +@app.get("/") +@foobar(1, 2) +async def handler(request: Request): + return text("hi") +``` +```` + +.. column:: + +```` +```python +def foobar(arg1, arg2): + def decorator(f): + @wraps(f) + async def decorated_function(request, *args, **kwargs): + + response = f(request, *args, **kwargs) + if isawaitable(response): + response = await response + + return response + + return decorated_function + + return decorator +``` +```` + +### Without args + +.. column:: + +```` +Sometimes you want a decorator that will not take arguments. When this is the case, it is a nice convenience not to have to call it + +```python +@app.get("/") +@foobar +async def handler(request: Request): + return text("hi") +``` +```` + +.. column:: + +```` +```python +def foobar(func): + def decorator(f): + @wraps(f) + async def decorated_function(request, *args, **kwargs): + + response = f(request, *args, **kwargs) + if isawaitable(response): + response = await response + + return response + + return decorated_function + + return decorator(func) +``` +```` + +### With or Without args + +.. column:: + +```` +If you want a decorator with the ability to be called or not, you can follow this pattern. Using keyword only arguments is not necessary, but might make implementation simpler. + +```python +@app.get("/") +@foobar(arg1=1, arg2=2) +async def handler(request: Request): + return text("hi") +``` + +```python +@app.get("/") +@foobar +async def handler(request: Request): + return text("hi") +``` +```` + +.. column:: + +```` +```python +def foobar(maybe_func=None, *, arg1=None, arg2=None): + def decorator(f): + @wraps(f) + async def decorated_function(request, *args, **kwargs): + + response = f(request, *args, **kwargs) + if isawaitable(response): + response = await response + + return response + + return decorated_function + + return decorator(maybe_func) if maybe_func else decorator +``` +```` diff --git a/guide/content/ko/guide/best-practices/exceptions.md b/guide/content/ko/guide/best-practices/exceptions.md new file mode 100644 index 0000000000..9b46c551a1 --- /dev/null +++ b/guide/content/ko/guide/best-practices/exceptions.md @@ -0,0 +1,636 @@ +# Exceptions + +## Using Sanic exceptions + +Sometimes you just need to tell Sanic to halt execution of a handler and send back a status code response. You can raise a `SanicException` for this and Sanic will do the rest for you. + +You can pass an optional `status_code` argument. By default, a SanicException will return an internal server error 500 response. + +```python +from sanic.exceptions import SanicException + +@app.route("/youshallnotpass") +async def no_no(request): + raise SanicException("Something went wrong.", status_code=501) +``` + +Sanic provides a number of standard exceptions. They each automatically will raise the appropriate HTTP status code in your response. [Check the API reference](https://sanic.readthedocs.io/en/latest/sanic/api_reference.html#module-sanic.exceptions) for more details. + +.. column:: + +``` +The more common exceptions you _should_ implement yourself include: + +- `BadRequest` (400) +- `Unauthorized` (401) +- `Forbidden` (403) +- `NotFound` (404) +- `ServerError` (500) +``` + +.. column:: + +```` +```python +from sanic import exceptions + +@app.route("/login") +async def login(request): + user = await some_login_func(request) + if not user: + raise exceptions.NotFound( + f"Could not find user with username={request.json.username}" + ) +``` +```` + +## Exception properties + +All exceptions in Sanic derive from `SanicException`. That class has a few properties on it that assist the developer in consistently reporting their exceptions across an application. + +- `message` +- `status_code` +- `quiet` +- `headers` +- `context` +- `extra` + +All of these properties can be passed to the exception when it is created, but the first three can also be used as class variables as we will see. + +.. column:: + +``` +### `message` + +The `message` property obviously controls the message that will be displayed as with any other exception in Python. What is particularly useful is that you can set the `message` property on the class definition allowing for easy standardization of language across an application +``` + +.. column:: + +```` +```python +class CustomError(SanicException): + message = "Something bad happened" + +raise CustomError +# or +raise CustomError("Override the default message with something else") +``` +```` + +.. column:: + +``` +### `status_code` + +This property is used to set the response code when the exception is raised. This can particularly be useful when creating custom 400 series exceptions that are usually in response to bad information coming from the client. +``` + +.. column:: + +```` +```python +class TeapotError(SanicException): + status_code = 418 + message = "Sorry, I cannot brew coffee" + +raise TeapotError +# or +raise TeapotError(status_code=400) +``` +```` + +.. column:: + +``` +### `quiet` + +By default, exceptions will be output by Sanic to the `error_logger`. Sometimes this may not be desirable, especially if you are using exceptions to trigger events in exception handlers (see [the following section](./exceptions.md#handling)). You can suppress the log output using `quiet=True`. +``` + +.. column:: + +```` +```python +class SilentError(SanicException): + message = "Something happened, but not shown in logs" + quiet = True + +raise SilentError +# or +raise InvalidUsage("blah blah", quiet=True) +``` +```` + +.. column:: + +``` +Sometimes while debugging you may want to globally ignore the `quiet=True` property. You can force Sanic to log out all exceptions regardless of this property using `NOISY_EXCEPTIONS` + +*Added in v21.12* +``` + +.. column:: + +```` +```python +app.config.NOISY_EXCEPTIONS = True +``` +```` + +.. column:: + +``` +### `headers` + +Using `SanicException` as a tool for creating responses is super powerful. This is in part because not only can you control the `status_code`, but you can also control reponse headers directly from the exception. +``` + +.. column:: + +```` +```python +class MyException(SanicException): + headers = { + "X-Foo": "bar" + } + +raise MyException +# or +raise InvalidUsage("blah blah", headers={ + "X-Foo": "bar" +}) +``` +```` + +.. column:: + +``` +### `extra` + +See [contextual exceptions](./exceptions.md#contextual-exceptions) + +*Added in v21.12* +``` + +.. column:: + +```` +```python +raise SanicException(..., extra={"name": "Adam"}) +``` +```` + +.. column:: + +``` +### `context` + +See [contextual exceptions](./exceptions.md#contextual-exceptions) + +*Added in v21.12* +``` + +.. column:: + +```` +```python +raise SanicException(..., context={"foo": "bar"}) +``` +```` + +## Handling + +Sanic handles exceptions automatically by rendering an error page, so in many cases you don't need to handle them yourself. However, if you would like more control on what to do when an exception is raised, you can implement a handler yourself. + +Sanic provides a decorator for this, which applies to not only the Sanic standard exceptions, but **any** exception that your application might throw. + +.. column:: + +``` +The easiest method to add a handler is to use `@app.exception()` and pass it one or more exceptions. +``` + +.. column:: + +```` +```python +from sanic.exceptions import NotFound + +@app.exception(NotFound, SomeCustomException) +async def ignore_404s(request, exception): + return text("Yep, I totally found the page: {}".format(request.url)) +``` +```` + +.. column:: + +``` +You can also create a catchall handler by catching `Exception`. +``` + +.. column:: + +```` +```python +@app.exception(Exception) +async def catch_anything(request, exception): + ... +``` +```` + +.. column:: + +``` +You can also use `app.error_handler.add()` to add error handlers. +``` + +.. column:: + +```` +```python +async def server_error_handler(request, exception): + return text("Oops, server error", status=500) + +app.error_handler.add(Exception, server_error_handler) +``` +```` + +## Built-in error handling + +Sanic ships with three formats for exceptions: HTML, JSON, and text. You can see examples of them below in the [Fallback handler](#fallback-handler) section. + +.. column:: + +``` +You can control _per route_ which format to use with the `error_format` keyword argument. + +*Added in v21.9* +``` + +.. column:: + +```` +```python +@app.request("/", error_format="text") +async def handler(request): + ... +``` +```` + +## Custom error handling + +In some cases, you might want to add some more error handling functionality to what is provided by default. In that case, you can subclass Sanic's default error handler as such: + +```python +from sanic.handlers import ErrorHandler + +class CustomErrorHandler(ErrorHandler): + def default(self, request: Request, exception: Exception) -> HTTPResponse: + ''' handles errors that have no error handlers assigned ''' + # You custom error handling logic... + status_code = getattr(exception, "status_code", 500) + return json({ + "error": str(exception), + "foo": "bar" + }, status=status_code) + +app.error_handler = CustomErrorHandler() +``` + +## Fallback handler + +Sanic comes with three fallback exception handlers: + +1. HTML +2. Text +3. JSON + +These handlers present differing levels of detail depending upon whether your application is in [debug mode](/guide/deployment/development.md) or not. + +By default, Sanic will be in "auto" mode, which means that it will using the incoming request and potential matching handler to choose the appropriate response format. For example, when in a browser it should always provide an HTML error page. When using curl, you might see JSON or plain text. + +### HTML + +```python +app.config.FALLBACK_ERROR_FORMAT = "html" +``` + +.. column:: + +```` +```python +app.config.DEBUG = True +``` + +![Error](/assets/images/error-display-html-debug.png) +```` + +.. column:: + +```` +```python +app.config.DEBUG = False +``` + +![Error](/assets/images/error-display-html-prod.png) +```` + +### Text + +```python +app.config.FALLBACK_ERROR_FORMAT = "text" +``` + +.. column:: + +```` +```python +app.config.DEBUG = True +``` + +```sh +curl localhost:8000/exc -i +HTTP/1.1 500 Internal Server Error +content-length: 620 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +⚠️ 500 — Internal Server Error +============================== +That time when that thing broke that other thing? That happened. + +ServerError: That time when that thing broke that other thing? That happened. while handling path /exc +Traceback of TestApp (most recent call last): + + ServerError: That time when that thing broke that other thing? That happened. + File /path/to/sanic/app.py, line 979, in handle_request + response = await response + + File /path/to/server.py, line 16, in handler + do_something(cause_error=True) + + File /path/to/something.py, line 9, in do_something + raise ServerError( +``` +```` + +.. column:: + +```` +```python +app.config.DEBUG = False +``` + +```sh +curl localhost:8000/exc -i +HTTP/1.1 500 Internal Server Error +content-length: 134 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +⚠️ 500 — Internal Server Error +============================== +That time when that thing broke that other thing? That happened. +``` +```` + +### JSON + +```python +app.config.FALLBACK_ERROR_FORMAT = "json" +``` + +.. column:: + +```` +```python +app.config.DEBUG = True +``` + +```sh +curl localhost:8000/exc -i +HTTP/1.1 500 Internal Server Error +content-length: 572 +connection: keep-alive +content-type: application/jso + +{ + "description": "Internal Server Error", + "status": 500, + "message": "That time when that thing broke that other thing? That happened.", + "path": "/exc", + "args": {}, + "exceptions": [ + { + "type": "ServerError", + "exception": "That time when that thing broke that other thing? That happened.", + "frames": [ + { + "file": "/path/to/sanic/app.py", + "line": 979, + "name": "handle_request", + "src": "response = await response" + }, + { + "file": "/path/to/server.py", + "line": 16, + "name": "handler", + "src": "do_something(cause_error=True)" + }, + { + "file": "/path/to/something.py", + "line": 9, + "name": "do_something", + "src": "raise ServerError(" + } + ] + } + ] +} +``` +```` + +.. column:: + +```` +```python +app.config.DEBUG = False +``` + +```sh +curl localhost:8000/exc -i +HTTP/1.1 500 Internal Server Error +content-length: 129 +connection: keep-alive +content-type: application/json + +{ + "description": "Internal Server Error", + "status": 500, + "message": "That time when that thing broke that other thing? That happened." +} + +``` +```` + +### Auto + +Sanic also provides an option for guessing which fallback option to use. + +```python +app.config.FALLBACK_ERROR_FORMAT = "auto" +``` + +## Contextual Exceptions + +Default exception messages that simplify the ability to consistently raise exceptions throughout your application. + +```python +class TeapotError(SanicException): + status_code = 418 + message = "Sorry, I cannot brew coffee" + +raise TeapotError +``` + +But this lacks two things: + +1. A dynamic and predictable message format +2. The ability to add additional context to an error message (more on this in a moment) + +_Added in v21.12_ + +Using one of Sanic's exceptions, you have two options to provide additional details at runtime: + +```python +raise TeapotError(extra={"foo": "bar"}, context={"foo": "bar"}) +``` + +What's the difference and when should you decide to use each? + +- `extra` - The object itself will **never** be sent to a production client. It is meant for internal use only. What could it be used for? + - Generating (as we will see in a minute) a dynamic error message + - Providing runtime details to a logger + - Debug information (when in development mode, it is rendered) +- `context` - This object is **always** sent to production clients. It is generally meant to be used to send additional details about the context of what happened. What could it be used for? + - Providing alternative values on a `BadRequest` validation issue + - Responding with helpful details for your customers to open a support ticket + - Displaying state information like current logged in user info + +### Dynamic and predictable message using `extra` + +Sanic exceptions can be raised using `extra` keyword arguments to provide additional information to a raised exception instance. + +```python +class TeapotError(SanicException): + status_code = 418 + + @property + def message(self): + return f"Sorry {self.extra['name']}, I cannot make you coffee" + +raise TeapotError(extra={"name": "Adam"}) +``` + +The new feature allows the passing of `extra` meta to the exception instance, which can be particularly useful as in the above example to pass dynamic data into the message text. This `extra` info object **will be suppressed** when in `PRODUCTION` mode, but displayed in `DEVELOPMENT` mode. + +.. column:: + +``` +**DEVELOPMENT** + +![image](~@assets/images/error-extra-debug.png) +``` + +.. column:: + +``` +**PRODUCTION** + +![image](~@assets/images/error-extra-prod.png) +``` + +### Additional `context` to an error message + +Sanic exceptions can also be raised with a `context` argument to pass intended information along to the user about what happened. This is particularly useful when creating microservices or an API intended to pass error messages in JSON format. In this use case, we want to have some context around the exception beyond just a parseable error message to return details to the client. + +```python +raise TeapotError(context={"foo": "bar"}) +``` + +This is information **that we want** to always be passed in the error (when it is available). Here is what it should look like: + +.. column:: + +```` +**PRODUCTION** + +```json +{ + "description": "I'm a teapot", + "status": 418, + "message": "Sorry Adam, I cannot make you coffee", + "context": { + "foo": "bar" + } +} +``` +```` + +.. column:: + +```` +**DEVELOPMENT** + +```json +{ + "description": "I'm a teapot", + "status": 418, + "message": "Sorry Adam, I cannot make you coffee", + "context": { + "foo": "bar" + }, + "path": "/", + "args": {}, + "exceptions": [ + { + "type": "TeapotError", + "exception": "Sorry Adam, I cannot make you coffee", + "frames": [ + { + "file": "handle_request", + "line": 83, + "name": "handle_request", + "src": "" + }, + { + "file": "/tmp/p.py", + "line": 17, + "name": "handler", + "src": "raise TeapotError(" + } + ] + } + ] +} +``` +```` + +## Error reporting + +Sanic has a [signal](../advanced/signals.md#built-in-signals) that allows you to hook into the exception reporting process. This is useful if you want to send exception information to a third party service like Sentry or Rollbar. This can be conveniently accomplished by attaching an error reporting handler as show below: + +```python +@app.report_exception +async def catch_any_exception(app: Sanic, exception: Exception): +print("Caught exception:", exception) +``` + +.. note:: + +``` +This handler will be dispatched into a background task and **IS NOT** intended for use to manipulate any response data. It is intended to be used for logging or reporting purposes only, and should not impact the ability of your application to return the error response to the client. +``` + +_Added in v23.6_ diff --git a/guide/content/ko/guide/best-practices/logging.md b/guide/content/ko/guide/best-practices/logging.md new file mode 100644 index 0000000000..629127030d --- /dev/null +++ b/guide/content/ko/guide/best-practices/logging.md @@ -0,0 +1,218 @@ +# Logging + +Sanic allows you to do different types of logging (access log, error log) on the requests based on the [Python logging API](https://docs.python.org/3/howto/logging.html). You should have some basic knowledge on Python logging if you want to create a new configuration. + +But, don't worry, out of the box Sanic ships with some sensible logging defaults. Out of the box it uses an `AutoFormatter` that will format the logs depending upon whether you are in debug mode or not. We will show you how to force this later on. + +## Quick Start + +Let's start by looking at what logging might look like in local development. For this, we will use the default logging configuration that Sanic provides and make sure to run Sanic in development mode. + +.. column:: + +``` +A simple example using default settings would be like this: +``` + +.. column:: + +```` +```python +from sanic import Sanic +from sanic.log import logger +from sanic.response import text + +app = Sanic('logging_example') + +@app.route('/') +async def test(request): + logger.info('Here is your log') + return text('Hello World!') +``` +```` + +.. column:: + +``` +Because we are specifically trying to look at the development logs, we will make sure to run Sanic in development mode. +``` + +.. column:: + +```` +```sh +sanic path.to.server:app --dev +``` +```` + +After the server is running, you should see logs like this. + +![Sanic Logging Start](/assets/images/logging-debug-start.png) + +You can send a request to server and it will print the log messages. + +![Sanic Logging Access](/assets/images/logging-debug-access.png) + +Some important points to note: + +- The default log level in **production** mode is `INFO`. +- The default log level in **debug** mode is `DEBUG`. +- When in **debug** mode, the log messages will not have a timestamp (except on access logs). +- Sanic will try to colorize the logs if the terminal supports it. If you are running in Docker with docker-compose, you may need to set `tty: true` in your `docker-compose.yml` file to see the colors. + +## Sanic's loggers + +Out of the box, Sanic ships with five loggers: + +| **Logger Name** | **Use Case** | +| ------------------ | ---------------------------------------------- | +| `sanic.root` | Used to log internal messages. | +| `sanic.error` | Used to log error logs. | +| `sanic.access` | Used to log access logs. | +| `sanic.server` | Used to log server logs. | +| `sanic.websockets` | Used to log websocket logs. | + +.. column:: + +``` +If you want to use these loggers yourself, you can import them from `sanic.log`. +``` + +.. column:: + +```` +```python +from sanic.log import logger, error_logger, access_logger, server_logger, websockets_logger + +logger.info('This is a root logger message') +``` +```` + +.. warning:: + +``` +Feel free to use the root logger and the error logger yourself. But, you probably don't want to use the access logger, server logger, or websockets logger directly. These are used internally by Sanic and are configured to log in a specific way. If you want to change the way these loggers log, you should change the logging configuration. +``` + +## Default logging configuration + +Sanic ships with a default logging configuration that is used when you do not provide your own. This configuration is stored in `sanic.log.LOGGING_CONFIG_DEFAULTS`. + +```python +{ + 'version': 1, + 'disable_existing_loggers': False, + 'loggers': { + 'sanic.root': {'level': 'INFO', 'handlers': ['console']}, + 'sanic.error': { + 'level': 'INFO', + 'handlers': ['error_console'], + 'propagate': True, + 'qualname': 'sanic.error' + }, + 'sanic.access': { + 'level': 'INFO', + 'handlers': ['access_console'], + 'propagate': True, + 'qualname': 'sanic.access' + }, + 'sanic.server': { + 'level': 'INFO', + 'handlers': ['console'], + 'propagate': True, + 'qualname': 'sanic.server' + }, + 'sanic.websockets': { + 'level': 'INFO', + 'handlers': ['console'], + 'propagate': True, + 'qualname': 'sanic.websockets' + } + }, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'formatter': 'generic', + 'stream': sys.stdout + }, + 'error_console': { + 'class': 'logging.StreamHandler', + 'formatter': 'generic', + 'stream': sys.stderr + }, + 'access_console': { + 'class': 'logging.StreamHandler', + 'formatter': 'access', + 'stream': sys.stdout + } + }, + 'formatters': { + 'generic': {'class': 'sanic.logging.formatter.AutoFormatter'}, + 'access': {'class': 'sanic.logging.formatter.AutoAccessFormatter'} + } +} +``` + +## Changing Sanic loggers + +.. column:: + +``` +To use your own logging config, simply use `logging.config.dictConfig`, or pass `log_config` when you initialize Sanic app. +``` + +.. column:: + +```` +```python +app = Sanic('logging_example', log_config=LOGGING_CONFIG) + +if __name__ == "__main__": + app.run(access_log=False) +``` +```` + +.. column:: + +``` +But, what if you do not want to control the logging completely, just change the formatter for example? Here, we will import the default logging config and modify only the parts that we want to force (for example) to use the `ProdFormatter` all of the time. +``` + +.. column:: + +```` +```python +from sanic.log import LOGGING_CONFIG_DEFAULTS + +LOGGING_CONFIG_DEFAULTS['formatters']['generic']['class'] = 'sanic.logging.formatter.ProdFormatter' +LOGGING_CONFIG_DEFAULTS['formatters']['access']['class'] = 'sanic.logging.formatter.ProdAccessFormatter' + +app = Sanic('logging_example', log_config=LOGGING_CONFIG_DEFAULTS) +``` +```` + +.. tip:: FYI + +``` +Logging in Python is a relatively cheap operation. However, if you are serving a high number of requests and performance is a concern, all of that time logging out access logs adds up and becomes quite expensive. + +This is a good opportunity to place Sanic behind a proxy (like nginx) and to do your access logging there. You will see a *significant* increase in overall performance by disabling the `access_log`. + +For optimal production performance, it is advised to run Sanic with `debug` and `access_log` disabled: `app.run(debug=False, access_log=False)` +``` + +## Access logger additional parameters + +Sanic provides additional parameters to the access logger. + +| Log Context Parameter | Parameter Value | Datatype | +| --------------------- | ------------------------------------ | -------- | +| `host` | `request.ip` | `str` | +| `request` | `request.method + " " + request.url` | `str` | +| `status` | `response` | `int` | +| `byte` | `len(response.body)` | `int` | +| `duration` | | `float` | + +## Legacy logging + +Many logging changes were introduced in Sanic 24.3. The main changes were related to logging formats. If you prefer the legacy logging format, you can use the `sanic.logging.formatter.LegacyFormatter` and `sanic.logging.formatter.LegacyAccessFormatter` formatters. diff --git a/guide/content/ko/guide/best-practices/testing.md b/guide/content/ko/guide/best-practices/testing.md new file mode 100644 index 0000000000..a7ac6fdaf6 --- /dev/null +++ b/guide/content/ko/guide/best-practices/testing.md @@ -0,0 +1,3 @@ +# Testing + +See [sanic-testing](../../plugins/sanic-testing/getting-started.md) diff --git a/guide/content/ko/guide/deployment/caddy.md b/guide/content/ko/guide/deployment/caddy.md new file mode 100644 index 0000000000..77d8eb39fc --- /dev/null +++ b/guide/content/ko/guide/deployment/caddy.md @@ -0,0 +1,74 @@ +# Caddy Deployment + +## Introduction + +Caddy is a state-of-the-art web server and proxy that supports up to HTTP/3. Its simplicity lies in its minimalistic configuration and the inbuilt ability to automatically procure TLS certificates for your domains from Let's Encrypt. In this setup, we will configure the Sanic application to serve locally at 127.0.0.1:8001, with Caddy playing the role of the public-facing server for the domain example.com. + +You may install Caddy from your favorite package menager on Windows, Linux and Mac. The package is named `caddy`. + +## Proxied Sanic app + +```python +from sanic import Sanic +from sanic.response import text + +app = Sanic("proxied_example") + +@app.get("/") +def index(request): + # This should display external (public) addresses: + return text( + f"{request.remote_addr} connected to {request.url_for('index')}\n" + f"Forwarded: {request.forwarded}\n" + ) +``` + +To run this application, save as `proxied_example.py`, and use the sanic command-line interface as follows: + +```bash +SANIC_PROXIES_COUNT=1 sanic proxied_example --port 8001 +``` + +Setting the SANIC_PROXIES_COUNT environment variable instructs Sanic to trust the X-Forwarded-\* headers sent by Caddy, allowing it to correctly identify the client's IP address and other information. + +## Caddy is simple + +If you have no other web servers running, you can simply run Caddy CLI (needs `sudo` on Linux): + +```bash +caddy reverse-proxy --from example.com --to :8001 +``` + +This is a complete server that includes a certificate for your domain, http-to-https redirect, proxy headers, streaming and WebSockets. Your Sanic application should now be available on the domain you specified by HTTP versions 1, 2 and 3. Remember to open up UDP/443 on your firewall to enable H3 communications. + +All done? + +Soon enough you'll be needing more than one server, or more control over details, which is where the configuration files come in. The above command is equivalent to this `Caddyfile`, serving as a good starting point for your install: + +``` +example.com { + reverse_proxy localhost:8001 +} +``` + +Some Linux distributions install Caddy such that it reads configuration from `/etc/caddy/Caddyfile`, which `import /etc/caddy/conf.d/*` for each site you are running. If not, you'll need to manually run `caddy run` as a system service, pointing it at the proper config file. Alternatively, use Caddy API mode with `caddy run --resume` for persistent config changes. Note that any Caddyfile loading will replace all prior configuration and thus `caddy-api` is not configurable in this traditional manner. + +## Advanced configuration + +At times, you might need to mix static files and handlers at the site root for cleaner URLs. In Sanic, you'd use `app.static("/", "static", index="index.html")` to achieve this. However, for improved performance, you can offload serving static files to Caddy: + +``` +app.example.com { + # Look for static files first, proxy to Sanic if not found + route { + file_server { + root /srv/sanicexample/static + precompress br # brotli your large scripts and styles + pass_thru + } + reverse_proxy unix//tmp/sanic.socket # sanic --unix /tmp/sanic.socket + } +} +``` + +Please refer to [Caddy documentation](https://caddyserver.com/docs/) for more options. diff --git a/guide/content/ko/guide/deployment/docker.md b/guide/content/ko/guide/deployment/docker.md new file mode 100644 index 0000000000..fe6f0dd05f --- /dev/null +++ b/guide/content/ko/guide/deployment/docker.md @@ -0,0 +1,199 @@ +# Docker Deployment + +## Introduction + +For a long time, the environment has always been a difficult problem for deployment. If there are conflicting configurations in your project, you have to spend a lot of time resolving them. Fortunately, virtualization provides us with a good solution. Docker is one of them. If you don't know Docker, you can visit [Docker official website](https://www.docker.com/) to learn more. + +## Build Image + +Let's start with a simple project. We will use a Sanic project as an example. Assume the project path is `/path/to/SanicDocker`. + +.. column:: + +``` +The directory structure looks like this: +``` + +.. column:: + +```` +```text +# /path/to/SanicDocker +SanicDocker +├── requirements.txt +├── dockerfile +└── server.py +``` +```` + +.. column:: + +``` +And the `server.py` code looks like this: +``` + +.. column:: + +```` +```python +app = Sanic("MySanicApp") + +@app.get('/') +async def hello(request): + return text("OK!") + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=8000) +``` +```` + +.. note:: + +``` +Please note that the host cannot be 127.0.0.1 . In docker container, 127.0.0.1 is the default network interface of the container, only the container can communicate with other containers. more information please visit [Docker network](https://docs.docker.com/engine/reference/commandline/network/) +``` + +Code is ready, let's write the `Dockerfile`: + +```Dockerfile + +FROM sanicframework/sanic:3.8-latest + +WORKDIR /sanic + +COPY . . + +RUN pip install -r requirements.txt + +EXPOSE 8000 + +CMD ["python", "server.py"] +``` + +Run the following command to build the image: + +```shell +docker build -t my-sanic-image . +``` + +## Start Container + +.. column:: + +``` +After the image built, we can start the container use `my-sanic-image`: +``` + +.. column:: + +```` +```shell +docker run --name mysanic -p 8000:8000 -d my-sanic-image +``` +```` + +.. column:: + +``` +Now we can visit `http://localhost:8000` to see the result: +``` + +.. column:: + +```` +```text +OK! +``` +```` + +## Use docker-compose + +If your project consist of multiple services, you can use [docker-compose](https://docs.docker.com/compose/) to manage them. + +for example, we will deploy `my-sanic-image` and `nginx`, achieve through nginx access sanic server. + +.. column:: + +``` +First of all, we need prepare nginx configuration file. create a file named `mysanic.conf`: +``` + +.. column:: + +```` +```nginx +server { + listen 80; + listen [::]:80; + location / { + proxy_pass http://mysanic:8000/; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection upgrade; + proxy_set_header Accept-Encoding gzip; + } +} +``` +```` + +.. column:: + +``` +Then, we need to prepare `docker-compose.yml` file. The content follows: +``` + +.. column:: + +```` +```yaml +version: "3" + +services: + mysanic: + image: my-sanic-image + ports: + - "8000:8000" + restart: always + + mynginx: + image: nginx:1.13.6-alpine + ports: + - "80:80" + depends_on: + - mysanic + volumes: + - ./mysanic.conf:/etc/nginx/conf.d/mysanic.conf + restart: always + +networks: + default: + driver: bridge +``` +```` + +.. column:: + +``` +After that, we can start them: +``` + +.. column:: + +```` +```shell +docker-compose up -d +``` +```` + +.. column:: + +``` +Now, we can visit `http://localhost:80` to see the result: +``` + +.. column:: + +```` +```text +OK! +``` +```` diff --git a/guide/content/ko/guide/deployment/kubernetes.md b/guide/content/ko/guide/deployment/kubernetes.md new file mode 100644 index 0000000000..8d6340de1c --- /dev/null +++ b/guide/content/ko/guide/deployment/kubernetes.md @@ -0,0 +1 @@ +# Kubernetes diff --git a/guide/content/ko/guide/deployment/nginx.md b/guide/content/ko/guide/deployment/nginx.md new file mode 100644 index 0000000000..ee78d2967a --- /dev/null +++ b/guide/content/ko/guide/deployment/nginx.md @@ -0,0 +1,172 @@ +# Nginx Deployment + +## Introduction + +Although Sanic can be run directly on Internet, it may be useful to use a proxy +server such as Nginx in front of it. This is particularly useful for running +multiple virtual hosts on the same IP, serving NodeJS or other services beside +a single Sanic app, and it also allows for efficient serving of static files. +TLS and HTTP/2 are also easily implemented on such proxy. + +We are setting the Sanic app to serve only locally at 127.0.0.1:8001, while the +Nginx installation is responsible for providing the service to public Internet +on domain example.com. Static files will be served by Nginx for maximal +performance. + +## Proxied Sanic app + +```python +from sanic import Sanic +from sanic.response import text + +app = Sanic("proxied_example") + +@app.get("/") +def index(request): + # This should display external (public) addresses: + return text( + f"{request.remote_addr} connected to {request.url_for('index')}\n" + f"Forwarded: {request.forwarded}\n" + ) +``` + +Since this is going to be a system service, save your code to +`/srv/sanicservice/proxied_example.py`. + +For testing, run your app in a terminal using the `sanic` CLI in the folder where you saved the file. + +```bash +SANIC_FORWARDED_SECRET=_hostname sanic proxied_example --port 8001 +``` + +We provide Sanic config `FORWARDED_SECRET` to identify which proxy it gets +the remote addresses from. Note the `_` in front of the local hostname. +This gives basic protection against users spoofing these headers and faking +their IP addresses and more. + +## SSL certificates + +Install Certbot and obtain a certicate for all your domains. This will spin up its own webserver on port 80 for a moment to verify you control the given domain names. + +```bash +certbot -d example.com -d www.example.com +``` + +## Nginx configuration + +Quite much configuration is required to allow fast transparent proxying, but +for the most part these don't need to be modified, so bear with me. + +.. tip:: Note + +``` +Separate upstream section, rather than simply adding the IP after `proxy_pass` +as in most tutorials, is needed for HTTP keep-alive. We also enable streaming, +WebSockets and Nginx serving static files. +``` + +The following config goes inside the `http` section of `nginx.conf` or if your +system uses multiple config files, `/etc/nginx/sites-available/default` or +your own files (be sure to symlink them to `sites-enabled`): + +```nginx +# Files managed by Certbot +ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; +ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; + +# Sanic service +upstream example.com { + keepalive 100; + server 127.0.0.1:8001; + #server unix:/tmp//sanic.sock; +} + +server { + server_name example.com; + listen 443 ssl http2 default_server; + listen [::]:443 ssl http2 default_server; + # Serve static files if found, otherwise proxy to Sanic + location / { + root /srv/sanicexample/static; + try_files $uri @sanic; + } + location @sanic { + proxy_pass http://$server_name; + # Allow fast streaming HTTP/1.1 pipes (keep-alive, unbuffered) + proxy_http_version 1.1; + proxy_request_buffering off; + proxy_buffering off; + proxy_set_header forwarded 'by=\"_$hostname\";$for_addr;proto=$scheme;host=\"$http_host\"'; + # Allow websockets and keep-alive (avoid connection: close) + proxy_set_header connection "upgrade"; + proxy_set_header upgrade $http_upgrade; + } +} + +# Redirect WWW to no-WWW +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name ~^www\.(.*)$; + return 308 $scheme://$1$request_uri; +} + +# Redirect all HTTP to HTTPS with no-WWW +server { + listen 80 default_server; + listen [::]:80 default_server; + server_name ~^(?:www\.)?(.*)$; + return 308 https://$1$request_uri; +} + +# Forwarded for= client IP address formatting +map $remote_addr $for_addr { + ~^[0-9.]+$ "for=$remote_addr"; # IPv4 client address + ~^[0-9A-Fa-f:.]+$ "for=\"[$remote_addr]\""; # IPv6 bracketed and quoted + default "for=unknown"; # Unix socket +} +``` + +Start or restart Nginx for changes to take effect. E.g. + +```bash +systemctl restart nginx +``` + +You should be able to connect your app on `https://example.com`. Any 404 +errors and such will be handled by Sanic's error pages, and whenever a static +file is present at a given path, it will be served by Nginx. + +## Running as a service + +This part is for Linux distributions based on `systemd`. Create a unit file +`/etc/systemd/system/sanicexample.service` + +``` +[Unit] +Description=Sanic Example + +[Service] +DynamicUser=Yes +WorkingDirectory=/srv/sanicservice +Environment=SANIC_PROXY_SECRET=_hostname +ExecStart=sanic proxied_example --port 8001 --fast +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +Then reload service files, start your service and enable it on boot: + +```bash +systemctl daemon-reload +systemctl start sanicexample +systemctl enable sanicexample +``` + +.. tip:: Note + +``` +For brevity we skipped setting up a separate user account and a Python virtual environment or installing your app as a Python module. There are good tutorials on those topics elsewhere that easily apply to Sanic as well. The DynamicUser setting creates a strong sandbox which basically means your application cannot store its data in files, so you may consider setting `User=sanicexample` instead if you need that. +``` diff --git a/guide/content/ko/guide/getting-started.md b/guide/content/ko/guide/getting-started.md new file mode 100644 index 0000000000..1b9b2e042b --- /dev/null +++ b/guide/content/ko/guide/getting-started.md @@ -0,0 +1,106 @@ +# Getting Started + +Before we begin, make sure you are running Python 3.9 or higher. Currently, Sanic is works with Python versions 3.9 – 3.13. + +## Install + +```sh +pip install sanic +``` + +## Hello, world application + +.. column:: + +``` +If you have ever used one of the many decorator based frameworks, this probably looks somewhat familiar to you. + + + +.. note:: + + If you are coming from Flask or another framework, there are a few important things to point out. Remember, Sanic aims for performance, flexibility, and ease of use. These guiding principles have tangible impact on the API and how it works. +``` + +.. column:: + +```` +```python +from sanic import Sanic +from sanic.response import text + +app = Sanic("MyHelloWorldApp") + +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` +```` + +### Important to note + +- Every request handler can either be sync (`def hello_world`) or async (`async def hello_world`). Unless you have a clear reason for it, always go with `async`. +- The `request` object is always the first argument of your handler. Other frameworks pass this around in a context variable to be imported. In the `async` world, this would not work so well and it is far easier (not to mention cleaner and more performant) to be explicit about it. +- You **must** use a response type. MANY other frameworks allow you to have a return value like this: `return "Hello, world."` or this: `return {"foo": "bar"}`. But, in order to do this implicit calling, somewhere in the chain needs to spend valuable time trying to determine what you meant. So, at the expense of this ease, Sanic has decided to require an explicit call. + +### Running + +.. column:: + +``` +Let's save the above file as `server.py`. And launch it. +``` + +.. column:: + +```` +```sh +sanic server +``` +```` + +.. note:: + +``` +This **another** important distinction. Other frameworks come with a built in development server and explicitly say that it is _only_ intended for development use. The opposite is true with Sanic. + +**The packaged server is production ready.** +``` + +## Sanic Extensions + +Sanic intentionally aims for a clean and unopinionated feature list. The project does not want to require you to build your application in a certain way, and tries to avoid prescribing specific development patterns. There are a number of third-party plugins that are built and maintained by the community to add additional features that do not otherwise meet the requirements of the core repository. + +However, in order **to help API developers**, the Sanic organization maintains an official plugin called [Sanic Extensions](../plugins/sanic-ext/getting-started.md) to provide all sorts of goodies, including: + +- **OpenAPI** documentation with Redoc and/or Swagger +- **CORS** protection +- **Dependency injection** into route handlers +- Request query arguments and body input **validation** +- Auto create `HEAD`, `OPTIONS`, and `TRACE` endpoints +- Predefined, endpoint-specific response serializers + +The preferred method to set it up is to install it along with Sanic, but you can also install the packages on their own. + +.. column:: + +```` +```sh +pip install sanic[ext] +``` +```` + +.. column:: + +```` +```sh +pip install sanic sanic-ext +``` +```` + +Starting in v21.12, Sanic will automatically setup Sanic Extensions if it is in the same environment. You will also have access to two additional application properties: + +- `app.extend()` - used to configure Sanic Extensions +- `app.ext` - the `Extend` instance attached to the application + +See [the plugin documentation](../plugins/sanic-ext/getting-started.md) for more information about how to use and work with the plugin diff --git a/guide/content/ko/guide/how-to/README.md b/guide/content/ko/guide/how-to/README.md new file mode 100644 index 0000000000..8e66fc0483 --- /dev/null +++ b/guide/content/ko/guide/how-to/README.md @@ -0,0 +1 @@ +# How to ... diff --git a/guide/content/ko/guide/how-to/authentication.md b/guide/content/ko/guide/how-to/authentication.md new file mode 100644 index 0000000000..9b107b4a9b --- /dev/null +++ b/guide/content/ko/guide/how-to/authentication.md @@ -0,0 +1,116 @@ +# Authentication + +> How do I control authentication and authorization? + +This is an _extremely_ complicated subject to cram into a few snippets. But, this should provide you with an idea on ways to tackle this problem. This example uses [JWTs](https://jwt.io/), but the concepts should be equally applicable to sessions or some other scheme. + +## `server.py` + +```python +from sanic import Sanic, text + +from auth import protected +from login import login + +app = Sanic("AuthApp") +app.config.SECRET = "KEEP_IT_SECRET_KEEP_IT_SAFE" +app.blueprint(login) + +@app.get("/secret") +@protected +async def secret(request): + return text("To go fast, you must be fast.") +``` + +## `login.py` + +```python +import jwt +from sanic import Blueprint, text + +login = Blueprint("login", url_prefix="/login") + +@login.post("/") +async def do_login(request): + token = jwt.encode({}, request.app.config.SECRET) + return text(token) +``` + +## `auth.py` + +```python +from functools import wraps + +import jwt +from sanic import text + +def check_token(request): + if not request.token: + return False + + try: + jwt.decode( + request.token, request.app.config.SECRET, algorithms=["HS256"] + ) + except jwt.exceptions.InvalidTokenError: + return False + else: + return True + +def protected(wrapped): + def decorator(f): + @wraps(f) + async def decorated_function(request, *args, **kwargs): + is_authenticated = check_token(request) + + if is_authenticated: + response = await f(request, *args, **kwargs) + return response + else: + return text("You are unauthorized.", 401) + + return decorated_function + + return decorator(wrapped) +``` + +This decorator pattern is taken from the [decorators page](/en/guide/best-practices/decorators.md). + +--- + +```bash +$ curl localhost:9999/secret -i +HTTP/1.1 401 Unauthorized +content-length: 21 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +You are unauthorized. + + +$ curl localhost:9999/login -X POST +eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e30.rjxS7ztIGt5tpiRWS8BGLUqjQFca4QOetHcZTi061DE + + +$ curl localhost:9999/secret -i -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e30.rjxS7ztIGt5tpiRWS8BGLUqjQFca4QOetHcZTi061DE" +HTTP/1.1 200 OK +content-length: 29 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +To go fast, you must be fast. + + +$ curl localhost:9999/secret -i -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e30.BAD" +HTTP/1.1 401 Unauthorized +content-length: 21 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +You are unauthorized. +``` + +Also, checkout some resources from the community: + +- Awesome Sanic - [Authorization](https://github.com/mekicha/awesome-sanic/blob/master/README.md#authentication) & [Session](https://github.com/mekicha/awesome-sanic/blob/master/README.md#session) +- [EuroPython 2020 - Overcoming access control in web APIs](https://www.youtube.com/watch?v=Uqgoj43ky6A) diff --git a/guide/content/ko/guide/how-to/autodiscovery.md b/guide/content/ko/guide/how-to/autodiscovery.md new file mode 100644 index 0000000000..ab590c4881 --- /dev/null +++ b/guide/content/ko/guide/how-to/autodiscovery.md @@ -0,0 +1,197 @@ +--- +title: Autodiscovery +--- + +# Autodiscovery of Blueprints, Middleware, and Listeners + +> How do I autodiscover the components I am using to build my application? + +One of the first problems someone faces when building an application, is _how_ to structure the project. Sanic makes heavy use of decorators to register route handlers, middleware, and listeners. And, after creating blueprints, they need to be mounted to the application. + +A possible solution is a single file in which **everything** is imported and applied to the Sanic instance. Another is passing around the Sanic instance as a global variable. Both of these solutions have their drawbacks. + +An alternative is autodiscovery. You point your application at modules (already imported, or strings), and let it wire everything up. + +## `server.py` + +```python +from sanic import Sanic +from sanic.response import empty + +import blueprints +from utility import autodiscover + +app = Sanic("auto", register=True) +autodiscover( + app, + blueprints, + "parent.child", + "listeners.something", + recursive=True, +) + +app.route("/")(lambda _: empty()) +``` + +```bash +[2021-03-02 21:37:02 +0200] [880451] [INFO] Goin' Fast @ http://127.0.0.1:9999 +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something @ nested +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something @ level1 +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something @ level3 +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something inside __init__.py +[2021-03-02 21:37:02 +0200] [880451] [INFO] Starting worker [880451] +``` + +## `utility.py` + +```python + +from glob import glob +from importlib import import_module, util +from inspect import getmembers +from pathlib import Path +from types import ModuleType +from typing import Union + +from sanic.blueprints import Blueprint + +def autodiscover( + app, *module_names: Union[str, ModuleType], recursive: bool = False +): + mod = app.__module__ + blueprints = set() + _imported = set() + + def _find_bps(module): + nonlocal blueprints + + for _, member in getmembers(module): + if isinstance(member, Blueprint): + blueprints.add(member) + + for module in module_names: + if isinstance(module, str): + module = import_module(module, mod) + _imported.add(module.__file__) + _find_bps(module) + + if recursive: + base = Path(module.__file__).parent + for path in glob(f"{base}/**/*.py", recursive=True): + if path not in _imported: + name = "module" + if "__init__" in path: + *_, name, __ = path.split("/") + spec = util.spec_from_file_location(name, path) + specmod = util.module_from_spec(spec) + _imported.add(path) + spec.loader.exec_module(specmod) + _find_bps(specmod) + + for bp in blueprints: + app.blueprint(bp) +``` + +## `blueprints/level1.py` + +```python +from sanic import Blueprint +from sanic.log import logger + +level1 = Blueprint("level1") + +@level1.after_server_start +def print_something(app, loop): + logger.debug("something @ level1") +``` + +## `blueprints/one/two/level3.py` + +```python +from sanic import Blueprint +from sanic.log import logger + +level3 = Blueprint("level3") + +@level3.after_server_start +def print_something(app, loop): + logger.debug("something @ level3") +``` + +## `listeners/something.py` + +```python +from sanic import Sanic +from sanic.log import logger + +app = Sanic.get_app("auto") + +@app.after_server_start +def print_something(app, loop): + logger.debug("something") +``` + +## `parent/child/__init__.py` + +```python +from sanic import Blueprint +from sanic.log import logger + +bp = Blueprint("__init__") + +@bp.after_server_start +def print_something(app, loop): + logger.debug("something inside __init__.py") +``` + +## `parent/child/nested.py` + +```python +from sanic import Blueprint +from sanic.log import logger + +nested = Blueprint("nested") + +@nested.after_server_start +def print_something(app, loop): + logger.debug("something @ nested") +``` + +--- + +```text +here is the dir tree +generate with 'find . -type d -name "__pycache__" -exec rm -rf {} +; tree' + +. # run 'sanic sever -d' here +├── blueprints +│ ├── __init__.py # you need add this file, just empty +│ ├── level1.py +│ └── one +│ └── two +│ └── level3.py +├── listeners +│ └── something.py +├── parent +│ └── child +│ ├── __init__.py +│ └── nested.py +├── server.py +└── utility.py +``` + +```sh +source ./.venv/bin/activate # activate the python venv which sanic is installed in +sanic sever -d # run this in the directory containing server.py +``` + +```text +you will see "something ***" like this: + +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something inside __init__.py +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something @ level3 +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something @ level1 +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something @ nested +``` diff --git a/guide/content/ko/guide/how-to/cors.md b/guide/content/ko/guide/how-to/cors.md new file mode 100644 index 0000000000..53d4ebe3c3 --- /dev/null +++ b/guide/content/ko/guide/how-to/cors.md @@ -0,0 +1,138 @@ +--- +title: CORS +--- + +# Cross-origin resource sharing (CORS) + +> How do I configure my application for CORS? + +.. note:: + +``` +🏆 The best solution is to use [Sanic Extensions](../../plugins/sanic-ext/http/cors.md). + +However, if you would like to build your own version, you could use this limited example as a starting point. +``` + +### `server.py` + +```python +from sanic import Sanic, text + +from cors import add_cors_headers +from options import setup_options + +app = Sanic("app") + +@app.route("/", methods=["GET", "POST"]) +async def do_stuff(request): + return text("...") + +# Add OPTIONS handlers to any route that is missing it +app.register_listener(setup_options, "before_server_start") + +# Fill in CORS headers +app.register_middleware(add_cors_headers, "response") +``` + +## `cors.py` + +```python +from typing import Iterable + +def _add_cors_headers(response, methods: Iterable[str]) -> None: + allow_methods = list(set(methods)) + if "OPTIONS" not in allow_methods: + allow_methods.append("OPTIONS") + headers = { + "Access-Control-Allow-Methods": ",".join(allow_methods), + "Access-Control-Allow-Origin": "mydomain.com", + "Access-Control-Allow-Credentials": "true", + "Access-Control-Allow-Headers": ( + "origin, content-type, accept, " + "authorization, x-xsrf-token, x-request-id" + ), + } + response.headers.extend(headers) + +def add_cors_headers(request, response): + if request.method != "OPTIONS": + methods = [method for method in request.route.methods] + _add_cors_headers(response, methods) +``` + +## `options.py` + +```python +from collections import defaultdict +from typing import Dict, FrozenSet + +from sanic import Sanic, response +from sanic.router import Route + +from cors import _add_cors_headers + +def _compile_routes_needing_options( + routes: Dict[str, Route] +) -> Dict[str, FrozenSet]: + needs_options = defaultdict(list) + # This is 21.12 and later. You will need to change this for older versions. + for route in routes.values(): + if "OPTIONS" not in route.methods: + needs_options[route.uri].extend(route.methods) + + return { + uri: frozenset(methods) for uri, methods in dict(needs_options).items() + } + +def _options_wrapper(handler, methods): + def wrapped_handler(request, *args, **kwargs): + nonlocal methods + return handler(request, methods) + + return wrapped_handler + +async def options_handler(request, methods) -> response.HTTPResponse: + resp = response.empty() + _add_cors_headers(resp, methods) + return resp + +def setup_options(app: Sanic, _): + app.router.reset() + needs_options = _compile_routes_needing_options(app.router.routes_all) + for uri, methods in needs_options.items(): + app.add_route( + _options_wrapper(options_handler, methods), + uri, + methods=["OPTIONS"], + ) + app.router.finalize() +``` + +--- + +``` +$ curl localhost:9999/ -i +HTTP/1.1 200 OK +Access-Control-Allow-Methods: OPTIONS,POST,GET +Access-Control-Allow-Origin: mydomain.com +Access-Control-Allow-Credentials: true +Access-Control-Allow-Headers: origin, content-type, accept, authorization, x-xsrf-token, x-request-id +content-length: 3 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +... + +$ curl localhost:9999/ -i -X OPTIONS +HTTP/1.1 204 No Content +Access-Control-Allow-Methods: GET,POST,OPTIONS +Access-Control-Allow-Origin: mydomain.com +Access-Control-Allow-Credentials: true +Access-Control-Allow-Headers: origin, content-type, accept, authorization, x-xsrf-token, x-request-id +connection: keep-alive +``` + +Also, checkout some resources from the community: + +- [Awesome Sanic](https://github.com/mekicha/awesome-sanic/blob/master/README.md#frontend) diff --git a/guide/content/ko/guide/how-to/csrf.md b/guide/content/ko/guide/how-to/csrf.md new file mode 100644 index 0000000000..7f982ff12f --- /dev/null +++ b/guide/content/ko/guide/how-to/csrf.md @@ -0,0 +1 @@ +csrf diff --git a/guide/content/ko/guide/how-to/db.md b/guide/content/ko/guide/how-to/db.md new file mode 100644 index 0000000000..60ca822eac --- /dev/null +++ b/guide/content/ko/guide/how-to/db.md @@ -0,0 +1 @@ +connecting to data sources diff --git a/guide/content/ko/guide/how-to/decorators.md b/guide/content/ko/guide/how-to/decorators.md new file mode 100644 index 0000000000..9ef318134e --- /dev/null +++ b/guide/content/ko/guide/how-to/decorators.md @@ -0,0 +1 @@ +decorators diff --git a/guide/content/ko/guide/how-to/mounting.md b/guide/content/ko/guide/how-to/mounting.md new file mode 100644 index 0000000000..47bf056fbb --- /dev/null +++ b/guide/content/ko/guide/how-to/mounting.md @@ -0,0 +1,53 @@ +# Application Mounting + +> How do I mount my application at some path above the root? + +```python +# server.py +from sanic import Sanic, text + +app = Sanic("app") +app.config.SERVER_NAME = "example.com/api" + +@app.route("/foo") +def handler(request): + url = app.url_for("handler", _external=True) + return text(f"URL: {url}") +``` + +```yaml +# docker-compose.yml +version: "3.7" +services: + app: + image: nginx:alpine + ports: + - 80:80 + volumes: + - type: bind + source: ./conf + target: /etc/nginx/conf.d/default.conf +``` + +```nginx +# conf +server { + listen 80; + + # Computed data service + location /api/ { + proxy_pass http://:9999/; + proxy_set_header Host example.com; + } +} +``` + +```bash +$ docker-compose up -d +$ sanic server.app --port=9999 --host=0.0.0.0 +``` + +```bash +$ curl localhost/api/foo +URL: http://example.com/api/foo +``` diff --git a/guide/content/ko/guide/how-to/orm.md b/guide/content/ko/guide/how-to/orm.md new file mode 100644 index 0000000000..5f7f188d9a --- /dev/null +++ b/guide/content/ko/guide/how-to/orm.md @@ -0,0 +1,467 @@ +# ORM + +> How do I use SQLAlchemy with Sanic ? + +All ORM tools can work with Sanic, but non-async ORM tool have a impact on Sanic performance. +There are some orm packages who support + +At present, there are many ORMs that support Python's `async`/`await` keywords. Some possible choices include: + +- [Mayim](https://ahopkins.github.io/mayim/) +- [SQLAlchemy 1.4](https://docs.sqlalchemy.org/en/14/changelog/changelog_14.html) +- [tortoise-orm](https://github.com/tortoise/tortoise-orm) + +Integration in to your Sanic application is fairly simple: + +## Mayim + +Mayim ships with [an extension for Sanic Extensions](https://ahopkins.github.io/mayim/guide/extensions.html#sanic), which makes it super simple to get started with Sanic. It is certainly possible to run Mayim with Sanic without the extension, but it is recommended because it handles all of the [lifecycle events](https://sanic.dev/en/guide/basics/listeners.html) and [dependency injections](https://sanic.dev/en/plugins/sanic-ext/injection.html). + +.. column:: + +``` +### Dependencies + +First, we need to install the required dependencies. See [Mayim docs](https://ahopkins.github.io/mayim/guide/install.html#postgres) for the installation needed for your DB driver. +``` + +.. column:: + +```` +```shell +pip install sanic-ext +pip install mayim[postgres] +``` +```` + +.. column:: + +``` +### Define ORM Model + +Mayim allows you to use whatever you want for models. Whether it is [dataclasses](https://docs.python.org/3/library/dataclasses.html), [pydantic](https://pydantic-docs.helpmanual.io/), [attrs](https://www.attrs.org/en/stable/), or even just plain `dict` objects. Since it works very nicely [out of the box with Pydantic](https://ahopkins.github.io/mayim/guide/pydantic.html), that is what we will use here. +``` + +.. column:: + +```` +```python +# ./models.py +from pydantic import BaseModel + +class City(BaseModel): + id: int + name: str + district: str + population: int + +class Country(BaseModel): + code: str + name: str + continent: str + region: str + capital: City +``` +```` + +.. column:: + +``` +### Define SQL + +If you are unfamiliar, Mayim is different from other ORMs in that it is one-way, SQL-first. This means you define your own queries either inline, or in a separate `.sql` file, which is what we will do here. +``` + +.. column:: + +```` +```sql +-- ./queries/select_all_countries.sql +SELECT country.code, + country.name, + country.continent, + country.region, + ( + SELECT row_to_json(q) + FROM ( + SELECT city.id, + city.name, + city.district, + city.population + ) q + ) capital +FROM country + JOIN city ON country.capital = city.id +ORDER BY country.name ASC +LIMIT $limit OFFSET $offset; +``` +```` + +.. column:: + +``` +### Create Sanic App and Async Engine + +We need to create the app instance and attach the `SanicMayimExtension` with any executors. +``` + +.. column:: + +```` +```python +# ./server.py +from sanic import Sanic, Request, json +from sanic_ext import Extend +from mayim.executor import PostgresExecutor +from mayim.extensions import SanicMayimExtension +from models import Country + +class CountryExecutor(PostgresExecutor): + async def select_all_countries( + self, limit: int = 4, offset: int = 0 + ) -> list[Country]: + ... + +app = Sanic("Test") +Extend.register( + SanicMayimExtension( + executors=[CountryExecutor], + dsn="postgres://...", + ) +) +``` +```` + +.. column:: + +``` +### Register Routes + +Because we are using Mayim's extension for Sanic, we have the automatic `CountryExecutor` injection into the route handler. It makes for an easy, type-annotated development experience. +``` + +.. column:: + +```` +```python +@app.get("/") +async def handler(request: Request, executor: CountryExecutor): + countries = await executor.select_all_countries() + return json({"countries": [country.dict() for country in co +``` +```` + +.. column:: + +``` +### Send Requests +``` + +.. column:: + +```` +```sh +curl 'http://127.0.0.1:8000' +{"countries":[{"code":"AFG","name":"Afghanistan","continent":"Asia","region":"Southern and Central Asia","capital":{"id":1,"name":"Kabul","district":"Kabol","population":1780000}},{"code":"ALB","name":"Albania","continent":"Europe","region":"Southern Europe","capital":{"id":34,"name":"Tirana","district":"Tirana","population":270000}},{"code":"DZA","name":"Algeria","continent":"Africa","region":"Northern Africa","capital":{"id":35,"name":"Alger","district":"Alger","population":2168000}},{"code":"ASM","name":"American Samoa","continent":"Oceania","region":"Polynesia","capital":{"id":54,"name":"Fagatogo","district":"Tutuila","population":2323}}]} +``` +```` + +## SQLAlchemy + +Because [SQLAlchemy 1.4](https://docs.sqlalchemy.org/en/14/changelog/changelog_14.html) has added native support for `asyncio`, Sanic can finally work well with SQLAlchemy. Be aware that this functionality is still considered _beta_ by the SQLAlchemy project. + +.. column:: + +``` +### Dependencies + +First, we need to install the required dependencies. In the past, the dependencies installed were `sqlalchemy` and `pymysql`, but now `sqlalchemy` and `aiomysql` are needed. +``` + +.. column:: + +```` +```shell +pip install -U sqlalchemy +pip install -U aiomysql +``` +```` + +.. column:: + +``` +### Define ORM Model + +ORM model creation remains the same. +``` + +.. column:: + +```` +```python +# ./models.py +from sqlalchemy import INTEGER, Column, ForeignKey, String +from sqlalchemy.orm import declarative_base, relationship + +Base = declarative_base() + +class BaseModel(Base): + __abstract__ = True + id = Column(INTEGER(), primary_key=True) + +class Person(BaseModel): + __tablename__ = "person" + name = Column(String()) + cars = relationship("Car") + + def to_dict(self): + return {"name": self.name, "cars": [{"brand": car.brand} for car in self.cars]} + +class Car(BaseModel): + __tablename__ = "car" + + brand = Column(String()) + user_id = Column(ForeignKey("person.id")) + user = relationship("Person", back_populates="cars") +``` +```` + +.. column:: + +``` +### Create Sanic App and Async Engine + +Here we use mysql as the database, and you can also choose PostgreSQL/SQLite. Pay attention to changing the driver from `aiomysql` to `asyncpg`/`aiosqlite`. +``` + +.. column:: + +```` +```python +# ./server.py +from sanic import Sanic +from sqlalchemy.ext.asyncio import create_async_engine + +app = Sanic("my_app") + +bind = create_async_engine("mysql+aiomysql://root:root@localhost/test", echo=True) +``` +```` + +.. column:: + +``` +### Register Middlewares + +The request middleware creates an usable `AsyncSession` object and set it to `request.ctx` and `_base_model_session_ctx`. + +Thread-safe variable `_base_model_session_ctx` helps you to use the session object instead of fetching it from `request.ctx`. +``` + +.. column:: + +```` +```python +# ./server.py +from contextvars import ContextVar + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.orm import sessionmaker + +_sessionmaker = sessionmaker(bind, AsyncSession, expire_on_commit=False) + +_base_model_session_ctx = ContextVar("session") + +@app.middleware("request") +async def inject_session(request): + request.ctx.session = _sessionmaker() + request.ctx.session_ctx_token = _base_model_session_ctx.set(request.ctx.session) + +@app.middleware("response") +async def close_session(request, response): + if hasattr(request.ctx, "session_ctx_token"): + _base_model_session_ctx.reset(request.ctx.session_ctx_token) + await request.ctx.session.close() +``` +```` + +.. column:: + +``` +### Register Routes + +According to sqlalchemy official docs, `session.query` will be legacy in 2.0, and the 2.0 way to query an ORM object is using `select`. +``` + +.. column:: + +```` +```python +# ./server.py +from sqlalchemy import select +from sqlalchemy.orm import selectinload +from sanic.response import json + +from models import Car, Person + +@app.post("/user") +async def create_user(request): + session = request.ctx.session + async with session.begin(): + car = Car(brand="Tesla") + person = Person(name="foo", cars=[car]) + session.add_all([person]) + return json(person.to_dict()) + +@app.get("/user/") +async def get_user(request, pk): + session = request.ctx.session + async with session.begin(): + stmt = select(Person).where(Person.id == pk).options(selectinload(Person.cars)) + result = await session.execute(stmt) + person = result.scalar() + + if not person: + return json({}) + + return json(person.to_dict()) +``` +```` + +.. column:: + +``` +### Send Requests +``` + +.. column:: + +```` +```sh +curl --location --request POST 'http://127.0.0.1:8000/user' +{"name":"foo","cars":[{"brand":"Tesla"}]} +``` + +```sh +curl --location --request GET 'http://127.0.0.1:8000/user/1' +{"name":"foo","cars":[{"brand":"Tesla"}]} +``` +```` + +## Tortoise-ORM + +.. column:: + +``` +### Dependencies + +tortoise-orm's dependency is very simple, you just need install tortoise-orm. +``` + +.. column:: + +```` +```shell +pip install -U tortoise-orm +``` +```` + +.. column:: + +``` +### Define ORM Model + +If you are familiar with Django, you should find this part very familiar. +``` + +.. column:: + +```` +```python +# ./models.py +from tortoise import Model, fields + +class Users(Model): + id = fields.IntField(pk=True) + name = fields.CharField(50) + + def __str__(self): + return f"I am {self.name}" +``` +```` + +.. column:: + +``` +### Create Sanic App and Async Engine + +Tortoise-orm provides a set of registration interface, which is convenient for users, and you can use it to create database connection easily. +``` + +.. column:: + +```` +```python +# ./main.py + +from models import Users +from tortoise.contrib.sanic import register_tortoise + +app = Sanic(__name__) + +register_tortoise( + app, db_url="mysql://root:root@localhost/test", modules={"models": ["models"]}, generate_schemas=True +) + +``` +```` + +.. column:: + +``` +### Register Routes +``` + +.. column:: + +```` +```python +# ./main.py + +from models import Users +from sanic import Sanic, response + +@app.route("/user") +async def list_all(request): + users = await Users.all() + return response.json({"users": [str(user) for user in users]}) + +@app.route("/user/") +async def get_user(request, pk): + user = await Users.query(pk=pk) + return response.json({"user": str(user)}) + +if __name__ == "__main__": + app.run(port=5000) +``` +```` + +.. column:: + +``` +### Send Requests +``` + +.. column:: + +```` +```sh +curl --location --request POST 'http://127.0.0.1:8000/user' +{"users":["I am foo", "I am bar"]} +``` + +```sh +curl --location --request GET 'http://127.0.0.1:8000/user/1' +{"user": "I am foo"} +``` +```` diff --git a/guide/content/ko/guide/how-to/serialization.md b/guide/content/ko/guide/how-to/serialization.md new file mode 100644 index 0000000000..0dfc62d35b --- /dev/null +++ b/guide/content/ko/guide/how-to/serialization.md @@ -0,0 +1 @@ +# Serialization diff --git a/guide/content/ko/guide/how-to/server-sent-events.md b/guide/content/ko/guide/how-to/server-sent-events.md new file mode 100644 index 0000000000..7daf86745e --- /dev/null +++ b/guide/content/ko/guide/how-to/server-sent-events.md @@ -0,0 +1 @@ +sse diff --git a/guide/content/ko/guide/how-to/static-redirects.md b/guide/content/ko/guide/how-to/static-redirects.md new file mode 100644 index 0000000000..d177756bfc --- /dev/null +++ b/guide/content/ko/guide/how-to/static-redirects.md @@ -0,0 +1,112 @@ +# "Static" Redirects + +> How do I configure static redirects? + +## `app.py` + +```python +### SETUP ### +import typing +import sanic, sanic.response + +# Create the Sanic app +app = sanic.Sanic(__name__) + +# This dictionary represents your "static" +# redirects. For example, these values +# could be pulled from a configuration file. +REDIRECTS = { + '/':'/hello_world', # Redirect '/' to '/hello_world' + '/hello_world':'/hello_world.html' # Redirect '/hello_world' to 'hello_world.html' +} + +# This function will return another function +# that will return the configured value +# regardless of the arguments passed to it. +def get_static_function(value:typing.Any) -> typing.Callable[..., typing.Any]: + return lambda *_, **__: value + +### ROUTING ### +# Iterate through the redirects +for src, dest in REDIRECTS.items(): + # Create the redirect response object + response:sanic.HTTPResponse = sanic.response.redirect(dest) + + # Create the handler function. Typically, + # only a sanic.Request object is passed + # to the function. This object will be + # ignored. + handler = get_static_function(response) + + # Route the src path to the handler + app.route(src)(handler) + +# Route some file and client resources +app.static('/files/', 'files') +app.static('/', 'client') + +### RUN ### +if __name__ == '__main__': + app.run( + '127.0.0.1', + 10000 + ) +``` + +## `client/hello_world.html` + +```html + + + + + + + Hello World + + + +
+ Hello world! +
+ + +``` + +## `client/hello_world.css` + +```css +#hello_world { + width: 1000px; + margin-left: auto; + margin-right: auto; + margin-top: 100px; + + padding: 100px; + color: aqua; + text-align: center; + font-size: 100px; + font-family: monospace; + + background-color: rgba(0, 0, 0, 0.75); + + border-radius: 10px; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.75); +} + +body { + background-image: url("/files/grottoes.jpg"); + background-repeat: no-repeat; + background-size: cover; +} +``` + +## `files/grottoes.jpg` + +![lake](/assets/images/grottoes.jpg) + +--- + +Also, checkout some resources from the community: + +- [Static Routing Example](https://github.com/Perzan/sanic-static-routing-example) diff --git a/guide/content/ko/guide/how-to/table-of-contents.md b/guide/content/ko/guide/how-to/table-of-contents.md new file mode 100644 index 0000000000..d2aeba0461 --- /dev/null +++ b/guide/content/ko/guide/how-to/table-of-contents.md @@ -0,0 +1,17 @@ +--- +title: Table of Contents +--- + +# Table of Contents + +We have compiled fully working examples to answer common questions and user cases. For the most part, the examples are as minimal as possible, but should be complete and runnable solutions. + +| Page | How do I ... | +| :------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Application mounting](./mounting.md) | ... mount my application at some path above the root? | +| [Authentication](./authentication.md) | ... control authentication and authorization? | +| [Autodiscovery](./autodiscovery.md) | ... autodiscover the components I am using to build my application? | +| [CORS](./cors.md) | ... configure my application for CORS? | +| [ORM](./orm) | ... use an ORM with Sanic? | +| ["Static" Redirects](./static-redirects.md) | ... configure static redirects | +| [TLS/SSL/HTTPS](./tls.md) | ... run Sanic via HTTPS?
... redirect HTTP to HTTPS? | diff --git a/guide/content/ko/guide/how-to/task-queue.md b/guide/content/ko/guide/how-to/task-queue.md new file mode 100644 index 0000000000..c5ee7c54a3 --- /dev/null +++ b/guide/content/ko/guide/how-to/task-queue.md @@ -0,0 +1 @@ +task queue diff --git a/guide/content/ko/guide/how-to/tls.md b/guide/content/ko/guide/how-to/tls.md new file mode 100644 index 0000000000..5a4859598a --- /dev/null +++ b/guide/content/ko/guide/how-to/tls.md @@ -0,0 +1,199 @@ +--- +title: TLS/SSL/HTTPS +--- + +# TLS/SSL/HTTPS + +> How do I run Sanic via HTTPS? + +If you do not have TLS certificates yet, [see the end of this page](./tls.md#get-certificates-for-your-domain-names). + +## Single domain and single certificate + +.. column:: + +``` +Let Sanic automatically load your certificate files, which need to be named `fullchain.pem` and `privkey.pem` in the given folder: +``` + +.. column:: + +```` +```sh +sudo sanic myserver:app -H :: -p 443 \ + --tls /etc/letsencrypt/live/example.com/ +``` +```python +app.run("::", 443, ssl="/etc/letsencrypt/live/example.com/") +``` +```` + +.. column:: + +``` +Or, you can pass cert and key filenames separately as a dictionary: + +Additionally, `password` may be added if the key is encrypted, all fields except for the password are passed to `request.conn_info.cert`. +``` + +.. column:: + +```` +```python +ssl = { + "cert": "/path/to/fullchain.pem", + "key": "/path/to/privkey.pem", + "password": "for encrypted privkey file", # Optional +} +app.run(host="0.0.0.0", port=8443, ssl=ssl) +``` +```` + +.. column:: + +``` +Alternatively, [`ssl.SSLContext`](https://docs.python.org/3/library/ssl.html) may be passed, if you need full control over details such as which crypto algorithms are permitted. By default Sanic only allows secure algorithms, which may restrict access from very old devices. +``` + +.. column:: + +```` +```python +import ssl + +context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) +context.load_cert_chain("certs/fullchain.pem", "certs/privkey.pem") + +app.run(host="0.0.0.0", port=8443, ssl=context) +``` +```` + +## Multiple domains with separate certificates + +.. column:: + +``` +A list of multiple certificates may be provided, in which case Sanic chooses the one matching the hostname the user is connecting to. This occurs so early in the TLS handshake that Sanic has not sent any packets to the client yet. + +If the client sends no SNI (Server Name Indication), the first certificate on the list will be used even though on the client browser it will likely fail with a TLS error due to name mismatch. To prevent this fallback and to cause immediate disconnection of clients without a known hostname, add `None` as the first entry on the list. `--tls-strict-host` is the equivalent CLI option. +``` + +.. column:: + +```` +```python +ssl = ["certs/example.com/", "certs/bigcorp.test/"] +app.run(host="0.0.0.0", port=8443, ssl=ssl) +``` +```sh +sanic myserver:app + --tls certs/example.com/ + --tls certs/bigcorp.test/ + --tls-strict-host +``` +```` + +.. tip:: + +``` +You may also use `None` in front of a single certificate if you do not wish to reveal your certificate, true hostname or site content to anyone connecting to the IP address instead of the proper DNS name. +``` + +.. column:: + +``` +Dictionaries can be used on the list. This allows also specifying which domains a certificate matches to, although the names present on the certificate itself cannot be controlled from here. If names are not specified, the names from the certificate itself are used. + +To only allow connections to the main domain **example.com** and only to subdomains of **bigcorp.test**: +``` + +.. column:: + +```` +```python +ssl = [ + None, # No fallback if names do not match! + { + "cert": "certs/example.com/fullchain.pem", + "key": "certs/example.com/privkey.pem", + "names": ["example.com", "*.bigcorp.test"], + } +] +app.run(host="0.0.0.0", port=8443, ssl=ssl) +``` +```` + +## Accessing TLS information in handlers via `request.conn_info` fields + +- `.ssl` - is the connection secure (bool) +- `.cert` - certificate info and dict fields of the currently active cert (dict) +- `.server_name` - the SNI sent by the client (str, may be empty) + +Do note that all `conn_info` fields are per connection, where there may be many requests over time. If a proxy is used in front of your server, these requests on the same pipe may even come from different users. + +## Redirect HTTP to HTTPS, with certificate requests still over HTTP + +In addition to your normal server(s) running HTTPS, run another server for redirection, `http_redir.py`: + +```python +from sanic import Sanic, exceptions, response + +app = Sanic("http_redir") + +# Serve ACME/certbot files without HTTPS, for certificate renewals +app.static("/.well-known", "/var/www/.well-known", resource_type="dir") + +@app.exception(exceptions.NotFound, exceptions.MethodNotSupported) +def redirect_everything_else(request, exception): + server, path = request.server_name, request.path + if server and path.startswith("/"): + return response.redirect(f"https://{server}{path}", status=308) + return response.text("Bad Request. Please use HTTPS!", status=400) +``` + +It is best to setup this as a systemd unit separate of your HTTPS servers. You may need to run HTTP while initially requesting your certificates, while you cannot run the HTTPS server yet. Start for IPv4 and IPv6: + +``` +sanic http_redir:app -H 0.0.0.0 -p 80 +sanic http_redir:app -H :: -p 80 +``` + +Alternatively, it is possible to run the HTTP redirect application from the main application: + +```python +# app == Your main application +# redirect == Your http_redir application +@app.before_server_start +async def start(app, _): + app.ctx.redirect = await redirect.create_server( + port=80, return_asyncio_server=True + ) + app.add_task(runner(redirect, app.ctx.redirect)) + +@app.before_server_stop +async def stop(app, _): + await app.ctx.redirect.close() + +async def runner(app, app_server): + app.state.is_running = True + try: + app.signalize() + app.finalize() + app.state.is_started = True + await app_server.serve_forever() + finally: + app.state.is_running = False + app.state.is_stopping = True +``` + +## Get certificates for your domain names + +You can get free certificates from [Let's Encrypt](https://letsencrypt.org/). Install [certbot](https://certbot.eff.org/) via your package manager, and request a certificate: + +```sh +sudo certbot certonly --key-type ecdsa --preferred-chain "ISRG Root X1" -d example.com -d www.example.com +``` + +Multiple domain names may be added by further `-d` arguments, all stored into a single certificate which gets saved to `/etc/letsencrypt/live/example.com/` as per **the first domain** that you list here. + +The key type and preferred chain options are necessary for getting a minimal size certificate file, essential for making your server run as _fast_ as possible. The chain will still contain one RSA certificate until when Let's Encrypt gets their new EC chain trusted in all major browsers. diff --git a/guide/content/ko/guide/how-to/validation.md b/guide/content/ko/guide/how-to/validation.md new file mode 100644 index 0000000000..d45b2dea4c --- /dev/null +++ b/guide/content/ko/guide/how-to/validation.md @@ -0,0 +1 @@ +validation diff --git a/guide/content/ko/guide/how-to/websocket-feed.md b/guide/content/ko/guide/how-to/websocket-feed.md new file mode 100644 index 0000000000..ae2abc2def --- /dev/null +++ b/guide/content/ko/guide/how-to/websocket-feed.md @@ -0,0 +1 @@ +websocket feed diff --git a/guide/content/ko/guide/introduction.md b/guide/content/ko/guide/introduction.md new file mode 100644 index 0000000000..0e67eaf473 --- /dev/null +++ b/guide/content/ko/guide/introduction.md @@ -0,0 +1,71 @@ +# Introduction + +Sanic is a Python 3.9+ web server and web framework that’s written to go fast. It allows the usage of the async/await syntax added in Python 3.5, which makes your code non-blocking and speedy. + +.. attrs:: +:class: introduction-table + +``` +| | | +|--|--| +| Build | [![Tests](https://github.com/sanic-org/sanic/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/sanic-org/sanic/actions/workflows/tests.yml) | +| Docs | [![User Guide](https://img.shields.io/badge/user%20guide-sanic-ff0068)](https://sanicframework.org/) [![Documentation](https://readthedocs.org/projects/sanic/badge/?version=latest)](http://sanic.readthedocs.io/en/latest/?badge=latest) | +| Package | [![PyPI](https://img.shields.io/pypi/v/sanic.svg)](https://pypi.python.org/pypi/sanic/) [![PyPI version](https://img.shields.io/pypi/pyversions/sanic.svg)](https://pypi.python.org/pypi/sanic/) [![Wheel](https://img.shields.io/pypi/wheel/sanic.svg)](https://pypi.python.org/pypi/sanic) [![Supported implementations](https://img.shields.io/pypi/implementation/sanic.svg)](https://pypi.python.org/pypi/sanic) [![Code style black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) | +| Support | [![Forums](https://img.shields.io/badge/forums-community-ff0068.svg)](https://community.sanicframework.org/) [![Discord](https://img.shields.io/discord/812221182594121728?logo=discord)](https://discord.gg/FARQzAEMAA) [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/mekicha/awesome-sanic) | +| Stats | [![Monthly Downloads](https://img.shields.io/pypi/dm/sanic.svg)](https://pepy.tech/project/sanic) [![Weekly Downloads](https://img.shields.io/pypi/dw/sanic.svg)](https://pepy.tech/project/sanic) [![Conda downloads](https://img.shields.io/conda/dn/conda-forge/sanic.svg)](https://anaconda.org/conda-forge/sanic) | +``` + +## What is it? + +First things first, before you jump in the water, you should know that Sanic is different than other frameworks. + +Right there in that first sentence there is a huge mistake because Sanic is _both_ a **framework** and a **web server**. In the deployment section we will talk a little bit more about this. + +But, remember, out of the box Sanic comes with everything you need to write, deploy, and scale a production grade web application. 🚀 + +## Goal + +> To provide a simple way to get up and running a highly performant HTTP server that is easy to build, to expand, and ultimately to scale. + +## Features + +.. column:: + +``` +### Core + +- Built in, **_fast_** web server +- Production ready +- Highly scalable +- ASGI compliant +- Simple and intuitive API design +- By the community, for the community +``` + +.. column:: + +``` +### Sanic Extensions [[learn more](../plugins/sanic-ext/getting-started.md)] + +- CORS protection +- Template rendering with Jinja +- Dependency injection into route handlers +- OpenAPI documentation with Redoc and/or Swagger +- Predefined, endpoint-specific response serializers +- Request query arguments and body input validation +- Auto create `HEAD`, `OPTIONS`, and `TRACE` endpoints +``` + +## Sponsor + +Check out [open collective](https://opencollective.com/sanic-org) to learn more about helping to fund Sanic. + +## Join the Community + +The main channel for discussion is at the [community forums](https://community.sanicframework.org/). There also is a [Discord Server](https://discord.gg/FARQzAEMAA) for live discussion and chat. + +The Stackoverflow `[sanic]` tag is [actively monitored](https://stackoverflow.com/questions/tagged/sanic) by project maintainers. + +## Contribution + +We are always happy to have new contributions. We have [marked issues good for anyone looking to get started](https://github.com/sanic-org/sanic/issues?q=is%3Aopen+is%3Aissue+label%3Abeginner), and welcome [questions/answers/discussion on the forums](https://community.sanicframework.org/). Please take a look at our [Contribution guidelines](https://github.com/sanic-org/sanic/blob/master/CONTRIBUTING.rst). diff --git a/guide/content/ko/guide/running/app-loader.md b/guide/content/ko/guide/running/app-loader.md new file mode 100644 index 0000000000..03f13e7ec7 --- /dev/null +++ b/guide/content/ko/guide/running/app-loader.md @@ -0,0 +1,98 @@ +--- +title: Dynamic Applications +--- + +# Dynamic Applications + +Running Sanic has been optimized to work with the CLI. If you have not read it yet, you should read [Running Sanic](./running.md#sanic-server) to become familiar with the options. + +.. column:: + +``` +This includes running it as a global scope object... +``` + +.. column:: + +```` +```sh +sanic path.to.server:app +``` +```python +# server.py +app = Sanic("TestApp") + +@app.get("/") +async def handler(request: Request): + return json({"foo": "bar"}) +``` +```` + +.. column:: + +``` +...or, a factory function that creates the `Sanic` application object. +``` + +.. column:: + +```` +```sh +sanic path.to.server:create_app --factory +``` +```python +# server.py +def create_app(): + app = Sanic("TestApp") + + @app.get("/") + async def handler(request: Request): + return json({"foo": "bar"}) + + return app +``` +```` + +**Sometimes, this is not enough ... 🤔** + +Introduced in [v22.9](../release-notes/v22.9.md), Sanic has an `AppLoader` object that is responsible for creating an application in the various [worker processes](./manager.md#how-sanic-server-starts-processes). You can take advantage of this if you need to create a more dynamic startup experience for your application. + +.. column:: + +``` +An `AppLoader` can be passed a callable that returns a `Sanic` instance. That `AppLoader` could be used with the low-level application running API. +``` + +.. column:: + +```` +```python +import sys +from functools import partial + +from sanic import Request, Sanic, json +from sanic.worker.loader import AppLoader + +def attach_endpoints(app: Sanic): + @app.get("/") + async def handler(request: Request): + return json({"app_name": request.app.name}) + +def create_app(app_name: str) -> Sanic: + app = Sanic(app_name) + attach_endpoints(app) + return app + +if __name__ == "__main__": + app_name = sys.argv[-1] + loader = AppLoader(factory=partial(create_app, app_name)) + app = loader.load() + app.prepare(port=9999, dev=True) + Sanic.serve(primary=app, app_loader=loader) +``` +```sh +python path/to/server.py MyTestAppName +``` +```` + +In the above example, the `AppLoader` is created with a `factory` that can be used to create copies of the same application across processes. When doing this, you should explicitly use the `Sanic.serve` pattern shown above so that the `AppLoader` that you create is not replaced. diff --git a/guide/content/ko/guide/running/configuration.md b/guide/content/ko/guide/running/configuration.md new file mode 100644 index 0000000000..057ea4b318 --- /dev/null +++ b/guide/content/ko/guide/running/configuration.md @@ -0,0 +1,328 @@ +# Configuration + +## Basics + +.. column:: + +``` +Sanic holds the configuration in the config attribute of the application object. The configuration object is merely an object that can be modified either using dot-notation or like a dictionary. +``` + +.. column:: + +```` +```python +app = Sanic("myapp") +app.config.DB_NAME = "appdb" +app.config["DB_USER"] = "appuser" +``` +```` + +.. column:: + +``` +You can also use the `update()` method like on regular dictionaries. +``` + +.. column:: + +```` +```python +db_settings = { + 'DB_HOST': 'localhost', + 'DB_NAME': 'appdb', + 'DB_USER': 'appuser' +} +app.config.update(db_settings) +``` +```` + +.. note:: + +``` +It is standard practice in Sanic to name your config values in **uppercase letters**. Indeed, you may experience weird behaviors if you start mixing uppercase and lowercase names. +``` + +## Loading + +### Environment variables + +.. column:: + +``` +Any environment variables defined with the `SANIC_` prefix will be applied to the Sanic config. For example, setting `SANIC_REQUEST_TIMEOUT` will be loaded by the application automatically and fed into the `REQUEST_TIMEOUT` config variable. +``` + +.. column:: + +```` +```bash +$ export SANIC_REQUEST_TIMEOUT=10 +``` +```python +>>> print(app.config.REQUEST_TIMEOUT) +10 +``` +```` + +.. column:: + +``` +You can change the prefix that Sanic is expecting at startup. +``` + +.. column:: + +```` +```bash +$ export MYAPP_REQUEST_TIMEOUT=10 +``` +```python +>>> app = Sanic(__name__, env_prefix='MYAPP_') +>>> print(app.config.REQUEST_TIMEOUT) +10 +``` +```` + +.. column:: + +``` +You can also disable environment variable loading completely. +``` + +.. column:: + +```` +```python +app = Sanic(__name__, load_env=False) +``` +```` + +### Using Sanic.update_config + +The `Sanic` instance has a _very_ versatile method for loading config: `app.update_config`. You can feed it a path to a file, a dictionary, a class, or just about any other sort of object. + +#### From a file + +.. column:: + +``` +Let's say you have `my_config.py` file that looks like this. +``` + +.. column:: + +```` +```python +# my_config.py +A = 1 +B = 2 +``` +```` + +.. column:: + +``` +You can load this as config values by passing its path to `app.update_config`. +``` + +.. column:: + +```` +```python +>>> app.update_config("/path/to/my_config.py") +>>> print(app.config.A) +1 +``` +```` + +.. column:: + +``` +This path also accepts bash style environment variables. +``` + +.. column:: + +```` +```bash +$ export my_path="/path/to" +``` +```python +app.update_config("${my_path}/my_config.py") +``` +```` + +.. note:: + +``` +Just remember that you have to provide environment variables in the format `${environment_variable}` and that `$environment_variable` is not expanded (is treated as "plain" text). +``` + +#### From a dict + +.. column:: + +``` +The `app.update_config` method also works on plain dictionaries. +``` + +.. column:: + +```` +```python +app.update_config({"A": 1, "B": 2}) +``` +```` + +#### From a class or object + +.. column:: + +``` +You can define your own config class, and pass it to `app.update_config` +``` + +.. column:: + +```` +```python +class MyConfig: + A = 1 + B = 2 + +app.update_config(MyConfig) +``` +```` + +.. column:: + +``` +It even could be instantiated. +``` + +.. column:: + +```` +```python +app.update_config(MyConfig()) +``` +```` + +### Type casting + +When loading from environment variables, Sanic will attempt to cast the values to expected Python types. This particularly applies to: + +- `int` +- `float` +- `bool` + +In regards to `bool`, the following _case insensitive_ values are allowed: + +- **`True`**: `y`, `yes`, `yep`, `yup`, `t`, `true`, `on`, `enable`, `enabled`, `1` +- **`False`**: `n`, `no`, `f`, `false`, `off`, `disable`, `disabled`, `0` + +If a value cannot be cast, it will default to a `str`. + +.. column:: + +``` +Additionally, Sanic can be configured to cast additional types using additional type converters. This should be any callable that returns the value or raises a `ValueError`. + +*Added in v21.12* +``` + +.. column:: + +```` +```python +app = Sanic(..., config=Config(converters=[UUID])) +``` +```` + +## Builtin values + +| **Variable** | **Default** | **Description** | +| -------------------------------------------------------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| ACCESS_LOG | True | Disable or enable access log | +| AUTO_EXTEND | True | Control whether [Sanic Extensions](../../plugins/sanic-ext/getting-started.md) will load if it is in the existing virtual environment | +| AUTO_RELOAD | True | Control whether the application will automatically reload when a file changes | +| EVENT_AUTOREGISTER | True | When `True` using the `app.event()` method on a non-existing signal will automatically create it and not raise an exception | +| FALLBACK_ERROR_FORMAT | html | Format of error response if an exception is not caught and handled | +| FORWARDED_FOR_HEADER | X-Forwarded-For | The name of "X-Forwarded-For" HTTP header that contains client and proxy ip | +| FORWARDED_SECRET | None | Used to securely identify a specific proxy server (see below) | +| GRACEFUL_SHUTDOWN_TIMEOUT | 15.0 | How long to wait to force close non-idle connection (sec) | +| INSPECTOR | False | Whether to enable the Inspector | +| INSPECTOR_HOST | localhost | The host for the Inspector | +| INSPECTOR_PORT | 6457 | The port for the Inspector | +| INSPECTOR_TLS_KEY | - | The TLS key for the Inspector | +| INSPECTOR_TLS_CERT | - | The TLS certificate for the Inspector | +| INSPECTOR_API_KEY | - | The API key for the Inspector | +| KEEP_ALIVE_TIMEOUT | 120 | How long to hold a TCP connection open (sec) | +| KEEP_ALIVE | True | Disables keep-alive when False | +| MOTD | True | Whether to display the MOTD (message of the day) at startup | +| MOTD_DISPLAY | {} | Key/value pairs to display additional, arbitrary data in the MOTD | +| NOISY_EXCEPTIONS | False | Force all `quiet` exceptions to be logged | +| PROXIES_COUNT | None | The number of proxy servers in front of the app (e.g. nginx; see below) | +| REAL_IP_HEADER | None | The name of "X-Real-IP" HTTP header that contains real client ip | +| REGISTER | True | Whether the app registry should be enabled | +| REQUEST_BUFFER_SIZE | 65536 | Request buffer size before request is paused, default is 64 Kib | +| REQUEST_ID_HEADER | X-Request-ID | The name of "X-Request-ID" HTTP header that contains request/correlation ID | +| REQUEST_MAX_SIZE | 100000000 | How big a request may be (bytes), default is 100 megabytes | +| REQUEST_MAX_HEADER_SIZE | 8192 | How big a request header may be (bytes), default is 8192 bytes | +| REQUEST_TIMEOUT | 60 | How long a request can take to arrive (sec) | +| RESPONSE_TIMEOUT | 60 | How long a response can take to process (sec) | +| USE_UVLOOP | True | Whether to override the loop policy to use `uvloop`. Supported only with `app.run`. | +| WEBSOCKET_MAX_SIZE | 2^20 | Maximum size for incoming messages (bytes) | +| WEBSOCKET_PING_INTERVAL | 20 | A Ping frame is sent every ping_interval seconds. | +| WEBSOCKET_PING_TIMEOUT | 20 | Connection is closed when Pong is not received after ping_timeout seconds | + +.. tip:: FYI + +``` +- The `USE_UVLOOP` value will be ignored if running with Gunicorn. Defaults to `False` on non-supported platforms (Windows). +- The `WEBSOCKET_` values will be ignored if in ASGI mode. +- v21.12 added: `AUTO_EXTEND`, `MOTD`, `MOTD_DISPLAY`, `NOISY_EXCEPTIONS` +- v22.9 added: `INSPECTOR` +- v22.12 added: `INSPECTOR_HOST`, `INSPECTOR_PORT`, `INSPECTOR_TLS_KEY`, `INSPECTOR_TLS_CERT`, `INSPECTOR_API_KEY` +``` + +## Timeouts + +### REQUEST_TIMEOUT + +A request timeout measures the duration of time between the instant when a new open TCP connection is passed to the +Sanic backend server, and the instant when the whole HTTP request is received. If the time taken exceeds the +`REQUEST_TIMEOUT` value (in seconds), this is considered a Client Error so Sanic generates an `HTTP 408` response +and sends that to the client. Set this parameter's value higher if your clients routinely pass very large request payloads +or upload requests very slowly. + +### RESPONSE_TIMEOUT + +A response timeout measures the duration of time between the instant the Sanic server passes the HTTP request to the Sanic App, and the instant a HTTP response is sent to the client. If the time taken exceeds the `RESPONSE_TIMEOUT` value (in seconds), this is considered a Server Error so Sanic generates an `HTTP 503` response and sends that to the client. Set this parameter's value higher if your application is likely to have long-running process that delay the +generation of a response. + +### KEEP_ALIVE_TIMEOUT + +#### What is Keep Alive? And what does the Keep Alive Timeout value do? + +`Keep-Alive` is a HTTP feature introduced in `HTTP 1.1`. When sending a HTTP request, the client (usually a web browser application) can set a `Keep-Alive` header to indicate the http server (Sanic) to not close the TCP connection after it has send the response. This allows the client to reuse the existing TCP connection to send subsequent HTTP requests, and ensures more efficient network traffic for both the client and the server. + +The `KEEP_ALIVE` config variable is set to `True` in Sanic by default. If you don't need this feature in your application, set it to `False` to cause all client connections to close immediately after a response is sent, regardless of the `Keep-Alive` header on the request. + +The amount of time the server holds the TCP connection open is decided by the server itself. In Sanic, that value is configured using the `KEEP_ALIVE_TIMEOUT` value. By default, **it is set to 120 seconds**. This means that if the client sends a `Keep-Alive` header, the server will hold the TCP connection open for 120 seconds after sending the response, and the client can reuse the connection to send another HTTP request within that time. + +For reference: + +- Apache httpd server default keepalive timeout = 5 seconds +- Nginx server default keepalive timeout = 75 seconds +- Nginx performance tuning guidelines uses keepalive = 15 seconds +- Caddy server default keepalive timeout = 120 seconds +- IE (5-9) client hard keepalive limit = 60 seconds +- Firefox client hard keepalive limit = 115 seconds +- Opera 11 client hard keepalive limit = 120 seconds +- Chrome 13+ client keepalive limit > 300+ seconds + +## Proxy configuration + +See [proxy configuration section](/guide/advanced/proxy-headers.md) diff --git a/guide/content/ko/guide/running/development.md b/guide/content/ko/guide/running/development.md new file mode 100644 index 0000000000..2b1753fb99 --- /dev/null +++ b/guide/content/ko/guide/running/development.md @@ -0,0 +1,308 @@ +# Development + +The first thing that should be mentioned is that the webserver that is integrated into Sanic is **not** just a development server. + +It is production ready out-of-the-box, _unless you enable in debug mode_. + +## Debug mode + +By setting the debug mode, Sanic will be more verbose in its output and will disable several run-time optimizations. + +```python +# server.py +from sanic import Sanic +from sanic.response import json + +app = Sanic(__name__) + +@app.route("/") +async def hello_world(request): + return json({"hello": "world"}) +``` + +```sh +sanic server:app --host=0.0.0.0 --port=1234 --debug +``` + +.. danger:: + +``` +Sanic's debug mode will slow down the server's performance, and is **NOT** intended for production environments. + +**DO NOT** enable debug mode in production. +``` + +## Automatic Reloader + +.. column:: + +``` +Sanic offers a way to enable or disable the Automatic Reloader. The easiest way to enable it is using the CLI's `--reload` argument to activate the Automatic Reloader. Every time a Python file is changed, the reloader will restart your application automatically. This is very convenient while developing. + +.. note:: + + The reloader is only available when using Sanic's [worker manager](./manager.md). If you have disabled it using `--single-process` then the reloader will not be available to you. +``` + +.. column:: + +```` +```sh +sanic path.to:app --reload +``` +You can also use the shorthand property +```sh +sanic path.to:app -r +``` +```` + +.. column:: + +``` +If you have additional directories that you would like to automatically reload on file save (for example, a directory of HTML templates), you can add that using `--reload-dir`. +``` + +.. column:: + +```` +```sh +sanic path.to:app --reload --reload-dir=/path/to/templates +``` +Or multiple directories, shown here using the shorthand properties +```sh +sanic path.to:app -r -R /path/to/one -R /path/to/two +``` +```` + +## Development REPL + +The Sanic CLI comes with a REPL (aka "read-eval-print loop") that can be used to interact with your application. This is useful for debugging and testing. A REPL is the interactive shell that you get when you run `python` without any arguments. + +.. column:: + +``` +You can start the REPL by passing the `--repl` argument to the Sanic CLI. +``` + +.. column:: + +```` +```sh +sanic path.to.server:app --repl +``` +```` + +.. column:: + +``` +Or, perhaps more conveniently, when you run `--dev`, Sanic will automatically start the REPL for you. However, in this case you might be prompted to hit the "ENTER" key before actually starting the REPL. +``` + +.. column:: + +```` +```sh +sanic path.to.server:app --dev +``` +```` + +![](/assets/images/repl.png) + +As seen in the screenshot above, the REPL will automatically add a few variables to the global namespace. These are: + +- `app` - The Sanic application instance. This is the same instance that is passed to the `sanic` CLI. +- `sanic` - The `sanic` module. This is the same module that is imported when you run `import sanic`. +- `do` - A function that will create a mock `Request` object and pass it to your application. This is useful for testing your application from the REPL. +- `client` - An instance of `httpx.Client` that is configured to make requests to your application. This is useful for testing your application from the REPL. **Note:** This is only available if `httpx` is installed in your environment. + +### Async/Await support + +.. column:: + +``` +The REPL supports `async`/`await` syntax. This means that you can use `await` in the REPL to wait for asynchronous operations to complete. This is useful for testing asynchronous code. +``` + +.. column:: + +```` +```python +>>> await app.ctx.db.fetchval("SELECT 1") +1 +``` +```` + +### The `app` variable + +You need to keep in mind that the `app` variable is your app instance as it existed when the REPL was started. It is the instance that is loaded when running the CLI command. This means that any changes that are made to your source code and subsequently reloaded in the workers will not be reflected in the `app` variable. If you want to interact with the reloaded app instance, you will need to restart the REPL. + +However, it is also very useful to have access to the original app instance in the REPL for adhoc testing and debugging. + +### The `client` variable + +When [httpx](https://www.python-httpx.org/) is installed in your environment, the `client` variable will be available in the REPL. This is an instance of `httpx.Client` that is configured to make requests to your running application. + +.. column:: + +``` +To use it, simply call one of the HTTP methods on the client. See the [httpx documentation](https://www.python-httpx.org/api/#client) for more information. +``` + +.. column:: + +```` +```python +>>> client.get("/") + +``` +```` + +### The `do` function + +As discussed above, the `app` instance exists as it did at the time the REPL was started, and as was modified inside the REPL. Any changes to the instance that cause a server to be reloaded will not be reflected in the `app` variable. This is where the `do` function comes in. + +Let's say that you have modified your application inside the REPL to add a new route: + +```python +>>> @app.get("/new-route") +... async def new_route(request): +... return sanic.json({"hello": "world"}) +... +>>> +``` + +You can use the `do` function to mock out a request, and pass it to the application as if it were a real HTTP request. This will allow you to test your new route without having to restart the REPL. + +```python +>>> await do("/new-route") +Result(request=, response=) +``` + +The `do` function returns a `Result` object that contains the `Request` and `Response` objects that were returned by your application. It is a `NamedTuple`, so you can access the values by name: + +```python +>>> result = await do("/new-route") +>>> result.request + +>>> result.response + +``` + +Or, by destructuring the tuple: + +```python +>>> request, response = await do("/new-route") +>>> request + +>>> response + +``` + +### When to use `do` vs `client`? + +.. column:: + +``` +**Use `do` when ...** + +- You want to test a route that does not exist in the running application +- You want to test a route that has been modified in the REPL +- You make a change to your application inside the REPL +``` + +.. column:: + +``` +**Use `client` when ...** + +- You want to test a route that already exists in the running application +- You want to test a route that has been modified in your source code +- You want to send an actual HTTP request to your application +``` + +_Added in v23.12_ + +## Complete development mode + +.. column:: + +``` +If you would like to be in debug mode **and** have the Automatic Reloader running, you can pass `dev=True`. This is equivalent to **debug + auto reload + REPL**. + +*Added in v22.3* +``` + +.. column:: + +```` +```sh +sanic path.to:app --dev +``` +You can also use the shorthand property +```sh +sanic path.to:app -d +``` +```` + +Added to the `--dev` flag in v23.12 is the ability to start a REPL. See the [Development REPL](./development.md#development-repl) section for more information. + +As of v23.12, the `--dev` flag is roughly equivalent to `--debug --reload --repl`. Using `--dev` will require you to expressly begin the REPL by hitting "ENTER", while passing the `--repl` flag explicitly starts it. +Before v23.12, the `--dev` flag is more similar to `--debug --reload`. + +.. column:: + +``` +If you would like to disable the REPL while using the `--dev` flag, you can pass `--no-repl`. +``` + +.. column:: + +```` +```sh +sanic path.to:app --dev --no-repl +``` +```` + +## Automatic TLS certificate + +When running in `DEBUG` mode, you can ask Sanic to handle setting up localhost temporary TLS certificates. This is helpful if you want to access your local development environment with `https://`. + +This functionality is provided by either [mkcert](https://github.com/FiloSottile/mkcert) or [trustme](https://github.com/python-trio/trustme). Both are good choices, but there are some differences. `trustme` is a Python library and can be installed into your environment with `pip`. This makes for easy envrionment handling, but it is not compatible when running a HTTP/3 server. `mkcert` might be a more involved installation process, but can install a local CA and make it easier to use. + +.. column:: + +``` +You can choose which platform to use by setting `config.LOCAL_CERT_CREATOR`. When set to `"auto"`, it will select either option, preferring `mkcert` if possible. +``` + +.. column:: + +```` +```python +app.config.LOCAL_CERT_CREATOR = "auto" +app.config.LOCAL_CERT_CREATOR = "mkcert" +app.config.LOCAL_CERT_CREATOR = "trustme" +``` +```` + +.. column:: + +``` +Automatic TLS can be enabled at Sanic server run time: +``` + +.. column:: + +```` +```sh +sanic path.to.server:app --auto-tls --debug +``` +```` + +.. warning:: + +``` +Localhost TLS certificates (like those generated by both `mkcert` and `trustme`) are **NOT** suitable for production environments. If you are not familiar with how to obtain a *real* TLS certificate, checkout the [How to...](../how-to/tls.md) section. +``` + +_Added in v22.6_ diff --git a/guide/content/ko/guide/running/inspector.md b/guide/content/ko/guide/running/inspector.md new file mode 100644 index 0000000000..2c6bb1d1a8 --- /dev/null +++ b/guide/content/ko/guide/running/inspector.md @@ -0,0 +1,245 @@ +# Inspector + +The Sanic Inspector is a feature of Sanic Server. It is _only_ available when running Sanic with the built-in [worker manager](./manager.md). + +It is an HTTP application that _optionally_ runs in the background of your application to allow you to interact with the running instance of your application. + +.. tip:: INFO + +``` +The Inspector was introduced in limited capacity in v22.9, but the documentation on this page assumes you are using v22.12 or higher. +``` + +## Getting Started + +The inspector is disabled by default. To enable it, you have two options. + +.. column:: + +``` +Set a flag when creating your application instance. +``` + +.. column:: + +```` +```python +app = Sanic("TestApp", inspector=True) +``` +```` + +.. column:: + +``` +Or, set a configuration value. +``` + +.. column:: + +```` +```python +app = Sanic("TestApp") +app.config.INSPECTOR = True +``` +```` + +.. warning:: + +``` +If you are using the configuration value, it *must* be done early and before the main worker process starts. This means that it should either be an environment variable, or it should be set shortly after creating the application instance as shown above. +``` + +## Using the Inspector + +Once the inspector is running, you will have access to it via the CLI or by directly accessing its web API via HTTP. + +.. column:: + +```` +**Via CLI** +```sh +sanic inspect +``` +```` + +.. column:: + +```` +**Via HTTP** +```sh +curl http://localhost:6457 +``` +```` + +.. note:: + +``` +Remember, the Inspector is not running on your Sanic application. It is a seperate process, with a seperate application, and exposed on a seperate socket. +``` + +## Built-in Commands + +The Inspector comes with the following built-in commands. + +| CLI Command | HTTP Action | Description | +| ------------------ | ---------------------------------- | -------------------------------------------------------------------------------------------------------- | +| `inspect` | `GET /` | Display basic details about the running application. | +| `inspect reload` | `POST /reload` | Trigger a reload of all server workers. | +| `inspect shutdown` | `POST /shutdown` | Trigger a shutdown of all processes. | +| `inspect scale N` | `POST /scale`
`{"replicas": N}` | Scale the number of workers. Where `N` is the target number of replicas. | + +## Custom Commands + +The Inspector is easily extendable to add custom commands (and endpoints). + +.. column:: + +``` +Subclass the `Inspector` class and create arbitrary methods. As long as the method name is not preceded by an underscore (`_`), then the name of the method will be a new subcommand on the inspector. +``` + +.. column:: + +```` +```python +from sanic import json +from sanic.worker.inspector import Inspector + +class MyInspector(Inspector): + async def something(self, *args, **kwargs): + print(args) + print(kwargs) + +app = Sanic("TestApp", inspector_class=MyInspector, inspector=True) +``` +```` + +This will expose custom methods in the general pattern: + +- CLI: `sanic inspect ` +- HTTP: `POST /` + +It is important to note that the arguments that the new method accepts are derived from how you intend to use the command. For example, the above `something` method accepts all positional and keyword based parameters. + +.. column:: + +``` +In the CLI, the positional and keyword parameters are passed as either positional or keyword arguments to your method. All values will be a `str` with the following exceptions: + +- A keyword parameter with no assigned value will be: `True` +- Unless the parameter is prefixed with `no-`, then it will be: `False` +``` + +.. column:: + +```` +```sh +sanic inspect something one two three --four --no-five --six=6 +``` +In your application log console, you will see: +``` +('one', 'two', 'three') +{'four': True, 'five': False, 'six': '6'} +``` +```` + +.. column:: + +``` +The same can be achieved by hitting the API directly. You can pass arguments to the method by exposing them in a JSON payload. The only thing to note is that the positional arguments should be exposed as `{"args": [...]}`. +``` + +.. column:: + +```` +```sh +curl http://localhost:6457/something \ + --json '{"args":["one", "two", "three"], "four":true, "five":false, "six":6}' +``` +In your application log console, you will see: +``` +('one', 'two', 'three') +{'four': True, 'five': False, 'six': 6} +``` +```` + +## Using in production + +.. danger:: + +``` +Before exposing the Inspector on a product, please consider all of the options in this section carefully. +``` + +When running Inspector on a remote production instance, you can protect the endpoints by requiring TLS encryption, and requiring API key authentication. + +### TLS encryption + +.. column:: + +``` +To the Inspector HTTP instance over TLS, pass the paths to your certificate and key. +``` + +.. column:: + +```` +```python +app.config.INSPECTOR_TLS_CERT = "/path/to/cert.pem" +app.config.INSPECTOR_TLS_KEY = "/path/to/key.pem" +``` +```` + +.. column:: + +``` +This will require use of the `--secure` flag, or `https://`. +``` + +.. column:: + +```` +```sh +sanic inspect --secure --host= +``` +```sh +curl https://:6457 +``` +```` + +### API Key Authentication + +.. column:: + +``` +You can secure the API with bearer token authentication. +``` + +.. column:: + +```` +```python +app.config.INSPECTOR_API_KEY = "Super-Secret-200" +``` +```` + +.. column:: + +``` +This will require the `--api-key` parameter, or bearer token authorization header. +``` + +.. column:: + +```` +```sh +sanic inspect --api-key=Super-Secret-200 +``` +```sh +curl http://localhost:6457 -H "Authorization: Bearer Super-Secret-200" +``` +```` + +## Configuration + +See [configuration](./configuration.md) diff --git a/guide/content/ko/guide/running/manager.md b/guide/content/ko/guide/running/manager.md new file mode 100644 index 0000000000..7114db3450 --- /dev/null +++ b/guide/content/ko/guide/running/manager.md @@ -0,0 +1,657 @@ +--- +title: Worker Manager +--- + +# Worker Manager + +The worker manager and its functionality was introduced in version 22.9. + +_The details of this section are intended for more advanced usages and **not** necessary to get started._ + +The purpose of the manager is to create consistency and flexibility between development and production environments. Whether you intend to run a single worker, or multiple workers, whether with, or without auto-reload: the experience will be the same. + +In general it looks like this: + +![](https://user-images.githubusercontent.com/166269/178677618-3b4089c3-6c6a-4ecc-8d7a-7eba2a7f29b0.png) + +When you run Sanic, the main process instantiates a `WorkerManager`. That manager is in charge of running one or more `WorkerProcess`. There generally are two kinds of processes: + +- server processes, and +- non-server processes. + +For the sake of ease, the User Guide generally will use the term "worker" or "worker process" to mean a server process, and "Manager" to mean the single worker manager running in your main process. + +## How Sanic Server starts processes + +Sanic will start processes using the [spawn](https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods) start method. This means that for every process/worker, the global scope of your application will be run on its own thread. The practical impact of this that _if_ you do not run Sanic with the CLI, you will need to nest the execution code inside a block to make sure it only runs on `__main__`. + +```python +if __name__ == "__main__": + app.run() +``` + +If you do not, you are likely to see an error message like this: + +``` +sanic.exceptions.ServerError: Sanic server could not start: [Errno 98] Address already in use. + +This may have happened if you are running Sanic in the global scope and not inside of a `if __name__ == "__main__"` block. + +See more information: https://sanic.dev/en/guide/deployment/manager.html#how-sanic-server-starts-processes +``` + +The likely fix for this problem is nesting your Sanic run call inside of the `__name__ == "__main__"` block. If you continue to receive this message after nesting, or if you see this while using the CLI, then it means the port you are trying to use is not available on your machine and you must select another port. + +### Starting a worker + +All worker processes _must_ send an acknowledgement when starting. This happens under the hood, and you as a developer do not need to do anything. However, the Manager will exit with a status code `1` if one or more workers do not send that `ack` message, or a worker process throws an exception while trying to start. If no exceptions are encountered, the Manager will wait for up to thirty (30) seconds for the acknowledgement. + +.. column:: + +``` +In the situation when you know that you will need more time to start, you can monkeypatch the Manager. The threshold does not include anything inside of a listener, and is limited to the execution time of everything in the global scope of your application. + +If you run into this issue, it may indicate a need to look deeper into what is causing the slow startup. +``` + +.. column:: + +```` +```python +from sanic.worker.manager import WorkerManager + +WorkerManager.THRESHOLD = 100 # Value is in 0.1s +``` +```` + +See [worker ack](#worker-ack) for more information. + +.. column:: + +``` +As stated above, Sanic will use [spawn](https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods) to start worker processes. If you would like to change this behavior and are aware of the implications of using different start methods, you can modify as shown here. +``` + +.. column:: + +```` +```python +from sanic import Sanic + +Sanic.start_method = "fork" +``` +```` + +### Worker ack + +When all of your workers are running in a subprocess a potential problem is created: deadlock. This can occur when the child processes cease to function, but the main process is unaware that this happened. Therefore, Sanic servers will automatically send an `ack` message (short for acknowledge) to the main process after startup. + +In version 22.9, the `ack` timeout was short and limited to `5s`. In version 22.12, the timeout was lengthened to `30s`. If your application is shutting down after thirty seconds then it might be necessary to manually increase this threshhold. + +.. column:: + +``` +The value of `WorkerManager.THRESHOLD` is in `0.1s` increments. Therefore, to set it to one minute, you should set the value to `600`. + +This value should be set as early as possible in your application, and should ideally happen in the global scope. Setting it after the main process has started will not work. +``` + +.. column:: + +```` +```python +from sanic.worker.manager import WorkerManager + +WorkerManager.THRESHOLD = 600 +``` +```` + +### Zero downtime restarts + +By default, when restarting workers, Sanic will teardown the existing process first before starting a new one. + +If you are intending to use the restart functionality in production then you may be interested in having zero-downtime reloading. This can be accomplished by forcing the reloader to change the order to start a new process, wait for it to [ack](#worker-ack), and then teardown the old process. + +.. column:: + +``` +From the multiplexer, use the `zero_downtime` argument +``` + +.. column:: + +```` +```python +app.m.restart(zero_downtime=True) +``` +```` + +_Added in v22.12_ + +## Using shared context between worker processes + +Python provides a few methods for [exchanging objects](https://docs.python.org/3/library/multiprocessing.html#exchanging-objects-between-processes), [synchronizing](https://docs.python.org/3/library/multiprocessing.html#synchronization-between-processes), and [sharing state](https://docs.python.org/3/library/multiprocessing.html#sharing-state-between-processes) between processes. This usually involves objects from the `multiprocessing` and `ctypes` modules. + +If you are familiar with these objects and how to work with them, you will be happy to know that Sanic provides an API for sharing these objects between your worker processes. If you are not familiar, you are encouraged to read through the Python documentation linked above and try some of the examples before proceeding with implementing shared context. + +Similar to how [application context](../basics/app.md#application-context) allows an applicaiton to share state across the lifetime of the application with `app.ctx`, shared context provides the same for the special objects mentioned above. This context is available as `app.shared_ctx` and should **ONLY** be used to share objects intended for this purpose. + +The `shared_ctx` will: + +- _NOT_ share regular objects like `int`, `dict`, or `list` +- _NOT_ share state between Sanic instances running on different machines +- _NOT_ share state to non-worker processes +- **only** share state between server workers managed by the same Manager + +Attaching an inappropriate object to `shared_ctx` will likely result in a warning, and not an error. You should be careful to not accidentally add an unsafe object to `shared_ctx` as it may not work as expected. If you are directed here because of one of those warnings, you might have accidentally used an unsafe object in `shared_ctx`. + +.. column:: + +``` +In order to create a shared object you **must** create it in the main process and attach it inside of the `main_process_start` listener. +``` + +.. column:: + +```` +```python +from multiprocessing import Queue + +@app.main_process_start +async def main_process_start(app): + app.shared_ctx.queue = Queue() +``` +```` + +Trying to attach to the `shared_ctx` object outside of this listener may result in a `RuntimeError`. + +.. column:: + +``` +After creating the objects in the `main_process_start` listener and attaching to the `shared_ctx`, they will be available in your workers wherever the application instance is available (example: listeners, middleware, request handlers). +``` + +.. column:: + +```` +```python +from multiprocessing import Queue + +@app.get("") +async def handler(request): + request.app.shared_ctx.queue.put(1) + ... +``` +```` + +## Access to the multiplexer + +The application instance has access to an object that provides access to interacting with the Manager and other worker processes. The object is attached as the `app.multiplexer` property, but it is more easily accessed by its alias: `app.m`. + +.. column:: + +``` +For example, you can get access to the current worker state. +``` + +.. column:: + +```` +```python +@app.on_request +async def print_state(request: Request): + print(request.app.m.name) + print(request.app.m.pid) + print(request.app.m.state) +``` +``` +Sanic-Server-0-0 +99999 +{'server': True, 'state': 'ACKED', 'pid': 99999, 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), 'starts': 2, 'restart_at': datetime.datetime(2022, 10, 1, 0, 0, 12, 861332, tzinfo=datetime.timezone.utc)} +``` +```` + +.. column:: + +``` +The `multiplexer` also has access to terminate the Manager, or restart worker processes +``` + +.. column:: + +```` +```python +# shutdown the entire application and all processes +app.m.name.terminate() + +# restart the current worker only +app.m.name.restart() + +# restart specific workers only (comma delimited) +app.m.name.restart("Sanic-Server-4-0,Sanic-Server-7-0") + +# restart ALL workers +app.m.name.restart(all_workers=True) # Available v22.12+ +``` +```` + +## Worker state + +.. column:: + +``` +As shown above, the `multiplexer` has access to report upon the state of the current running worker. However, it also contains the state for ALL processes running. +``` + +.. column:: + +```` +```python +@app.on_request +async def print_state(request: Request): + print(request.app.m.workers) +``` +``` +{ + 'Sanic-Main': {'pid': 99997}, + 'Sanic-Server-0-0': { + 'server': True, + 'state': 'ACKED', + 'pid': 9999, + 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), + 'starts': 2, + 'restart_at': datetime.datetime(2022, 10, 1, 0, 0, 12, 861332, tzinfo=datetime.timezone.utc) + }, + 'Sanic-Reloader-0': { + 'server': False, + 'state': 'STARTED', + 'pid': 99998, + 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), + 'starts': 1 + } +} +``` +```` + +The possible states are: + +- `NONE` - The worker has been created, but there is no process yet +- `IDLE` - The process has been created, but is not running yet +- `STARTING` - The process is starting +- `STARTED` - The process has started +- `ACKED` - The process has started and sent an acknowledgement (usually only for server processes) +- `JOINED` - The process has exited and joined the main process +- `TERMINATED` - The process has exited and terminated +- `RESTARTING` - The process is restarting +- `FAILED` - The process encountered an exception and is no longer running +- `COMPLETED` - The process has completed its work and exited successfully + +## Built-in non-server processes + +As mentioned, the Manager also has the ability to run non-server processes. Sanic comes with two built-in types of non-server processes, and allows for [creating custom processes](#running-custom-processes). + +The two built-in processes are + +- the [auto-reloader](./development.md#automatic-reloader), optionally enabled to watch the file system for changes and trigger a restart +- [inspector](#inspector), optionally enabled to provide external access to the state of the running instance + +## Inspector + +Sanic has the ability to expose the state and the functionality of the `multiplexer` to the CLI. Currently, this requires the CLI command to be run on the same machine as the running Sanic instance. By default the inspector is disabled. + +.. column:: + +``` +To enable it, set the config value to `True`. +``` + +.. column:: + +```` +```python +app.config.INSPECTOR = True +``` +```` + +You will now have access to execute any of these CLI commands: + +``` +sanic inspect reload Trigger a reload of the server workers +sanic inspect shutdown Shutdown the application and all processes +sanic inspect scale N Scale the number of workers to N +sanic inspect Run a custom command +``` + +![](https://user-images.githubusercontent.com/166269/190099384-2f2f3fae-22d5-4529-b279-8446f6b5f9bd.png) + +.. column:: + +``` +This works by exposing a small HTTP service on your machine. You can control the location using configuration values: +``` + +.. column:: + +```` +```python +app.config.INSPECTOR_HOST = "localhost" +app.config.INSPECTOR_PORT = 6457 +``` +```` + +[Learn more](./inspector.md) to find out what is possible with the Inspector. + +## Running custom processes + +To run a managed custom process on Sanic, you must create a callable. If that process is meant to be long-running, then it should handle a shutdown call by a `SIGINT` or `SIGTERM` signal. + +.. column:: + +``` +The simplest method for doing that in Python will be to just wrap your loop in `KeyboardInterrupt`. + +If you intend to run another application, like a bot, then it is likely that it already has capability to handle this signal and you likely do not need to do anything. +``` + +.. column:: + +```` +```python +from time import sleep + +def my_process(foo): + try: + while True: + sleep(1) + except KeyboardInterrupt: + print("done") +``` +```` + +.. column:: + +``` +That callable must be registered in the `main_process_ready` listener. It is important to note that is is **NOT** the same location that you should register [shared context](#using-shared-context-between-worker-processes) objects. +``` + +.. column:: + +```` +```python +@app.main_process_ready +async def ready(app: Sanic, _): +# app.manager.manage(, , ) + app.manager.manage("MyProcess", my_process, {"foo": "bar"}) +``` +```` + +### Transient v. durable processes + +.. column:: + +``` +When you manage a process with the `manage` method, you have the option to make it transient or durable. A transient process will be restarted by the auto-reloader, and a durable process will not. + +By default, all processes are durable. +``` + +.. column:: + +```` +```python +@app.main_process_ready +async def ready(app: Sanic, _): + app.manager.manage( + "MyProcess", + my_process, + {"foo": "bar"}, + transient=True, + ) +``` +```` + +### Tracked v. untracked processes + +Out of the box, Sanic will track the state of all processes. This means that you can access the state of the process from the [multiplexer](./manager#access-to-the-multiplexer) object, or from the [Inspector](./manager#inspector). + +See [worker state](./manager#worker-state) for more information. + +Sometimes it is helpful to run background processes that are not long-running. You run them once until completion and then they exit. Upon completion, they will either be in `FAILED` or `COMPLETED` state. + +.. column:: + +``` +When you are running a non-long-running process, you can opt out of tracking it by setting `tracked=False` in the `manage` method. This means that upon completion of the process it will be removed from the list of tracked processes. You will only be able to check the state of the process while it is running. +``` + +.. column:: + +```` +```python +@app.main_process_ready +async def ready(app: Sanic, _): + app.manager.manage( + "OneAndDone", + do_once, + {}, + tracked=False, + ) +``` +```` + +_Added in v23.12_ + +### Restartable custom processes + +A custom process that is transient will **always** be restartable. That means the auto-restart will work as expected. However, what if you want to be able to _manually_ restart a process, but not have it be restarted by the auto-reloader? + +.. column:: + +``` +In this scenario, you can set `restartable=True` in the `manage` method. This will allow you to manually restart the process, but it will not be restarted by the auto-reloader. +``` + +.. column:: + +```` +```python +@app.main_process_ready +async def ready(app: Sanic, _): + app.manager.manage( + "MyProcess", + my_process, + {"foo": "bar"}, + restartable=True, + ) +``` +```` + +.. column:: + +``` +You could now manually restart that process from the multiplexer. +``` + +.. column:: + +```` +```python +@app.get("/restart") +async def restart_handler(request: Request): + request.app.m.restart("Sanic-MyProcess-0") + return json({"foo": request.app.m.name}) +``` +```` + +_Added in v23.12_ + +### On the fly process management + +Custom processes are usually added in the `main_process_ready` listener. However, there may be times when you want to add a process after the application has started. For example, you may want to add a process from a request handler. The multiplexer provides a method for doing this. + +.. column:: + +``` +Once you have a reference to the multiplexer, you can call `manage` to add a process. It works the same as the `manage` method on the Manager. +``` + +.. column:: + +```` +```python +@app.post("/start") +async def start_handler(request: Request): + request.app.m.manage( + "MyProcess", + my_process, + {"foo": "bar"}, + workers=2, + ) + return json({"foo": request.app.m.name}) +``` +```` + +_Added in v23.12_ + +## Single process mode + +.. column:: + +``` +If you would like to opt out of running multiple processes, you can run Sanic in a single process only. In this case, the Manager will not run. You will also not have access to any features that require processes (auto-reload, the inspector, etc). +``` + +.. column:: + +```` +```sh +sanic path.to.server:app --single-process +``` +```python +if __name__ == "__main__": + app.run(single_process=True) +``` +```python +if __name__ == "__main__": + app.prepare(single_process=True) + Sanic.serve_single() +``` +```` + +## Sanic and multiprocessing + +Sanic makes heavy use of the [`multiprocessing` module](https://docs.python.org/3/library/multiprocessing.html) to manage the worker processes. You should generally avoid lower level usage of this module (like setting the start method) as it may interfere with the functionality of Sanic. + +### Start methods in Python + +Before explaining what Sanic tries to do, it is important to understand what the `start_method` is and why it is important. Python generally allows for three different methods of starting a process: + +- `fork` +- `spawn` +- `forkserver` + +The `fork` and `forkserver` methods are only available on Unix systems, and `spawn` is the only method available on Windows. On Unix systems where you have a choice, `fork` is generally the default system method. + +You are encouraged to read the [Python documentation](https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods) to learn more about the differences between these methods. However, the important thing to know is that `fork` basically copies the entire memory of the parent process into the child process, whereas `spawn` will create a new process and then load the application into that process. This is the reason why you need to nest your Sanic `run` call inside of the `__name__ == "__main__"` block if you are not using the CLI. + +### Sanic and start methods + +By default, Sanic will try and use `spawn` as the start method. This is because it is the only method available on Windows, and it is the safest method on Unix systems. + +.. column:: + +``` +However, if you are running Sanic on a Unix system and you would like to use `fork` instead, you can do so by setting the `start_method` on the `Sanic` class. You will want to do this as early as possible in your application, and ideally in the global scope before you import any other modules. +``` + +.. column:: + +```` +```python +from sanic import Sanic + +Sanic.start_method = "fork" +``` +```` + +### Overcoming a `RuntimeError` + +You might have received a `RuntimeError` that looks like this: + +``` +RuntimeError: Start method 'spawn' was requested, but 'fork' was already set. +``` + +If so, that means somewhere in your application you are trying to set the start method that conflicts with what Sanic is trying to do. You have a few options to resolve this: + +.. column:: + +``` +**OPTION 1:** You can tell Sanic that the start method has been set and to not try and set it again. +``` + +.. column:: + +```` +```python +from sanic import Sanic + +Sanic.START_METHOD_SET = True +``` +```` + +.. column:: + +``` +**OPTION 2:** You could tell Sanic that you intend to use `fork` and to not try and set it to `spawn`. +``` + +.. column:: + +```` +```python +from sanic import Sanic + +Sanic.start_method = "fork" +``` +```` + +.. column:: + +``` +**OPTION 3:** You can tell Python to use `spawn` instead of `fork` by setting the `multiprocessing` start method. +``` + +.. column:: + +```` +```python +import multiprocessing + +multiprocessing.set_start_method("spawn") +``` +```` + +In any of these options, you should run this code as early as possible in your application. Depending upon exactly what your specific scenario is, you may need to combine some of the options. + +.. note:: + +```` +The potential issues that arise from this problem are usually easily solved by just allowing Sanic to be in charge of multiprocessing. This usually means making use of the `main_process_start` and `main_process_ready` listeners to deal with multiprocessing issues. For example, you should move instantiating multiprocessing primitives that do a lot of work under the hood from the global scope and into a listener. + +```python +# This is BAD; avoid the global scope +from multiprocessing import Queue + +q = Queue() +``` + +```python +# This is GOOD; the queue is made in a listener and shared to all the processes on the shared_ctx +from multiprocessing import Queue + +@app.main_process_start +async def main_process_start(app): + app.shared_ctx.q = Queue() +``` +```` diff --git a/guide/content/ko/guide/running/running.md b/guide/content/ko/guide/running/running.md new file mode 100644 index 0000000000..8bf91256a0 --- /dev/null +++ b/guide/content/ko/guide/running/running.md @@ -0,0 +1,585 @@ +--- +title: Running Sanic +--- + +# Running Sanic + +Sanic ships with its own internal web server. Under most circumstances, this is the preferred method for deployment. In addition, you can also deploy Sanic as an ASGI app bundled with an ASGI-able web server. + +## Sanic Server + +The main way to run Sanic is to use the included [CLI](#sanic-cli). + +```sh +sanic path.to.server:app +``` + +In this example, Sanic is instructed to look for a python module called `path.to.server`. Inside of that module, it will look for a global variable called `app`, which should be an instance of `Sanic(...)`. + +```python +# ./path/to/server.py +from sanic import Sanic, Request, json + +app = Sanic("TestApp") + +@app.get("/") +async def handler(request: Request): + return json({"foo": "bar"}) +``` + +You may also dropdown to the [lower level API](#low-level-apprun) to call `app.run` as a script. However, if you choose this option you should be more comfortable handling issues that may arise with `multiprocessing`. + +### Workers + +.. column:: + +``` +By default, Sanic runs a main process and a single worker process (see [worker manager](./manager.md) for more details). + +To crank up the juice, just specify the number of workers in the run arguments. +``` + +.. column:: + +```` +```sh +sanic server:app --host=0.0.0.0 --port=1337 --workers=4 +``` +```` + +Sanic will automatically spin up multiple processes and route traffic between them. We recommend as many workers as you have available processors. + +.. column:: + +``` +The easiest way to get the maximum CPU performance is to use the `--fast` option. This will automatically run the maximum number of workers given the system constraints. + +*Added in v21.12* +``` + +.. column:: + +```` +```sh +sanic server:app --host=0.0.0.0 --port=1337 --fast +``` +```` + +In version 22.9, Sanic introduced a new worker manager to provide more consistency and flexibility between development and production servers. Read [about the manager](./manager.md) for more details about workers. + +.. column:: + +``` +If you only want to run Sanic with a single process, specify `single_process` in the run arguments. This means that auto-reload, and the worker manager will be unavailable. + +*Added in v22.9* +``` + +.. column:: + +```` +```sh +sanic server:app --host=0.0.0.0 --port=1337 --single-process +``` +```` + +### Running via command + +#### Sanic CLI + +Use `sanic --help` to see all the options. + +.. attrs:: +:title: Sanic CLI help output +:class: details + +```` +```text +$ sanic --help + + ▄███ █████ ██ ▄█▄ ██ █ █ ▄██████████ + ██ █ █ █ ██ █ █ ██ + ▀███████ ███▄ ▀ █ █ ██ ▄ █ ██ + ██ █████████ █ ██ █ █ ▄▄ + ████ ████████▀ █ █ █ ██ █ ▀██ ███████ + + To start running a Sanic application, provide a path to the module, where + app is a Sanic() instance: + + $ sanic path.to.server:app + + Or, a path to a callable that returns a Sanic() instance: + + $ sanic path.to.factory:create_app --factory + + Or, a path to a directory to run as a simple HTTP server: + + $ sanic ./path/to/static --simple + +Required +======== + Positional: + module Path to your Sanic app. Example: path.to.server:app + If running a Simple Server, path to directory to serve. Example: ./ + +Optional +======== + General: + -h, --help show this help message and exit + --version show program's version number and exit + + Application: + --factory Treat app as an application factory, i.e. a () -> callable + -s, --simple Run Sanic as a Simple Server, and serve the contents of a directory + (module arg should be a path) + --inspect Inspect the state of a running instance, human readable + --inspect-raw Inspect the state of a running instance, JSON output + --trigger-reload Trigger worker processes to reload + --trigger-shutdown Trigger all processes to shutdown + + HTTP version: + --http {1,3} Which HTTP version to use: HTTP/1.1 or HTTP/3. Value should + be either 1, or 3. [default 1] + -1 Run Sanic server using HTTP/1.1 + -3 Run Sanic server using HTTP/3 + + Socket binding: + -H HOST, --host HOST + Host address [default 127.0.0.1] + -p PORT, --port PORT + Port to serve on [default 8000] + -u UNIX, --unix UNIX + location of unix socket + + TLS certificate: + --cert CERT Location of fullchain.pem, bundle.crt or equivalent + --key KEY Location of privkey.pem or equivalent .key file + --tls DIR TLS certificate folder with fullchain.pem and privkey.pem + May be specified multiple times to choose multiple certificates + --tls-strict-host Only allow clients that send an SNI matching server certs + + Worker: + -w WORKERS, --workers WORKERS + Number of worker processes [default 1] + --fast Set the number of workers to max allowed + --single-process Do not use multiprocessing, run server in a single process + --legacy Use the legacy server manager + --access-logs Display access logs + --no-access-logs No display access logs + + Development: + --debug Run the server in debug mode + -r, --reload, --auto-reload + Watch source directory for file changes and reload on changes + -R PATH, --reload-dir PATH + Extra directories to watch and reload on changes + -d, --dev debug + auto reload + --auto-tls Create a temporary TLS certificate for local development (requires mkcert or trustme) + + Output: + --coffee Uhm, coffee? + --no-coffee No uhm, coffee? + --motd Show the startup display + --no-motd No show the startup display + -v, --verbosity Control logging noise, eg. -vv or --verbosity=2 [default 0] + --noisy-exceptions Output stack traces for all exceptions + --no-noisy-exceptions + No output stack traces for all exceptions + +``` +```` + +#### As a module + +.. column:: + +``` +Sanic applications can also be called directly as a module. +``` + +.. column:: + +```` +```bash +python -m sanic server.app --host=0.0.0.0 --port=1337 --workers=4 +``` +```` + +#### Using a factory + +A very common solution is to develop your application _not_ as a global variable, but instead using the factory pattern. In this context, "factory" means a function that returns an instance of `Sanic(...)`. + +.. column:: + +``` +Suppose that you have this in your `server.py` +``` + +.. column:: + +```` +```python +from sanic import Sanic + +def create_app() -> Sanic: + app = Sanic("MyApp") + + return app +``` +```` + +.. column:: + +``` +You can run this application now by referencing it in the CLI explicitly as a factory: +``` + +.. column:: + +```` +```sh +sanic server:create_app --factory +``` +Or, explicitly like this: +```sh +sanic "server:create_app()" +``` +Or, implicitly like this: +```sh +sanic server:create_app +``` + +*Implicit command added in v23.3* +```` + +### Low level `app.run` + +When using `app.run` you will just call your Python file like any other script. + +.. column:: + +``` +`app.run` must be properly nested inside of a name-main block. +``` + +.. column:: + +```` +```python +# server.py +app = Sanic("MyApp") + +if __name__ == "__main__": + app.run() +``` +```` + +.. danger:: + +```` +Be *careful* when using this pattern. A very common mistake is to put too much logic inside of the `if __name__ == "__main__":` block. + +🚫 This is a mistake + +```python +from sanic import Sanic +from my.other.module import bp + +app = Sanic("MyApp") + +if __name__ == "__main__": + app.blueprint(bp) + app.run() +``` + +If you do this, your [blueprint](../best-practices/blueprints.md) will not be attached to your application. This is because the `__main__` block will only run on Sanic's main worker process, **NOT** any of its [worker processes](../deployment/manager.md). This goes for anything else that might impact your application (like attaching listeners, signals, middleware, etc). The only safe operations are anything that is meant for the main process, like the `app.main_*` listeners. + +Perhaps something like this is more appropriate: + +```python +from sanic import Sanic +from my.other.module import bp + +app = Sanic("MyApp") + +if __name__ == "__mp_main__": + app.blueprint(bp) +elif __name__ == "__main__": + app.run() +``` +```` + +To use the low-level `run` API, after defining an instance of `sanic.Sanic`, we can call the run method with the following keyword arguments: + +| Parameter | Default | Description | +| :---------------------------------------: | :--------------: | :------------------------------------------------------------------------------------------------------------------------ | +| **host** | `"127.0.0.1"` | Address to host the server on. | +| **port** | `8000` | Port to host the server on. | +| **unix** | `None` | Unix socket name to host the server on (instead of TCP). | +| **dev** | `False` | Equivalent to `debug=True` and `auto_reload=True`. | +| **debug** | `False` | Enables debug output (slows server). | +| **ssl** | `None` | SSLContext for SSL encryption of worker(s). | +| **sock** | `None` | Socket for the server to accept connections from. | +| **workers** | `1` | Number of worker processes to spawn. Cannot be used with fast. | +| **loop** | `None` | An asyncio-compatible event loop. If none is specified, Sanic creates its own event loop. | +| **protocol** | `HttpProtocol` | Subclass of asyncio.protocol. | +| **version** | `HTTP.VERSION_1` | The HTTP version to use (`HTTP.VERSION_1` or `HTTP.VERSION_3`). | +| **access_log** | `True` | Enables log on handling requests (significantly slows server). | +| **auto_reload** | `None` | Enables auto-reload on the source directory. | +| **reload_dir** | `None` | A path or list of paths to directories the auto-reloader should watch. | +| **noisy_exceptions** | `None` | Whether to set noisy exceptions globally. None means leave as default. | +| **motd** | `True` | Whether to display the startup message. | +| **motd_display** | `None` | A dict with extra key/value information to display in the startup message | +| **fast** | `False` | Whether to maximize worker processes. Cannot be used with workers. | +| **verbosity** | `0` | Level of logging detail. Max is 2. | +| **auto_tls** | `False` | Whether to auto-create a TLS certificate for local development. Not for production. | +| **single_process** | `False` | Whether to run Sanic in a single process. | + +.. column:: + +``` +For example, we can turn off the access log in order to increase performance, and bind to a custom host and port. +``` + +.. column:: + +```` +```python +# server.py +app = Sanic("MyApp") + +if __name__ == "__main__": + app.run(host='0.0.0.0', port=1337, access_log=False) +``` +```` + +.. column:: + +``` +Now, just execute the python script that has `app.run(...)` +``` + +.. column:: + +```` +```sh +python server.py +``` +```` + +For a slightly more advanced implementation, it is good to know that `app.run` will call `app.prepare` and `Sanic.serve` under the hood. + +.. column:: + +``` +Therefore, these are equivalent: +``` + +.. column:: + +```` +```python +if __name__ == "__main__": + app.run(host='0.0.0.0', port=1337, access_log=False) +``` +```python +if __name__ == "__main__": + app.prepare(host='0.0.0.0', port=1337, access_log=False) + Sanic.serve() +``` +```` + +.. column:: + +``` +This can be useful if you need to bind your appliction(s) to multiple ports. +``` + +.. column:: + +```` +```python +if __name__ == "__main__": + app1.prepare(host='0.0.0.0', port=9990) + app1.prepare(host='0.0.0.0', port=9991) + app2.prepare(host='0.0.0.0', port=5555) + Sanic.serve() +``` +```` + +### Sanic Simple Server + +.. column:: + +``` +Sometimes you just have a directory of static files that need to be served. This especially can be handy for quickly standing up a localhost server. Sanic ships with a Simple Server, where you only need to point it at a directory. +``` + +.. column:: + +```` +```sh +sanic ./path/to/dir --simple +``` +```` + +.. column:: + +``` +This could also be paired with auto-reloading. +``` + +.. column:: + +```` +```sh +sanic ./path/to/dir --simple --reload --reload-dir=./path/to/dir +``` +```` + +_Added in v21.6_ + +### HTTP/3 + +Sanic server offers HTTP/3 support using [aioquic](https://github.com/aiortc/aioquic). This **must** be installed to use HTTP/3: + +```sh +pip install sanic aioquic +``` + +```sh +pip install sanic[http3] +``` + +To start HTTP/3, you must explicitly request it when running your application. + +.. column:: + +```` +```sh +sanic path.to.server:app --http=3 +``` + +```sh +sanic path.to.server:app -3 +``` +```` + +.. column:: + +```` +```python +app.run(version=3) +``` +```` + +To run both an HTTP/3 and HTTP/1.1 server simultaneously, you can use [application multi-serve](../release-notes/v22.3.html#application-multi-serve) introduced in v22.3. This will automatically add an [Alt-Svc](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Alt-Svc) header to your HTTP/1.1 requests to let the client know that it is also available as HTTP/3. + +.. column:: + +```` +```sh +sanic path.to.server:app --http=3 --http=1 +``` + +```sh +sanic path.to.server:app -3 -1 +``` +```` + +.. column:: + +```` +```python +app.prepare(version=3) +app.prepare(version=1) +Sanic.serve() +``` +```` + +Because HTTP/3 requires TLS, you cannot start a HTTP/3 server without a TLS certificate. You should [set it up yourself](../how-to/tls.html) or use `mkcert` if in `DEBUG` mode. Currently, automatic TLS setup for HTTP/3 is not compatible with `trustme`. See [development](./development.md) for more details. + +_Added in v22.6_ + +## ASGI + +Sanic is also ASGI-compliant. This means you can use your preferred ASGI webserver to run Sanic. The three main implementations of ASGI are [Daphne](http://github.com/django/daphne), [Uvicorn](https://www.uvicorn.org/), and [Hypercorn](https://pgjones.gitlab.io/hypercorn/index.html). + +.. warning:: + +``` +Daphne does not support the ASGI `lifespan` protocol, and therefore cannot be used to run Sanic. See [Issue #264](https://github.com/django/daphne/issues/264) for more details. +``` + +Follow their documentation for the proper way to run them, but it should look something like: + +```sh +uvicorn myapp:app +``` + +```sh +hypercorn myapp:app +``` + +A couple things to note when using ASGI: + +1. When using the Sanic webserver, websockets will run using the `websockets` package. In ASGI mode, there is no need for this package since websockets are managed in the ASGI server. +2. The ASGI lifespan protocol , supports only two server events: startup and shutdown. Sanic has four: before startup, after startup, before shutdown, and after shutdown. Therefore, in ASGI mode, the startup and shutdown events will run consecutively and not actually around the server process beginning and ending (since that is now controlled by the ASGI server). Therefore, it is best to use `after_server_start` and `before_server_stop`. + +### Trio + +Sanic has experimental support for running on Trio with: + +```sh +hypercorn -k trio myapp:app +``` + +## Gunicorn + +[Gunicorn](http://gunicorn.org/) ("Green Unicorn") is a WSGI HTTP Server for UNIX based operating systems. It is a pre-fork worker model ported from Ruby’s Unicorn project. + +In order to run Sanic application with Gunicorn, you need to use it with the adapter from [uvicorn](https://www.uvicorn.org/). Make sure uvicorn is installed and run it with `uvicorn.workers.UvicornWorker` for Gunicorn worker-class argument: + +```sh +gunicorn myapp:app --bind 0.0.0.0:1337 --worker-class uvicorn.workers.UvicornWorker +``` + +See the [Gunicorn Docs](http://docs.gunicorn.org/en/latest/settings.html#max-requests) for more information. + +.. warning:: + +``` +It is generally advised to not use `gunicorn` unless you need it. The Sanic Server is primed for running Sanic in production. Weigh your considerations carefully before making this choice. Gunicorn does provide a lot of configuration options, but it is not the best choice for getting Sanic to run at its fastest. +``` + +## Performance considerations + +.. column:: + +``` +When running in production, make sure you turn off `debug`. +``` + +.. column:: + +```` +```sh +sanic path.to.server:app +``` +```` + +.. column:: + +``` +Sanic will also perform fastest if you turn off `access_log`. + +If you still require access logs, but want to enjoy this performance boost, consider using [Nginx as a proxy](./nginx.md), and letting that handle your access logging. It will be much faster than anything Python can handle. +``` + +.. column:: + +```` +```sh +sanic path.to.server:app --no-access-logs +``` +```` diff --git a/guide/content/ko/help.md b/guide/content/ko/help.md new file mode 100644 index 0000000000..91880e2861 --- /dev/null +++ b/guide/content/ko/help.md @@ -0,0 +1,32 @@ +--- +title: Need some help? +layout: main +--- + +# Need some help? + +As an active community of developers, we try to support each other. If you need some help, try one of the following: + +.. column:: + +``` +### Discord 💬 + +Best place to turn for quick answers and live chat + +`#sanic-support` channel on the [Discord server](https://discord.gg/FARQzAEMAA) +``` + +.. column:: + +``` +### Community Forums 👥 + +Good for sharing snippets of code and longer support queries + +`Questions and Help` category on the [Forums](https://community.sanicframework.org/c/questions-and-help/6) +``` + +--- + +We also actively monitor the `[sanic]` tag on [Stack Overflow](https://stackoverflow.com/questions/tagged/sanic). diff --git a/guide/content/ko/index.md b/guide/content/ko/index.md new file mode 100644 index 0000000000..3c09f07788 --- /dev/null +++ b/guide/content/ko/index.md @@ -0,0 +1,334 @@ +--- +title: The lightning-fast asynchronous Python web framework +layout: home +features: + - title: Simple and lightweight + details: Intuitive API with smart defaults and no bloat allows you to get straight to work building your app. + - title: Unopinionated and flexible + details: Build the way you want to build without letting your tooling constrain you. + - title: Performant and scalable + details: Built from the ground up with speed and scalability as a main concern. It is ready to power web applications big and small. + - title: Production ready + details: Out of the box, it comes bundled with a web server ready to power your web applications. + - title: Trusted by millions + details: Sanic is one of the overall most popular frameworks on PyPI, and the top async enabled framework + - title: Community driven + details: The project is maintained and run by the community for the community. +--- + +### ⚡ The lightning-fast asynchronous Python web framework + +.. attrs:: +:class: columns is-multiline mt-6 + +``` +.. attrs:: + :class: column is-4 + + #### Simple and lightweight + + Intuitive API with smart defaults and no bloat allows you to get straight to work building your app. + +.. attrs:: + :class: column is-4 + + #### Unopinionated and flexible + + Build the way you want to build without letting your tooling constrain you. + +.. attrs:: + :class: column is-4 + + #### Performant and scalable + + Built from the ground up with speed and scalability as a main concern. It is ready to power web applications big and small. + +.. attrs:: + :class: column is-4 + + #### Production ready + + Out of the box, it comes bundled with a web server ready to power your web applications. + +.. attrs:: + :class: column is-4 + + #### Trusted by millions + + Sanic is one of the overall most popular frameworks on PyPI, and the top async enabled framework + +.. attrs:: + :class: column is-4 + + #### Community driven + + The project is maintained and run by the community for the community. +``` + +.. attrs:: +:class: is-size-3 mt-6 + +``` +**With the features and tools you'd expect.** +``` + +.. attrs:: +:class: is-size-3 ml-6 + +``` +**And some {span:has-text-primary:you wouldn't believe}.** +``` + +.. tab:: Production-grade + +```` +After installing, Sanic has all the tools you need for a scalable, production-grade server—out of the box! + +Including [full TLS support](/en/guide/how-to/tls). + +```python +from sanic import Sanic +from sanic.response import text + +app = Sanic("MyHelloWorldApp") + +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` + +```sh +sanic path.to.server:app +[2023-01-31 12:34:56 +0000] [999996] [INFO] Sanic v22.12.0 +[2023-01-31 12:34:56 +0000] [999996] [INFO] Goin' Fast @ http://127.0.0.1:8000 +[2023-01-31 12:34:56 +0000] [999996] [INFO] mode: production, single worker +[2023-01-31 12:34:56 +0000] [999996] [INFO] server: sanic, HTTP/1.1 +[2023-01-31 12:34:56 +0000] [999996] [INFO] python: 3.10.9 +[2023-01-31 12:34:56 +0000] [999996] [INFO] platform: SomeOS-9.8.7 +[2023-01-31 12:34:56 +0000] [999996] [INFO] packages: sanic-routing==22.8.0 +[2023-01-31 12:34:56 +0000] [999997] [INFO] Starting worker [999997] +``` +```` + +.. tab:: TLS server + +```` +Running Sanic with TLS enabled is as simple as passing it the file paths... +```sh +sanic path.to.server:app --cert=/path/to/bundle.crt --key=/path/to/privkey.pem +``` + +... or the a directory containing `fullchain.pem` and `privkey.pem` + +```sh +sanic path.to.server:app --tls=/path/to/certs +``` + +**Even better**, while you are developing, let Sanic handle setting up local TLS certificates so you can access your site over TLS at [https://localhost:8443](https://localhost:8443) + +```sh +sanic path.to.server:app --dev --auto-tls +``` +```` + +.. tab:: Websockets + +```` +Up and running with websockets in no time using the [websockets](https://websockets.readthedocs.io) package. +```python +from sanic import Request, Websocket + +@app.websocket("/feed") +async def feed(request: Request, ws: Websocket): + async for msg in ws: + await ws.send(msg) +``` +```` + +.. tab:: Static files + +```` +Serving static files is of course intuitive and easy. Just name an endpoint and either a file or directory that should be served. + +```python +app.static("/", "/path/to/index.html") +app.static("/uploads/", "/path/to/uploads/") +``` + +Moreover, serving a directory has two additional features: automatically serving an index, and automatically serving a file browser. + +Sanic can automatically serve `index.html` (or any other named file) as an index page in a directory or its subdirectories. + +```python +app.static( + "/uploads/", + "/path/to/uploads/", + index="index.html" +) +``` + +And/or, setup Sanic to display a file browser. + + +![image](/assets/images/directory-view.png) + +```python +app.static( + "/uploads/", + "/path/to/uploads/", + directory_view=True +) +``` +```` + +.. tab:: Lifecycle + +```` +Beginning or ending a route with functionality is as simple as adding a decorator. + +```python +@app.on_request +async def add_key(request): + request.ctx.foo = "bar" + +@app.on_response +async def custom_banner(request, response): + response.headers["X-Foo"] = request.ctx.foo +``` + +Same with server events. + +```python +@app.before_server_start +async def setup_db(app): + app.ctx.db_pool = await db_setup() + +@app.after_server_stop +async def setup_db(app): + await app.ctx.db_pool.shutdown() +``` + +But, Sanic also allows you to tie into a bunch of built-in events (called signals), or create and dispatch your own. + +```python +@app.signal("http.lifecycle.complete") # built-in +async def my_signal_handler(conn_info): + print("Connection has been closed") + +@app.signal("something.happened.ohmy") # custom +async def my_signal_handler(): + print("something happened") + +await app.dispatch("something.happened.ohmy") +``` +```` + +.. tab:: Smart error handling + +```` +Raising errors will intuitively result in proper HTTP errors: + +```python +raise sanic.exceptions.NotFound # Automatically responds with HTTP 404 +``` + +Or, make your own: + +```python +from sanic.exceptions import SanicException + +class TeapotError(SanicException): + status_code = 418 + message = "Sorry, I cannot brew coffee" + +raise TeapotError +``` + +And, when an error does happen, Sanic's beautiful DEV mode error page will help you drill down to the bug quickly. + +![image](../assets/images/error-div-by-zero.png) + +Regardless, Sanic comes with an algorithm that attempts to respond with HTML, JSON, or text-based errors as appropriate. Don't worry, it is super easy to setup and customize your error handling to your exact needs. +```` + +.. tab:: App Inspector + +```` +Check in on your live, running applications (whether local or remote). +```sh +sanic inspect + +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Sanic │ +│ Inspecting @ http://localhost:6457 │ +├───────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────┤ +│ │ mode: production, single worker │ +│ ▄███ █████ ██ │ server: unknown │ +│ ██ │ python: 3.10.9 │ +│ ▀███████ ███▄ │ platform: SomeOS-9.8.7 +│ ██ │ packages: sanic==22.12.0, sanic-routing==22.8.0, sanic-testing==22.12.0, sanic-ext==22.12.0 │ +│ ████ ████████▀ │ │ +│ │ │ +│ Build Fast. Run Fast. │ │ +└───────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────┘ + +Sanic-Main + pid: 999996 + +Sanic-Server-0-0 + server: True + state: ACKED + pid: 999997 + start_at: 2023-01-31T12:34:56.00000+00:00 + starts: 1 + +Sanic-Inspector-0 + server: False + state: STARTED + pid: 999998 + start_at: 2023-01-31T12:34:56.00000+00:00 + starts: 1 +``` + +And, issue commands like `reload`, `shutdown`, `scale`... + +```sh +sanic inspect scale 4 +``` + +... or even create your own! + +```sh +sanic inspect migrations +``` +```` + +.. tab:: Extendable + +``` +In addition to the tools that Sanic comes with, the officially supported [Sanic Extensions](./plugins/sanic-ext/getting-started.md) provides lots of extra goodies to make development easier. + +- **CORS** protection +- Template rendering with **Jinja** +- **Dependency injection** into route handlers +- OpenAPI documentation with **Redoc** and/or **Swagger** +- Predefined, endpoint-specific response **serializers** +- Request query arguments and body input **validation** +- **Auto create** HEAD, OPTIONS, and TRACE endpoints +- Live **health monitor** +``` + +.. tab:: Developer Experience + +``` +Sanic is **built for building**. + +From the moment it is installed, Sanic includes helpful tools to help the developer get their job done. + +- **One server** - Develop locally in DEV mode on the same server that will run your PRODUCTION application +- **Auto reload** - Reload running applications every time you save a Python file, but also auto-reload **on any arbitrary directory** like HTML template directories +- **Debugging tools** - Super helpful (and beautiful) [error pages](/en/guide/best-practices/exceptions) that help you traverse the trace stack easily +- **Auto TLS** - Running a localhost website with `https` can be difficult, [Sanic makes it easy](/en/guide/how-to/tls) +- **Streamlined testing** - Built-in testing capabilities, making it easier for developers to create and run tests, ensuring the quality and reliability of their services +- **Modern Python** - Thoughtful use of type hints to help the developer IDE experience +``` diff --git a/guide/content/ko/organization/code-of-conduct.md b/guide/content/ko/organization/code-of-conduct.md new file mode 100644 index 0000000000..905c7cdda8 --- /dev/null +++ b/guide/content/ko/organization/code-of-conduct.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at adam@sanicframework.org. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/guide/content/ko/organization/contributing.md b/guide/content/ko/organization/contributing.md new file mode 100644 index 0000000000..fc38154f48 --- /dev/null +++ b/guide/content/ko/organization/contributing.md @@ -0,0 +1,166 @@ +# Contributing + +Thank you for your interest! Sanic is always looking for contributors. If you don't feel comfortable contributing code, adding docstrings to the source files, or helping with the [Sanic User Guide](https://github.com/sanic-org/sanic-guide) by providing documentation or implementation examples would be appreciated! + +We are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, disability, ethnicity, religion, or similar personal characteristic. Our [code of conduct](https://github.com/sanic-org/sanic/blob/master/CONDUCT.md) sets the standards for behavior. + +## Installation + +To develop on Sanic (and mainly to just run the tests) it is highly recommend to install from sources. + +So assume you have already cloned the repo and are in the working directory with a virtual environment already set up, then run: + +```sh +pip install -e ".[dev]" +``` + +## Dependency Changes + +`Sanic` doesn't use `requirements*.txt` files to manage any kind of dependencies related to it in order to simplify the effort required in managing the dependencies. Please make sure you have read and understood the following section of the document that explains the way `sanic` manages dependencies inside the `setup.py` file. + +| Dependency Type | Usage | Installation | +| ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- | --------------------------- | +| requirements | Bare minimum dependencies required for sanic to function | `pip3 install -e .` | +| tests_require / extras_require['test'] | Dependencies required to run the Unit Tests for `sanic` | `pip3 install -e '.[test]'` | +| extras_require['dev'] | Additional Development requirements to add contributing | `pip3 install -e '.[dev]'` | +| extras_require['docs'] | Dependencies required to enable building and enhancing sanic documentation | `pip3 install -e '.[docs]'` | + +## Running all tests + +To run the tests for Sanic it is recommended to use tox like so: + +```sh +tox +``` + +See it's that simple! + +`tox.ini` contains different environments. Running `tox` without any arguments will +run all unittests, perform lint and other checks. + +## Run unittests + +`tox` environment -> `[testenv]` + +To execute only unittests, run `tox` with environment like so: + +```sh + +tox -e py37 -v -- tests/test_config.py +# or +tox -e py310 -v -- tests/test_config.py +``` + +## Run lint checks + +`tox` environment -> `[testenv:lint]` + +Permform `flake8`\ , `black` and `isort` checks. + +```sh +tox -e lint +``` + +## Run type annotation checks + +`tox` environment -> `[testenv:type-checking]` + +Permform `mypy` checks. + +```sh +tox -e type-checking +``` + +## Run other checks + +`tox` environment -> `[testenv:check]` + +Perform other checks. + +```sh +tox -e check +``` + +## Run Static Analysis + +`tox` environment -> `[testenv:security]` + +Perform static analysis security scan + +```sh +tox -e security +``` + +## Run Documentation sanity check + +`tox` environment -> `[testenv:docs]` + +Perform sanity check on documentation + +```sh +tox -e docs +``` + +## Code Style + +To maintain the code consistency, Sanic uses the following tools: + +1. [isort](https://github.com/timothycrosley/isort) +2. [black](https://github.com/python/black) +3. [flake8](https://github.com/PyCQA/flake8) +4. [slotscheck](https://github.com/ariebovenberg/slotscheck) + +### isort + +`isort` sorts Python imports. It divides imports into three categories sorted each in alphabetical order: + +1. built-in +2. third-party +3. project-specific + +### black + +`black` is a Python code formatter. + +### flake8 + +`flake8` is a Python style guide that wraps the following tools into one: + +1. PyFlakes +2. pycodestyle +3. Ned Batchelder's McCabe script + +### slotscheck + +`slotscheck` ensures that there are no problems with `__slots__` (e.g., overlaps, or missing slots in base classes). + +`isort`, `black`, `flake8`, and `slotscheck` checks are performed during `tox` lint checks. + +The **easiest** way to make your code conform is to run the following before committing: + +```bash +make pretty +``` + +Refer to [tox documentation](https://tox.readthedocs.io/en/latest/index.html) for more details. + +## Pull requests + +So the pull request approval rules are pretty simple: + +1. All pull requests must pass unit tests. +2. All pull requests must be reviewed and approved by at least one current member of the Core Developer team. +3. All pull requests must pass flake8 checks. +4. All pull requests must match `isort` and `black` requirements. +5. All pull requests must be **PROPERLY** type annotated, unless exemption is given. +6. All pull requests must be consistent with the existing code. +7. If you decide to remove/change anything from any common interface a deprecation message should accompany it in accordance with our [deprecation policy](https://sanicframework.org/en/guide/project/policies.html#deprecation). +8. If you implement a new feature you should have at least one unit test to accompany it. +9. An example must be one of the following: + - Example of how to use Sanic + - Example of how to use Sanic extensions + - Example of how to use Sanic and asynchronous library + +## Documentation + +_Check back. We are reworking our documentation so this will change._ diff --git a/guide/content/ko/organization/policies.md b/guide/content/ko/organization/policies.md new file mode 100644 index 0000000000..e6aca704dd --- /dev/null +++ b/guide/content/ko/organization/policies.md @@ -0,0 +1,80 @@ +# Policies + +## Versioning + +Sanic uses [calendar versioning](https://calver.org/), aka "calver". To be more specific, the pattern follows: + +``` +YY.MM.MICRO +``` + +Generally, versions are referred to in their `YY.MM` form. The `MICRO` number indicates an incremental patch version, starting at `0`. + +## Reporting a Vulnerability + +If you discover a security vulnerability, we ask that you **do not** create an issue on GitHub. Instead, please [send a message to the core-devs](https://community.sanicframework.org/g/core-devs) on the community forums. Once logged in, you can send a message to the core-devs by clicking the message button. + +Alternatively, you can send a private message to Adam Hopkins on Discord. Find him on the [Sanic discord server](https://discord.gg/FARQzAEMAA). + +This will help to not publicize the issue until the team can address it and resolve it. + +## Release Schedule + +There are four (4) scheduled releases per year: March, June, September, and December. Therefore, there are four (4) released versions per year: `YY.3`, `YY.6`, `YY.9`, and `YY.12`. + +This release schedule provides: + +- a predictable release cadence, +- relatively short development windows allowing features to be regularly released, +- controlled [deprecations](#deprecation), and +- consistent stability with a yearly LTS. + +We also use the yearly release cycle in conjunction with our governance model, covered by the [S.C.O.P.E.](./scope.md) + +### Long term support v Interim releases + +Sanic releases a long term support release (aka "LTS") once a year in December. The LTS releases receive bug fixes and security updates for **24 months**. Interim releases throughout the year occur every three months, and are supported until the subsequent release. + +| Version | Release | LTS | Supported | +| ------------------------------------- | ---------- | ------------- | --------- | +| 24.12 | 2024-12-31 | until 2026-12 | ✅ | +| 24.6 | 2024-06-30 | | ⚪ | +| 23.12 | 2023-12-31 | until 2025-12 | ☑️ | +| 23.6 | 2023-07-25 | | ⚪ | +| 23.3 | 2023-03-26 | | ⚪ | +| 22.12 | 2022-12-27 | | ☑️ | +| 22.9 | 2022-09-29 | | ⚪ | +| 22.6 | 2022-06-30 | | ⚪ | +| 22.3 | 2022-03-31 | | ⚪ | +| 21.12 | 2021-12-26 | | ⚪ | +| 21.9 | 2021-09-30 | | ⚪ | +| 21.6 | 2021-06-27 | | ⚪ | +| 21.3 | 2021-03-21 | | ⚪ | +| 20.12 | 2020-12-29 | | ⚪ | +| 20.9 | 2020-09-30 | | ⚪ | +| 20.6 | 2020-06-28 | | ⚪ | +| 20.3 | 2020-05-14 | | ⚪ | +| 19.12 | 2019-12-27 | | ⚪ | +| 19.9 | 2019-10-12 | | ⚪ | +| 19.6 | 2019-06-21 | | ⚪ | +| 19.3 | 2019-03-23 | | ⚪ | +| 18.12 | 2018-12-27 | | ⚪ | +| 0.8.3 | 2018-09-13 | | ⚪ | +| 0.7.0 | 2017-12-06 | | ⚪ | +| 0.6.0 | 2017-08-03 | | ⚪ | +| 0.5.4 | 2017-05-09 | | ⚪ | +| 0.4.1 | 2017-02-28 | | ⚪ | +| 0.3.1 | 2017-02-09 | | ⚪ | +| 0.2.0 | 2017-01-14 | | ⚪ | +| 0.1.9 | 2016-12-25 | | ⚪ | +| 0.1.0 | 2016-10-16 | | ⚪ | + +☑️ = security fixes\ +✅ = full support\ +⚪ = no support + +## Deprecation + +Before a feature is deprecated, or breaking changes are introduced into the API, it shall be publicized and shall appear with deprecation warnings through two release cycles. No deprecations shall be made in an LTS release. + +Breaking changes or feature removal may happen outside of these guidelines when absolutely warranted. These circumstances should be rare. For example, it might happen when no alternative is available to curtail a major security issue. diff --git a/guide/content/ko/organization/scope.md b/guide/content/ko/organization/scope.md new file mode 100644 index 0000000000..47a1c4f085 --- /dev/null +++ b/guide/content/ko/organization/scope.md @@ -0,0 +1,257 @@ +# Sanic Community Organization Policy E-manual (SCOPE) + +.. attrs:: +:class: is-size-7 + +``` +_December 2019, version 1_ +``` + +## Goals + +To create a sustainable, community-driven organization around the Sanic projects that promote: (1) stability and predictability, (2) quick iteration and enhancement cycles, (3) engagement from outside contributors, (4) overall reliable software, and (5) a safe, rewarding environment for the community members. + +## Overview + +This Policy is the governance model for the Sanic Community Organization (“SCO”). The SCO is a meritocratic, consensus-based community organization responsible for all projects adopted by it. Anyone with an interest in one of the projects can join the community, contribute to the community or projects, and participate in the decision making process. This document describes how that participation takes place and how to set about earning merit within the project community. + +## Structure + +The SCO has multiple **projects**. Each project is represented by a single GitHub repository under the Sanic community umbrella. These projects are used by **users**, developed by **contributors**, governed by **core developers**, released by **release managers**, and ultimately overseen by a **steering council**. If this sounds similar to the Python project and PEP 8016 that is because it is intentionally designed that way. + +## Roles and responsibilities + +### Users + +Users are community members who have a need for the projects. They are the developers and personnel that download and install the packages. Users are the **most important** members of the community and without them the projects would have no purpose. Anyone can be a user and the licenses adopted by the projects shall be appropriate open source licenses. + +_The SCO asks its users to participate in the project and community as much as possible._ + +User contributions enable the project team to ensure that they are satisfying the needs of those users. Common user contributions include (but are not limited to): + +- evangelizing about the project (e.g. a link on a website and word-of-mouth awareness raising) +- informing developers of strengths and weaknesses from a new user perspective +- providing moral support (a ‘thank you’ goes a long way) +- providing financial support (the software is open source, but its developers need to eat) + +Users who continue to engage with the SCO, its projects, and its community will often become more and more involved. Such users may find themselves becoming contributors, as described in the next section. + +### Contributors + +Contributors are community members who contribute in concrete ways to one or more of the projects. Anyone can become a contributor and contributions can take many forms. Contributions and requirements are governed by each project separately by a contribution policy. + +There is **no expectation** of commitment to the project, **no specific skill requirements** and **no selection process**. + +In addition to their actions as users, contributors may also find themselves doing one or more of the following: + +- supporting new users (existing users are often the best people to support new users) +- reporting bugs +- identifying requirements +- providing graphics and web design +- Programming +- example use cases +- assisting with project infrastructure +- writing documentation +- fixing bugs +- adding features +- providing constructive opinions and engaging in community discourse + +Contributors engage with the projects through GitHub and the Community Forums. They submit changes to the projects itself via pull requests, which will be considered for inclusion in the project by the community at large. The Community Forums are the most appropriate place to ask for help when making that first contribution. + +Indeed one of the most important roles of a contributor may be to **simply engage in the community conversation**. Most decisions about the direction of a project are made by consensus. This is discussed in more detail below. In general, however, it is helpful for the health and direction of the projects for the contributors to **speak freely** (within the confines of the code of conduct) and **express their opinions and experiences** to help drive the consensus building. + +As contributors gain experience and familiarity with a project, their profile within, and commitment to, the community will increase. At some stage, they may find themselves being nominated for a core developer team. + +### Core Developer + +Each project under the SCO umbrella has its own team of core developers. They are the people in charge of that project. + +_What is a core developer?_ + +Core developers are community members who have shown that they are committed to the continued development of the project through ongoing engagement with the community. Being a core developer allows contributors to more easily carry on with their project related activities by giving them direct access to the project’s resources. They can make changes directly to the project repository without having to submit changes via pull requests from a fork. + +This does not mean that a core developer is free to do what they want. In fact, core developers have no more direct authority over the final release of a package than do contributors. While this honor does indicate a valued member of the community who has demonstrated a healthy respect for the project’s aims and objectives, their work continues to be reviewed by the community before acceptance in an official release. + +_What can a core developer do on a project?_ + +Each project might define this role slightly differently. However, the general usage of this designation is that an individual has risen to a level of trust within the community such that they now are given some control. This comes in the form of push rights to non-protected branches, and the ability to have a voice in the approval of pull requests. + +The projects employ various communication mechanisms to ensure that all contributions are reviewed by the community as a whole. This includes tools provided by GitHub, as well as the Community Forums. By the time a contributor is invited to become a core developer, they should be familiar with the various tools and workflows as a user and then as a contributor. + +_How to become a core developer?_ + +Anyone can become a core developer; there are no special requirements, other than to have shown a willingness and ability to positively participate in the project as a team player. + +Typically, a potential core developer will need to show that they have an understanding of the project, its objectives and its strategy. They will also have provided valuable contributions to the project over a period of time. However, there is **no technical or other skill** requirement for eligibility. + +New core developers can be **nominated by any existing core developer** at any time. At least twice a year (April and October) there will be a ballot process run by the Steering Council. Voting should be done by secret ballot. Each existing core developer for that project receives a number of votes equivalent to the number of nominees on the ballot. For example, if there are four nominees, then each existing core developer has four votes. The core developer may cast those votes however they choose, but may not vote for a single nominee more than once. A nominee must receive two-thirds approval from the number of cast ballots (not the number of eligible ballots). Once accepted by the core developers, it is the responsibility of the Steering Council to approve and finalize the nomination. The Steering Council does not have the right to determine whether a nominee is meritorious enough to receive the core developer title. However, they do retain the right to override a vote in cases where the health of the community would so require. + +Once the vote has been held, the aggregated voting results are published on the Community Forums. The nominee is entitled to request an explanation of any override against them. A nominee that fails to be admitted as a core developer may be nominated again in the future. + +It is important to recognize that being a core developer is a privilege, not a right. That privilege must be earned and once earned it can be removed by the Steering Council (see next section) in extreme circumstances. However, under normal circumstances the core developer title exists for as long as the individual wishes to continue engaging with the project and community. + +A committer who shows an above-average level of contribution to the project, particularly with respect to its strategic direction and long-term health, may be nominated to become a member of the Steering Council, or a Release Manager. This role is described below. + +_What are the rights and responsibilities of core developers?_ + +As discussed, the majority of decisions to be made are by consensus building. In certain circumstances where an issue has become more contentious, or a major decision needs to be made, the Release Manager or Steering Council may decide (or be required) to implement the RFC process, which is outlined in more detail below. + +It is also incumbent upon core developers to have a voice in the governance of the community. All core developers for all of the projects have the ability to be nominated to be on the Steering Council and vote in their elections. + +This Policy (the “SCOPE”) may only be changed under the authority of two-thirds of active core developers, except that in the first six (6) months after adoption, the core developers reserve the right to make changes under the authority of a simple majority of active core developers. + +_What if a core developer becomes inactive?_ + +It is hoped that all core developers participate and remain active on a regular basis in their projects. However, it is also understood that such commitments may not be realistic or possible from time to time. + +Therefore, the Steering Council has the duty to encourage participation and the responsibility to place core developers into an inactive status if they are no longer willing or capable to participate. The main purpose of this is **not to punish** a person for behavior, but to help the development process to continue for those that do remain active. + +To this end, a core developer that becomes “inactive” shall not have commit rights to a repository, and shall not participate in any votes. To be eligible to vote in an election, a core developer **must have been active** at the time of the previous scheduled project release. + +Inactive members may ask the Steering Council to reinstate their status at any time, and upon such request the Steering Council shall make the core developer active again. + +Individuals that know they will be unable to maintain their active status for a period are asked to be in communication with the Steering Council and declare themselves inactive if necessary. + +An “active” core developer is an individual that has participated in a meaningful way during the previous six months. Any further definition is within the discretion of the Steering Council. + +### Release Manager + +Core developers shall have access only to make commits and merges on non-protected branches. The “master” branch and other protected branches are controlled by the release management team for that project. Release managers shall be elected from the core development team by the core development team, and shall serve for a full release cycle. + +Each core developer team may decide how many release managers to have for each release cycle. It is highly encouraged that there be at least two release managers for a release cycle to help divide the responsibilities and not force too much effort upon a single person. However, there also should not be so many managers that their efforts are impeded. + +The main responsibilities of the release management team include: + +- push the development cycle forward by monitoring and facilitating technical discussions +- establish a release calendar and perform actions required to release packages +- approve pull requests to the master branch and other protected branches +- merge pull requests to the master branch and other protected branches + +The release managers **do not have the authority to veto or withhold a merge** of a pull request that otherwise meets contribution criteria and has been accepted by the community. It is not their responsibility to decide what should be developed, but rather that the decisions of the community are carried out and that the project is being moved forward. + +From time to time, a decision may need to be made that cannot be achieved through consensus. In that case, the release managers have the authority to call upon the removal of the decision to the RFC process. This should not occur regularly (unless required as discussed below), and its use should be discouraged in favor of the more communal consensus building strategy. + +Since not all projects have the same requirements, the specifics governing release managers on a project shall be set forth in an Appendix to this Policy, or in the project’s contribution guidelines. + +If necessary, the Steering Council has the right to remove a release manager that is derelict in their duties, or for other good cause. + +### Steering Council + +The Steering Council is the governing body consisting of those individuals identified as the “project owner” and having control of the resources and assets of the SCO. Their ultimate goal is to ensure the smooth operation of the projects by removing impediments, and assisting the members as needed. It is expected that they will be regular voices in the community. + +_What can the Steering Council do?_ + +The members of the Steering Council **do not individually have any more authority than any other core developer**, and shall not have any additional rights to make decisions, commits, merges, or the like on a project. + +However, as a body, the Steering Council has the following capacity: + +- accept, remand, and reject all RFCs +- enforce the community code of conduct +- administer community assets such as repositories, servers, forums, integration services, and the like (or, to delegate such authority to someone else) +- place core developers into inactive status where appropriate take any other enforcement measures afforded to it in this Policy, including, in extreme cases, removing core developers +- adopt or remove projects from the community umbrella + +It is highly encouraged that the Steering Council delegate its authority as much as possible, and where appropriate, to other willing community members. + +The Steering Council **does not have the authority** to change this Policy. + +_How many members are on the Steering Council?_ + +Four. + +While it seems like a committee with four votes may potentially end in a deadlock with no way to break a majority vote, the Steering Council is discouraged from voting as much as possible. Instead, it should try to work by consensus, and requires three consenting votes when it is necessary to vote on a matter. + +_How long do members serve on the Steering Council?_ + +A single term shall be for two calendar years starting in January. Terms shall be staggered so that each year there are two members continuing from the previous year’s council. + +Therefore, the inaugural vote shall have two positions available for a two year term, and two positions available for a one year term. + +There are no limits to the number of terms that can be served, and it is possible for an individual to serve consecutive terms. + +_Who runs the Steering Council?_ + +After the Steering Council is elected, the group shall collectively decide upon one person to act as the Chair. The Chair does not have any additional rights or authority over any other member of the Steering Council. + +The role of the Chair is merely as a coordinator and facilitator. The Chair is expected to ensure that all governance processes are adhered to. The position is more administrative and clerical, and is expected that the Chair sets agendas and coordinates discussion of the group. + +_How are council members elected?_ + +Once a year, **all eligible core developers** for each of the projects shall have the right to elect members to the Steering Council. + +Nominations shall be open from September 1 and shall close on September 30. After that, voting shall begin on October 1 and shall close on October 31. Every core developer active on the date of the June release of the Sanic Framework for that year shall be eligible to receive one vote per vacant seat on the Steering Council. For the sake of clarity, to be eligible to vote, a core developer **does not** need to be a core developer on Sanic Framework, but rather just have been active within their respective project on that date. + +The top recipients of votes shall be declared the winners. If there is any tie, it is highly encouraged that the tied nominees themselves resolve the dispute before a decision is made at random. + +In regards to the inaugural vote of the Steering Council, the top two vote-recipients shall serve for two years, and the next two vote-recipients shall assume the one-year seats. + +To be an eligible candidate for the Steering Council, the individual must have been a core developer in active status on at least one project for the previous twelve months. + +_What if there is a vacancy?_ + +If a vacancy on the Steering Council exists during a term, then the next highest vote-recipient in the previous election shall be offered to complete the remainder of the term. If one cannot be found this way, the Steering Council may decide the most appropriate course of action to fill the seat (whether by appointment, vote, or other means). + +If a member of the Steering Council becomes inactive, then that individual shall be removed from the Steering Council immediately and the seat shall become vacant. + +In extreme cases, the body of all core developers has the right to bring a vote to remove a member of the Steering Council for cause by a two-thirds majority of all eligible voting core developers. + +_How shall the Steering Council conduct its business?_ + +As much as possible, the Steering Council shall conduct its business and discussions in the open. Any member of the community should be allowed to enter the conversation with them. However, at times it may be necessary or appropriate for discussions to be held privately. Selecting the proper venue for conversations is part of the administrative duties of the Chair. + +While the specifics of how to operate are beyond the scope of the Policy, it is encouraged that the Steering Council attempt to meet at least one time per quarter in a “real-time” discussion. This could be achieved via video conferencing, live chatting, or other appropriate means. + +## Support + +All participants in the community are encouraged to provide support for users within the project management infrastructure. This support is provided as a way of growing the community. Those seeking support should recognize that all support activity within the project is voluntary and is therefore provided as and when time allows. A user requiring guaranteed response times or results should therefore seek to purchase a support contract from a community member. However, for those willing to engage with the project on its own terms, and willing to help support other users, the community support channels are ideal. + +## Decision making process + +Decisions about the future of the projects are made through discussion with all members of the community, from the newest user to the most experienced member. Everyone has a voice. + +All non-sensitive project management discussion takes place on the community forums, or other designated channels. Occasionally, sensitive discussions may occur in private. + +In order to ensure that the project is not bogged down by endless discussion and continual voting, the project operates a policy of **lazy consensus**. This allows the majority of decisions to be made without resorting to a formal vote. For any **major decision** (as defined below), there is a separate Request for Comment (RFC) process. + +### Technical decisions + +Pull requests and technical decisions should generally fall into the following categories. + +- **Routine**: Documentation fixes, code changes that are for cleanup or additional testing. No functionality changes. +- **Minor**: Changes to the code base that either fix a bug, or introduce a trivial feature. No breaking changes. +- **Major**: Any change to the code base that breaks or deprecates existing API, alters operation in a non-trivial manner, or adds a significant feature. + +It is generally the responsibility of the release managers to make sure that changes to the repositories receive the proper authorization before merge. + +The release managers retain the authority to individually review and accept routine decisions that meet standards for code quality without additional input. + +### Lazy consensus + +Decision making (whether by the community or Steering Council) typically involves the following steps: + +- proposal +- discussion +- vote (if consensus is not reached through discussion) +- decision + +Any community member can make a proposal for consideration by the community. In order to initiate a discussion about a new idea, they should post a message on the appropriate channel on the Community forums, or submit a pull request implementing the idea on GitHub. This will prompt a review and, if necessary, a discussion of the idea. + +The goal of this review and discussion is to gain approval for the contribution. Since most people in the project community have a shared vision, there is often little need for discussion in order to reach consensus. + +In general, as long as nobody explicitly opposes a proposal or patch, it is recognized as having the support of the community. This is called lazy consensus; that is, those who have not stated their opinion explicitly have implicitly agreed to the implementation of the proposal. + +Lazy consensus is a very important concept within the SCO. It is this process that allows a large group of people to efficiently reach consensus, as someone with no objections to a proposal need not spend time stating their position, and others need not spend time reading such messages. + +For lazy consensus to be effective, it is necessary to allow an appropriate amount of time before assuming that there are no objections to the proposal. This is somewhat dependent upon the circumstances, but it is generally assumed that 72 hours is reasonable. This requirement ensures that everyone is given enough time to read, digest and respond to the proposal. This time period is chosen so as to be as inclusive as possible of all participants, regardless of their location and time commitments. The facilitators of discussion (whether it be the Chair or the Release Managers, where applicable) shall be charged with determining the proper length of time for such consensus to be reached. + +As discussed above regarding so-called routine decisions, the release managers have the right to make decisions within a shorter period of time. In such cases, lazy consensus shall be implied. + +### Request for Comment (RFC) + +The Steering Council shall be in charge of overseeing the RFC process. It shall be a process that remains open to debate to all members of the community, and shall allow for ample time to consider a proposal and for members to respond and engage in meaningful discussion. + +The final decision is vested with the Steering Council. However, it is strongly discouraged that the Steering Council adopt a decision that is contrary to any consensus that may exist in the community. From time to time this may happen if there is a conflict between consensus and the overall project and community goals. + +An RFC shall be initiated by submission to the Steering Council in the public manner as set forth by the Steering Council. Debate shall continue and be facilitated by the Steering Council in general, and the Chair specifically. + +In circumstances that the Steering Council feels it is appropriate, the RFC process may be waived in favor of lazy consensus. diff --git a/guide/content/ko/plugins/sanic-ext/configuration.md b/guide/content/ko/plugins/sanic-ext/configuration.md new file mode 100644 index 0000000000..2debb7e779 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/configuration.md @@ -0,0 +1,288 @@ +--- +title: Sanic Extensions - Configuration +--- + +# Configuration + +Sanic Extensions can be configured in all of the same ways that [you can configure Sanic](../../guide/deployment/configuration.md). That makes configuring Sanic Extensions very easy. + +```python +app = Sanic("MyApp") +app.config.OAS_URL_PREFIX = "/apidocs" +``` + +However, there are a few more configuration options that should be considered. + +## Manual `extend` + +.. column:: + +``` +Even though Sanic Extensions will automatically attach to your application, you can manually choose `extend`. When you do that, you can pass all of the configuration values as a keyword arguments (lowercase). +``` + +.. column:: + +```` +```python +app = Sanic("MyApp") +app.extend(oas_url_prefix="/apidocs") +``` +```` + +.. column:: + +``` +Or, alternatively they could be passed all at once as a single `dict`. +``` + +.. column:: + +```` +```python +app = Sanic("MyApp") +app.extend(config={"oas_url_prefix": "/apidocs"}) +``` +```` + +.. column:: + +``` +Both of these solutions suffers from the fact that the names of the configuration settings are not discoverable by an IDE. Therefore, there is also a type annotated object that you can use. This should help the development experience. +``` + +.. column:: + +```` +```python +from sanic_ext import Config + +app = Sanic("MyApp") +app.extend(config=Config(oas_url_prefix="/apidocs")) +``` +```` + +## Settings + +.. note:: + +```` +Often, the easiest way to change these for an application (since they likely are not going to change dependent upon an environment), is to set them directly on the `app.config` object. + +Simply use the capitalized version of the configuration key as shown here: + +```python +app = Sanic("MyApp") +app.config.OAS_URL_PREFIX = "/apidocs" +``` +```` + +### `cors` + +- **Type**: `bool` +- **Default**: `True` +- **Description**: Whether to enable CORS protection + +### `cors_allow_headers` + +- **Type**: `str` +- **Default**: `"*"` +- **Description**: Value of the header: `access-control-allow-headers` + +### `cors_always_send` + +- **Type**: `bool` +- **Default**: `True` +- **Description**: Whether to always send the header: `access-control-allow-origin` + +### `cors_automatic_options` + +- **Type**: `bool` +- **Default**: `True` +- **Description**: Whether to automatically generate `OPTIONS` endpoints for routes that do _not_ already have one defined + +### `cors_expose_headers` + +- **Type**: `str` +- **Default**: `""` +- **Description**: Value of the header: `access-control-expose-headers` + +### `cors_max_age` + +- **Type**: `int` +- **Default**: `5` +- **Description**: Value of the header: `access-control-max-age` + +### `cors_methods` + +- **Type**: `str` +- **Default**: `""` +- **Description**: Value of the header: `access-control-access-control-allow-methods` + +### `cors_origins` + +- **Type**: `str` +- **Default**: `""` +- **Description**: Value of the header: `access-control-allow-origin` + +.. warning:: + +``` +Be very careful if you place `*` here. Do not do this unless you know what you are doing as it can be a security issue. +``` + +### `cors_send_wildcard` + +- **Type**: `bool` +- **Default**: `False` +- **Description**: Whether to send a wildcard origin instead of the incoming request origin + +### `cors_supports_credentials` + +- **Type**: `bool` +- **Default**: `False` +- **Description**: Value of the header: `access-control-allow-credentials` + +### `cors_vary_header` + +- **Type**: `bool` +- **Default**: `True` +- **Description**: Whether to add the `vary` header + +### `http_all_methods` + +- **Type**: `bool` +- **Default**: `True` +- **Description**: Adds the HTTP `CONNECT` and `TRACE` methods as allowable + +### `http_auto_head` + +- **Type**: `bool` +- **Default**: `True` +- **Description**: Automatically adds `HEAD` handlers to any `GET` routes + +### `http_auto_options` + +- **Type**: `bool` +- **Default**: `True` +- **Description**: Automatically adds `OPTIONS` handlers to any routes without + +### `http_auto_trace` + +- **Type**: `bool` +- **Default**: `False` +- **Description**: Automatically adds `TRACE` handlers to any routes without + +### `oas` + +- **Type**: `bool` +- **Default**: `True` +- **Description**: Whether to enable OpenAPI specification generation + +### `oas_autodoc` + +- **Type**: `bool` +- **Default**: `True` +- **Description**: Whether to automatically extract OpenAPI details from the docstring of a route function + +### `oas_ignore_head` + +- **Type**: `bool` +- **Default**: `True` +- **Description**: WHen `True`, it will not add `HEAD` endpoints into the OpenAPI specification + +### `oas_ignore_options` + +- **Type**: `bool` +- **Default**: `True` +- **Description**: WHen `True`, it will not add `OPTIONS` endpoints into the OpenAPI specification + +### `oas_path_to_redoc_html` + +- **Type**: `Optional[str]` +- **Default**: `None` +- **Description**: Path to HTML file to override the existing Redoc HTML + +### `oas_path_to_swagger_html` + +- **Type**: `Optional[str]` +- **Default**: `None` +- **Description**: Path to HTML file to override the existing Swagger HTML + +### `oas_ui_default` + +- **Type**: `Optional[str]` +- **Default**: `"redoc"` +- **Description**: Which OAS documentation to serve on the bare `oas_url_prefix` endpoint; when `None` there will be no documentation at that location + +### `oas_ui_redoc` + +- **Type**: `bool` +- **Default**: `True` +- **Description**: Whether to enable the Redoc UI + +### `oas_ui_swagger` + +- **Type**: `bool` +- **Default**: `True` +- **Description**: Whether to enable the Swagger UI + +### `oas_ui_swagger_version` + +- **Type**: `str` +- **Default**: `"4.1.0"` +- **Description**: Which Swagger version to use + +### `oas_uri_to_config` + +- **Type**: `str` +- **Default**: `"/swagger-config"` +- **Description**: Path to serve the Swagger configuration + +### `oas_uri_to_json` + +- **Type**: `str` +- **Default**: `"/openapi.json"` +- **Description**: Path to serve the OpenAPI JSON + +### `oas_uri_to_redoc` + +- **Type**: `str` +- **Default**: `"/redoc"` +- **Description**: Path to Redoc + +### `oas_uri_to_swagger` + +- **Type**: `str` +- **Default**: `"/swagger"` +- **Description**: Path to Swagger + +### `oas_url_prefix` + +- **Type**: `str` +- **Default**: `"/docs"` +- **Description**: URL prefix for the Blueprint that all of the OAS documentation witll attach to + +### `swagger_ui_configuration` + +- **Type**: `Dict[str, Any]` +- **Default**: `{"apisSorter": "alpha", "operationsSorter": "alpha", "docExpansion": "full"}` +- **Description**: The Swagger documentation to be served to the frontend + +### `templating_enable_async` + +- **Type**: `bool` +- **Default**: `True` +- **Description**: Whether to set `enable_async` on the Jinja `Environment` + +### `templating_path_to_templates` + +- **Type**: `Union[str, os.PathLike, Sequence[Union[str, os.PathLike]]] ` +- **Default**: `templates` +- **Description**: A single path, or multiple paths to where your template files are located + +### `trace_excluded_headers` + +- **Type**: `Sequence[str]` +- **Default**: `("authorization", "cookie")` +- **Description**: Which headers should be suppresed from responses to `TRACE` requests diff --git a/guide/content/ko/plugins/sanic-ext/convenience.md b/guide/content/ko/plugins/sanic-ext/convenience.md new file mode 100644 index 0000000000..7aaef2aa75 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/convenience.md @@ -0,0 +1,134 @@ +--- +title: Sanic Extensions - Convenience +--- + +# Convenience + +## Fixed serializer + +.. column:: + +``` +Often when developing an application, there will be certain routes that always return the same sort of response. When this is the case, you can predefine the return serializer and on the endpoint, and then all that needs to be returned is the content. +``` + +.. column:: + +```` +```python +from sanic_ext import serializer + +@app.get("/") +@serializer(text) +async def hello_world(request, name: str): + if name.isnumeric(): + return "hello " * int(name) + return f"Hello, {name}" +``` +```` + +.. column:: + +``` +The `serializer` decorator also can add status codes. +``` + +.. column:: + +```` +```python +from sanic_ext import serializer + +@app.post("/") +@serializer(text, status=202) +async def create_something(request): + ... +``` +```` + +## Custom serializer + +.. column:: + +``` +Using the `@serializer` decorator, you can also pass your own custom functions as long as they also return a valid type (`HTTPResonse`). +``` + +.. column:: + +```` +```python +def message(retval, request, action, status): + return json( + { + "request_id": str(request.id), + "action": action, + "message": retval, + }, + status=status, + ) + +@app.post("/") +@serializer(message) +async def do_action(request, action: str): + return "This is a message" +``` +```` + +.. column:: + +``` +Now, returning just a string should return a nice serialized output. +``` + +.. column:: + +```` +```python +$ curl localhost:8000/eat_cookies -X POST +{ + "request_id": "ef81c45b-235c-46dd-9dbd-b550f8fa77f9", + "action": "eat_cookies", + "message": "This is a message" +} + +``` +```` + +## Request counter + +.. column:: + +``` +Sanic Extensions comes with a subclass of `Request` that can be setup to automatically keep track of the number of requests processed per worker process. To enable this, you should pass the `CountedRequest` class to your application contructor. +``` + +.. column:: + +```` +```python +from sanic_ext import CountedRequest + +app = Sanic(..., request_class=CountedRequest) +``` +```` + +.. column:: + +``` +You will now have access to the number of requests served during the lifetime of the worker process. +``` + +.. column:: + +```` +```python +@app.get("/") +async def handler(request: CountedRequest): + return json({"count": request.count}) +``` +```` + +If possible, the request count will also be added to the [worker state](../../guide/deployment/manager.md#worker-state). + +![](https://user-images.githubusercontent.com/166269/190922460-43bd2cfc-f81a-443b-b84f-07b6ce475cbf.png) diff --git a/guide/content/ko/plugins/sanic-ext/custom.md b/guide/content/ko/plugins/sanic-ext/custom.md new file mode 100644 index 0000000000..6facab3f66 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/custom.md @@ -0,0 +1,92 @@ +--- +title: Sanic Extensions - Custom +--- + +# Custom extensions + +It is possible to create your own custom extensions. + +Version 22.9 added the `Extend.register` [method](#extension-preregistration). This makes it extremely easy to add custom expensions to an application. + +## Anatomy of an extension + +All extensions must subclass `Extension`. + +### Required + +- `name`: By convention, the name is an all-lowercase string +- `startup`: A method that runs when the extension is added + +### Optional + +- `label`: A method that returns additional information about the extension in the MOTD +- `included`: A method that returns a boolean whether the extension should be enabled or not (could be used for example to check config state) + +### Example + +```python +from sanic import Request, Sanic, json +from sanic_ext import Extend, Extension + +app = Sanic(__name__) +app.config.MONITOR = True + +class AutoMonitor(Extension): + name = "automonitor" + + def startup(self, bootstrap) -> None: + if self.included(): + self.app.before_server_start(self.ensure_monitor_set) + self.app.on_request(self.monitor) + + @staticmethod + async def monitor(request: Request): + if request.route and request.route.ctx.monitor: + print("....") + + @staticmethod + async def ensure_monitor_set(app: Sanic): + for route in app.router.routes: + if not hasattr(route.ctx, "monitor"): + route.ctx.monitor = False + + def label(self): + has_monitor = [ + route + for route in self.app.router.routes + if getattr(route.ctx, "monitor", None) + ] + return f"{len(has_monitor)} endpoint(s)" + + def included(self): + return self.app.config.MONITOR + +Extend.register(AutoMonitor) + +@app.get("/", ctx_monitor=True) +async def handler(request: Request): + return json({"foo": "bar"}) +``` + +## Extension preregistration + +.. column:: + +``` +`Extend.register` simplifies the addition of custom extensions. +``` + +.. column:: + +```` +```python +from sanic_ext import Extend, Extension + +class MyCustomExtension(Extension): + ... + +Extend.register(MyCustomExtension()) +``` +```` + +_Added in v22.9_ diff --git a/guide/content/ko/plugins/sanic-ext/getting-started.md b/guide/content/ko/plugins/sanic-ext/getting-started.md new file mode 100644 index 0000000000..bf49796ec7 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/getting-started.md @@ -0,0 +1,92 @@ +--- +title: Sanic Extensions - Getting Started +--- + +# Getting Started + +Sanic Extensions is an _officially supported_ plugin developed, and maintained by the SCO. The primary goal of this project is to add additional features to help Web API and Web application development easier. + +## Features + +- CORS protection +- Template rendering with Jinja +- Dependency injection into route handlers +- OpenAPI documentation with Redoc and/or Swagger +- Predefined, endpoint-specific response serializers +- Request query arguments and body input validation +- Auto create `HEAD`, `OPTIONS`, and `TRACE` endpoints + +## Minimum requirements + +- **Python**: 3.8+ +- **Sanic**: 21.9+ + +## Install + +The best method is to just install Sanic Extensions along with Sanic itself: + +```bash +pip install sanic[ext] +``` + +You can of course also just install it by itself. + +```bash +pip install sanic-ext +``` + +## Extend your application + +Out of the box, Sanic Extensions will enable a bunch of features for you. + +.. column:: + +``` +To setup Sanic Extensions (v21.12+), you need to do: **nothing**. If it is installed in the environment, it is setup and ready to go. + +This code is the Hello, world app in the [Sanic Getting Started page](../../guide/getting-started.md) _without any changes_, but using Sanic Extensions with `sanic-ext` installed in the background. +``` + +.. column:: + +```` +```python +from sanic import Sanic +from sanic.response import text + +app = Sanic("MyHelloWorldApp") + +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` +```` + +.. column:: + +``` +**_OLD DEPRECATED SETUP_** + +In v21.9, the easiest way to get started is to instantiate it with `Extend`. + +If you look back at the Hello, world app in the [Sanic Getting Started page](../../guide/getting-started.md), you will see the only additions here are the two highlighted lines. +``` + +.. column:: + +```` +```python +from sanic import Sanic +from sanic.response import text +from sanic_ext import Extend + +app = Sanic("MyHelloWorldApp") +Extend(app) + +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` +```` + +Regardless of how it is setup, you should now be able to view the OpenAPI documentation and see some of the functionality in action: [http://localhost:8000/docs](http://localhost:8000/docs). diff --git a/guide/content/ko/plugins/sanic-ext/health-monitor.md b/guide/content/ko/plugins/sanic-ext/health-monitor.md new file mode 100644 index 0000000000..16111a0896 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/health-monitor.md @@ -0,0 +1,81 @@ +--- +title: Sanic Extensions - Health Monitor +--- + +# Health monitor + +The health monitor requires both `sanic>=22.9` and `sanic-ext>=22.9`. + +You can setup Sanic Extensions to monitor the health of your worker processes. This requires that you not be in [single process mode](../../guide/deployment/manager.md#single-process-mode). + +## Setup + +.. column:: + +``` +Out of the box, the health monitor is disabled. You will need to opt-in and enable the endpoint if you would like to use it. +``` + +.. column:: + +```` +```python +app.config.HEALTH = True +app.config.HEALTH_ENDPOINT = True +``` +```` + +## How does it work + +The monitor sets up a new background process that will periodically receive acknowledgements of liveliness from each worker process. If a worker process misses a report too many times, then the monitor will restart that one worker. + +## Diagnostics endpoint + +.. column:: + +``` +The health monitor will also enable a diagnostics endpoint that outputs the [worker state](../../guide/deployment/manager.md#worker-state). By default is id disabled. + +.. danger:: + + The diagnostics endpoint is not secured. If you are deploying it in a production environment, you should take steps to protect it with a proxy server if you are using one. If not, you may want to consider disabling this feature in production since it will leak details about your server state. +``` + +.. column:: + +```` +``` +$ curl http://localhost:8000/__health__ +{ + 'Sanic-Main': {'pid': 99997}, + 'Sanic-Server-0-0': { + 'server': True, + 'state': 'ACKED', + 'pid': 9999, + 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), + 'starts': 2, + 'restart_at': datetime.datetime(2022, 10, 1, 0, 0, 12, 861332, tzinfo=datetime.timezone.utc) + }, + 'Sanic-Reloader-0': { + 'server': False, + 'state': 'STARTED', + 'pid': 99998, + 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), + 'starts': 1 + } +} +``` +```` + +## Configuration + +| Key | Type | Default | Description | +| --------------------------------------------------------------------------------- | ------ | --------------- | ------------------------------------------------------------------------------------------- | +| HEALTH | `bool` | `False` | Whether to enable this extension. | +| HEALTH_ENDPOINT | `bool` | `False` | Whether to enable the diagnostics endpoint. | +| HEALTH_MAX_MISSES | `int` | `3` | The number of consecutive misses before a worker process is restarted. | +| HEALTH_MISSED_THRESHHOLD | `int` | `10` | The number of seconds the monitor checks for worker process health. | +| HEALTH_MONITOR | `bool` | `True` | Whether to enable the health monitor. | +| HEALTH_REPORT_INTERVAL | `int` | `5` | The number of seconds between reporting each acknowledgement of liveliness. | +| HEALTH_URI_TO_INFO | `str` | `""` | The URI path of the diagnostics endpoint. | +| HEALTH_URL_PREFIX | `str` | `"/__health__"` | The URI prefix of the diagnostics blueprint. | diff --git a/guide/content/ko/plugins/sanic-ext/http/cors.md b/guide/content/ko/plugins/sanic-ext/http/cors.md new file mode 100644 index 0000000000..53701868a9 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/http/cors.md @@ -0,0 +1,97 @@ +--- +title: Sanic Extensions - CORS protection +--- + +# CORS protection + +Cross-Origin Resource Sharing (aka CORS) is a _huge_ topic by itself. The documentation here cannot go into enough detail about _what_ it is. You are highly encouraged to do some research on your own to understand the security problem presented by it, and the theory behind the solutions. [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) are a great first step. + +In super brief terms, CORS protection is a framework that browsers use to facilitate how and when a web page can access information from another domain. It is extremely relevant to anyone building a single-page application. Often times your frontend might be on a domain like `https://portal.myapp.com`, but it needs to access the backend from `https://api.myapp.com`. + +The implementation here is heavily inspired by [`sanic-cors`](https://github.com/ashleysommer/sanic-cors), which is in turn based upon [`flask-cors`](https://github.com/corydolphin/flask-cors). It is therefore very likely that you can achieve a near drop-in replacement of `sanic-cors` with `sanic-ext`. + +## Basic implementation + +.. column:: + +``` +As shown in the example in the [auto-endpoints example](methods.md#options), Sanic Extensions will automatically enable CORS protection without further action. But, it does not offer too much out of the box. + +At a *bare minimum*, it is **highly** recommended that you set `config.CORS_ORIGINS` to the intended origin(s) that will be accessing the application. +``` + +.. column:: + +```` +```python +from sanic import Sanic, text +from sanic_ext import Extend + +app = Sanic(__name__) +app.config.CORS_ORIGINS = "http://foobar.com,http://bar.com" +Extend(app) + +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` + +``` +$ curl localhost:8000 -X OPTIONS -i +HTTP/1.1 204 No Content +allow: GET,HEAD,OPTIONS +access-control-allow-origin: http://foobar.com +connection: keep-alive +``` +```` + +## Configuration + +The true power of CORS protection, however, comes into play once you start configuring it. Here is a table of all of the options. + +| Key | Type | Default | Description | +| --------------------------- | -------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `CORS_ALLOW_HEADERS` | `str` or `List[str]` | `"*"` | The list of headers that will appear in `access-control-allow-headers`. | +| `CORS_ALWAYS_SEND` | `bool` | `True` | When `True`, will always set a value for `access-control-allow-origin`. When `False`, will only set it if there is an `Origin` header. | +| `CORS_AUTOMATIC_OPTIONS` | `bool` | `True` | When the incoming preflight request is received, whether to automatically set values for `access-control-allow-headers`, `access-control-max-age`, and `access-control-allow-methods` headers. If `False` these values will only be set on routes that are decorated with the `@cors` decorator. | +| `CORS_EXPOSE_HEADERS` | `str` or `List[str]` | `""` | Specific list of headers to be set in `access-control-expose-headers` header. | +| `CORS_MAX_AGE` | `str`, `int`, `timedelta` | `0` | The maximum number of seconds the preflight response may be cached using the `access-control-max-age` header. A falsey value will cause the header to not be set. | +| `CORS_METHODS` | `str` or `List[str]` | `""` | The HTTP methods that the allowed origins can access, as set on the `access-control-allow-methods` header. | +| `CORS_ORIGINS` | `str`, `List[str]`, `re.Pattern` | `"*"` | The origins that are allowed to access the resource, as set on the `access-control-allow-origin` header. | +| `CORS_SEND_WILDCARD` | `bool` | `False` | If `True`, will send the wildcard `*` origin instead of the `origin` request header. | +| `CORS_SUPPORTS_CREDENTIALS` | `bool` | `False` | Whether to set the `access-control-allow-credentials` header. | +| `CORS_VARY_HEADER` | `bool` | `True` | Whether to add `vary` header, when appropriate. | + +_For the sake of brevity, where the above says `List[str]` any instance of a `list`, `set`, `frozenset`, or `tuple` will be acceptable. Alternatively, if the value is a `str`, it can be a comma delimited list._ + +## Route level overrides + +.. column:: + +``` +It may sometimes be necessary to override app-wide settings for a specific route. To allow for this, you can use the `@sanic_ext.cors()` decorator to set different route-specific values. + +The values that can be overridden with this decorator are: + +- `origin` +- `expose_headers` +- `allow_headers` +- `allow_methods` +- `supports_credentials` +- `max_age` +``` + +.. column:: + +```` +```python +from sanic_ext import cors + +app.config.CORS_ORIGINS = "https://foo.com" + +@app.get("/", host="bar.com") +@cors(origin="https://bar.com") +async def hello_world(request): + return text("Hello, world.") +``` +```` diff --git a/guide/content/ko/plugins/sanic-ext/http/methods.md b/guide/content/ko/plugins/sanic-ext/http/methods.md new file mode 100644 index 0000000000..34e841d723 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/http/methods.md @@ -0,0 +1,165 @@ +--- +title: Sanic Extensions - HTTP Methods +--- + +# HTTP Methods + +## Auto-endpoints + +The default behavior is to automatically generate `HEAD` endpoints for all `GET` routes, and `OPTIONS` endpoints for all +routes. Additionally, there is the option to automatically generate `TRACE` endpoints. However, these are not enabled by +default. + +### HEAD + +.. column:: + +``` +- **Configuration**: `AUTO_HEAD` (default `True`) +- **MDN**: [Read more](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) + +A `HEAD` request provides the headers and an otherwise identical response to what a `GET` request would provide. +However, it does not actually return the body. +``` + +.. column:: + +```` +```python +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` + +Given the above route definition, Sanic Extensions will enable `HEAD` responses, as seen here. + +``` +$ curl localhost:8000 --head +HTTP/1.1 200 OK +access-control-allow-origin: * +content-length: 13 +connection: keep-alive +content-type: text/plain; charset=utf-8 +``` +```` + +### OPTIONS + +.. column:: + +``` +- **Configuration**: `AUTO_OPTIONS` (default `True`) +- **MDN**: [Read more](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS) + +`OPTIONS` requests provide the recipient with details about how the client is allowed to communicate with a given +endpoint. +``` + +.. column:: + +```` +```python +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` + +Given the above route definition, Sanic Extensions will enable `OPTIONS` responses, as seen here. + +It is important to note that we also see `access-control-allow-origins` in this example. This is because +the [CORS protection](cors.md) is enabled by default. + +``` +$ curl localhost:8000 -X OPTIONS -i +HTTP/1.1 204 No Content +allow: GET,HEAD,OPTIONS +access-control-allow-origin: * +connection: keep-alive +``` +```` + +.. tip:: + +``` +Even though Sanic Extensions will setup these routes for you automatically, if you decide to manually create an `@app.options` route, it will *not* be overridden. +``` + +### TRACE + +.. column:: + +``` +- **Configuration**: `AUTO_TRACE` (default `False`) +- **MDN**: [Read more](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/TRACE) + +By default, `TRACE` endpoints will **not** be automatically created. However, Sanic Extensions **will allow** you to +create them if you wanted. This is something that is not allowed in vanilla Sanic. +``` + +.. column:: + +```` +```python +@app.route("/", methods=["trace"]) +async def handler(request): + ... +``` + +To enable auto-creation of these endpoints, you must first enable them when extending Sanic. + +```python +from sanic_ext import Extend, Config + +app.extend(config=Config(http_auto_trace=True)) +``` + +Now, assuming you have some endpoints setup, you can trace them as shown here: + +``` +$ curl localhost:8000 -X TRACE +TRACE / HTTP/1.1 +Host: localhost:9999 +User-Agent: curl/7.76.1 +Accept: */* +``` +```` + +.. tip:: + +``` +Setting up `AUTO_TRACE` can be super helpful, especially when your application is deployed behind a proxy since it will help you determine how the proxy is behaving. +``` + +## Additional method support + +Vanilla Sanic allows you to build endpoints with the following HTTP methods: + +- [GET](/en/guide/basics/routing.html#get) +- [POST](/en/guide/basics/routing.html#post) +- [PUT](/en/guide/basics/routing.html#put) +- [HEAD](/en/guide/basics/routing.html#head) +- [OPTIONS](/en/guide/basics/routing.html#options) +- [PATCH](/en/guide/basics/routing.html#patch) +- [DELETE](/en/guide/basics/routing.html#delete) + +See [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) for more. + +.. column:: + +``` +There are, however, two more "standard" HTTP methods: `TRACE` and `CONNECT`. Sanic Extensions will allow you to build +endpoints using these methods, which would otherwise not be allowed. + +It is worth pointing out that this will *NOT* enable convenience methods: `@app.trace` or `@app.connect`. You need to +use `@app.route` as shown in the example here. +``` + +.. column:: + +```` +```python +@app.route("/", methods=["trace", "connect"]) +async def handler(_): + return empty() +``` +```` diff --git a/guide/content/ko/plugins/sanic-ext/injection.md b/guide/content/ko/plugins/sanic-ext/injection.md new file mode 100644 index 0000000000..7e9711d749 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/injection.md @@ -0,0 +1,396 @@ +--- +title: Sanic Extensions - Dependency Injection +--- + +# Dependency Injection + +Dependency injection is a method to add arguments to a route handler based upon the defined function signature. Specifically, it looks at the **type annotations** of the arguments in the handler. This can be useful in a number of cases like: + +- Fetching an object based upon request headers (like the current session user) +- Recasting certain objects into a specific type +- Using the request object to prefetch data +- Auto inject services + +The `Extend` instance has two basic methods on it used for dependency injection: a lower level `add_dependency`, and a higher level `dependency`. + +**Lower level**: `app.ext.add_dependency(...)` + +- `type: Type,`: some unique class that will be the type of the object +- `constructor: Optional[Callable[..., Any]],` (OPTIONAL): a function that will return that type + +**Higher level**: `app.ext.dependency(...)` + +- `obj: Any`: any object that you would like injected +- `name: Optional[str]`: some name that could alternately be used as a reference + +Let's explore some use cases here. + +.. warning:: + +``` +If you used dependency injection prior to v21.12, the lower level API method was called `injection`. It has since been renamed to `add_dependency` and starting in v21.12 `injection` is an alias for `add_dependency`. The `injection` method has been deprecated for removal in v22.6. +``` + +## Basic implementation + +The simplest use case would be simply to recast a value. + +.. column:: + +``` +This could be useful if you have a model that you want to generate based upon the matched path parameters. +``` + +.. column:: + +```` +```python +@dataclass +class IceCream: + flavor: str + + def __str__(self) -> str: + return f"{self.flavor.title()} (Yum!)" + +app.ext.add_dependency(IceCream) + +@app.get("/") +async def ice_cream(request, flavor: IceCream): + return text(f"You chose: {flavor}") +``` + +``` +$ curl localhost:8000/chocolate +You chose Chocolate (Yum!) +``` +```` + +.. column:: + +``` +This works by passing a keyword argument to the constructor of the `type` argument. The previous example is equivalent to this. +``` + +.. column:: + +```` +```python +flavor = IceCream(flavor="chocolate") +``` +```` + +## Additional constructors + +.. column:: + +``` +Sometimes you may need to also pass a constructor. This could be a function, or perhaps even a classmethod that acts as a constructor. In this example, we are creating an injection that will call `Person.create` first. + +Also important to note on this example, we are actually injecting **two (2)** objects! It of course does not need to be this way, but we will inject objects based upon the function signature. +``` + +.. column:: + +```` +```python +@dataclass +class PersonID: + person_id: int + +@dataclass +class Person: + person_id: PersonID + name: str + age: int + + @classmethod + async def create(cls, request: Request, person_id: int): + return cls(person_id=PersonID(person_id), name="noname", age=111) + + +app.ext.add_dependency(Person, Person.create) +app.ext.add_dependency(PersonID) + +@app.get("/person/") +async def person_details( + request: Request, person_id: PersonID, person: Person +): + return text(f"{person_id}\n{person}") +``` + +``` +$ curl localhost:8000/person/123 +PersonID(person_id=123) +Person(person_id=PersonID(person_id=123), name='noname', age=111) +``` +```` + +When a `constructor` is passed to `ext.add_dependency` (like in this example) that will be called. If not, then the object will be created by calling the `type`. A couple of important things to note about passing a `constructor`: + +1. A positional `request: Request` argument is _usually_ expected. See the `Person.create` method above as an example using a `request` and [arbitrary constructors](#arbitrary-constructors) for how to use a callable that does not require a `request`. +2. All matched path parameters are injected as keyword arguments. +3. Dependencies can be chained and nested. Notice how in the previous example the `Person` dataclass has a `PersonID`? That means that `PersonID` will be called first, and that value is added to the keyword arguments when calling `Person.create`. + +## Arbitrary constructors + +.. column:: + +``` +Sometimes you may want to construct your injectable _without_ the `Request` object. This is useful if you have arbitrary classes or functions that create your objects. If the callable does have any required arguments, then they should themselves be injectable objects. + +This is very useful if you have services or other types of objects that should only exist for the lifetime of a single request. For example, you might use this pattern to pull a single connection from your database pool. +``` + +.. column:: + +```` +```python +class Alpha: + ... + +class Beta: + def __init__(self, alpha: Alpha) -> None: + self.alpha = alpha + +app.ext.add_dependency(Alpha) +app.ext.add_dependency(Beta) + +@app.get("/beta") +async def handler(request: Request, beta: Beta): + assert isinstance(beta.alpha, Alpha) +``` +```` + +_Added in v22.9_ + +## Objects from the `Request` + +.. column:: + +```` +Sometimes you may want to extract details from the request and preprocess them. You could, for example, cast the request JSON to a Python object, and then add some additional logic based upon DB queries. + +.. warning:: + + If you plan to use this method, you should note that the injection actually happens *before* Sanic has had a chance to read the request body. The headers should already have been consumed. So, if you do want access to the body, you will need to manually consume as seen in this example. + + ```python + await request.receive_body() + ``` + + + This could be used in cases where you otherwise might: + + - use middleware to preprocess and add something to the `request.ctx` + - use decorators to preprocess and inject arguments into the request handler + + In this example, we are using the `Request` object in the `compile_profile` constructor to run a fake DB query to generate and return a `UserProfile` object. +```` + +.. column:: + +```` +```python +@dataclass +class User: + name: str + +@dataclass +class UserProfile: + user: User + age: int = field(default=0) + email: str = field(default="") + + def __json__(self): + return ujson.dumps( + { + "name": self.user.name, + "age": self.age, + "email": self.email, + } + ) + +async def fake_request_to_db(body): + today = date.today() + email = f'{body["name"]}@something.com'.lower() + difference = today - date.fromisoformat(body["birthday"]) + age = int(difference.days / 365) + return UserProfile( + User(body["name"]), + age=age, + email=email, + ) + +async def compile_profile(request: Request): + await request.receive_body() + profile = await fake_request_to_db(request.json) + return profile + +app.ext.add_dependency(UserProfile, compile_profile) + +@app.patch("/profile") +async def update_profile(request, profile: UserProfile): + return json(profile) +``` + +``` +$ curl localhost:8000/profile -X PATCH -d '{"name": "Alice", "birthday": "2000-01-01"}' +{ + "name":"Alice", + "age":21, + "email":"alice@something.com" +} +``` +```` + +## Injecting services + +It is a common pattern to create things like database connection pools and store them on the `app.ctx` object. This makes them available throughout your application, which is certainly a convenience. One downside, however, is that you no longer have a typed object to work with. You can use dependency injections to fix this. First we will show the concept using the lower level `add_dependency` like we have been using in the previous examples. But, there is a better way using the higher level `dependency` method. + +### The lower level API using `add_dependency` + +.. column:: + +``` +This works very similar to the [last example](#objects-from-the-request) where the goal is the extract something from the `Request` object. In this example, a database object was created on the `app.ctx` instance, and is being returned in the dependency injection constructor. +``` + +.. column:: + +```` +```python +class FakeConnection: + async def execute(self, query: str, **arguments): + return "result" + +@app.before_server_start +async def setup_db(app, _): + app.ctx.db_conn = FakeConnection() + app.ext.add_dependency(FakeConnection, get_db) + +def get_db(request: Request): + return request.app.ctx.db_conn + + + +@app.get("/") +async def handler(request, conn: FakeConnection): + response = await conn.execute("...") + return text(response) +``` +``` +$ curl localhost:8000/ +result +``` +```` + +### The higher level API using `dependency` + +.. column:: + +``` +Since we have an actual *object* that is available when adding the dependency injection, we can use the higher level `dependency` method. This will make the pattern much easier to write. + +This method should always be used when you want to inject something that exists throughout the lifetime of the application instance and is not request specific. It is very useful for services, third party clients, and connection pools since they are not request specific. +``` + +.. column:: + +```` +```python +class FakeConnection: + async def execute(self, query: str, **arguments): + return "result" + +@app.before_server_start +async def setup_db(app, _): + db_conn = FakeConnection() + app.ext.dependency(db_conn) + +@app.get("/") +async def handler(request, conn: FakeConnection): + response = await conn.execute("...") + return text(response) +``` +``` +$ curl localhost:8000/ +result +``` +```` + +## Generic types + +Be carefule when using a [generic type](https://docs.python.org/3/library/typing.html#typing.Generic). The way that Sanic's dependency injection works is by matching the entire type definition. Therefore, `Foo` is not the same as `Foo[str]`. This can be particularly tricky when trying to use the [higher-level `dependency` method](#the-higher-level-api-using-dependency) since the type is inferred. + +.. column:: + +``` +For example, this will **NOT** work as expected since there is no definition for `Test[str]`. +``` + +.. column:: + +```` +```python +import typing +from sanic import Sanic, text + +T = typing.TypeVar("T") + +class Test(typing.Generic[T]): + test: T + +app = Sanic("testapp") +app.ext.dependency(Test()) + +@app.get("/") +def test(request, test: Test[str]): + ... +``` +```` + +.. column:: + +``` +To get this example to work, you will need to add an explicit definition for the type you intend to be injected. +``` + +.. column:: + +```` +```python +import typing +from sanic import Sanic, text + +T = typing.TypeVar("T") + +class Test(typing.Generic[T]): + test: T + +app = Sanic("testapp") +_singleton = Test() +app.ext.add_dependency(Test[str], lambda: _singleton) + +@app.get("/") +def test(request, test: Test[str]): + ... +``` +```` + +## Configuration + +.. column:: + +``` +By default, dependencies will be injected after the `http.routing.after` [signal](../../guide/advanced/signals.md#built-in-signals). Starting in v22.9, you can change this to the `http.handler.before` signal. +``` + +.. column:: + +```` +```python +app.config.INJECTION_SIGNAL = "http.handler.before" +``` +```` + +_Added in v22.9_ diff --git a/guide/content/ko/plugins/sanic-ext/logger.md b/guide/content/ko/plugins/sanic-ext/logger.md new file mode 100644 index 0000000000..76e61e7f0a --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/logger.md @@ -0,0 +1,38 @@ +--- +title: Sanic Extensions - Background logger +--- + +# Background logger + +The background logger requires both `sanic>=22.9` and `sanic-ext>=22.9`. + +You can setup Sanic Extensions to log all of your messages from a background process. This requires that you not be in [single process mode](../../guide/deployment/manager.md#single-process-mode). + +Logging can sometimes be an expensive operation. By pushing all logging off to a background process, you can potentially gain some performance benefits. + +## Setup + +.. column:: + +``` +Out of the box, the background logger is disabled. You will need to opt-in if you would like to use it. +``` + +.. column:: + +```` +```python +app.config.LOGGING = True +``` +```` + +## How does it work + +When enabled, the extension will create a `multiprocessing.Queue`. It will remove all handlers on the [default Sanic loggers](../../guide/best-practices/logging.md) and replace them with a [`QueueHandler`](https://docs.python.org/3/library/logging.handlers.html#queuehandler). When a message is logged, it will be pushed into the queue by the handler, and read by the background process to the log handlers that were originally in place. This means you can still configure logging as normal and it should "just work." + +## Configuration + +| Key | Type | Default | Description | +| ------------------------------------------------------------------------------------- | ------ | ------- | ----------------------------------------------------------------------- | +| LOGGING | `bool` | `False` | Whether to enable this extension. | +| LOGGING_QUEUE_MAX_SIZE | `int` | `4096` | The max size of the queue before messages are rejected. | diff --git a/guide/content/ko/plugins/sanic-ext/openapi.md b/guide/content/ko/plugins/sanic-ext/openapi.md new file mode 100644 index 0000000000..acf33139b1 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/openapi.md @@ -0,0 +1,11 @@ +--- +title: Sanic Extensions - OAS +--- + +# Openapi + +- Adding documentation with decorators +- Documenting CBV +- Using autodoc +- Rendering docs with redoc/swagger +- Validation diff --git a/guide/content/ko/plugins/sanic-ext/openapi/advanced.md b/guide/content/ko/plugins/sanic-ext/openapi/advanced.md new file mode 100644 index 0000000000..b83b905741 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/openapi/advanced.md @@ -0,0 +1,13 @@ +--- +title: Sanic Extensions - Advanced OAS +--- + +# Advanced + +_Documentation in progress_ + +## CBV + +## Blueprints + +## Components diff --git a/guide/content/ko/plugins/sanic-ext/openapi/autodoc.md b/guide/content/ko/plugins/sanic-ext/openapi/autodoc.md new file mode 100644 index 0000000000..e5c962aced --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/openapi/autodoc.md @@ -0,0 +1,154 @@ +--- +title: Sanic Extensions - Auto-documentation +--- + +# Auto-documentation + +To make documenting endpoints easier, Sanic Extensions will use a function's docstring to populate your documentation. + +## Summary and description + +.. column:: + +``` +A function's docstring will be used to create the summary and description. As you can see from this example here, the docstring has been parsed to use the first line as the summary, and the remainder of the string as the description. +``` + +.. column:: + +```` +```python +@app.get("/foo") +async def handler(request, something: str): + """This is a simple foo handler + + It is helpful to know that you could also use **markdown** inside your + docstrings. + + - one + - two + - three""" + return text(">>>") +``` +```json +"paths": { + "/foo": { + "get": { + "summary": "This is a simple foo handler", + "description": "It is helpful to know that you could also use **markdown** inside your
docstrings.

- one
- two
- three", + "responses": { + "default": { + "description": "OK" + } + }, + "operationId": "get_handler" + } + } +} +``` +```` + +## Operation level YAML + +.. column:: + +``` +You can expand upon this by adding valid OpenAPI YAML to the docstring. Simply add a line that contains `openapi:`, followed by your YAML. + +The `---` shown in the example is *not* necessary. It is just there to help visually identify the YAML as a distinct section of the docstring. +``` + +.. column:: + +```` +```python +@app.get("/foo") +async def handler(request, something: str): + """This is a simple foo handler + + Now we will add some more details + + openapi: + --- + operationId: fooDots + tags: + - one + - two + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: Just some dots + """ + return text("...") +``` +```json +"paths": { + "/foo": { + "get": { + "operationId": "fooDots", + "summary": "This is a simple foo handler", + "description": "Now we will add some more details", + "tags": [ + "one", + "two" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "How many items to return at one time (max 100)", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Just some dots" + } + } + } + } +} +``` +```` + +.. note:: + +``` +When both YAML documentation and decorators are used, it is the content from the decorators that will take priority when generating the documentation. +``` + +## Excluding docstrings + +.. column:: + +``` +Sometimes a function may contain a docstring that is not meant to be consumed inside the documentation. + +**Option 1**: Globally turn off auto-documentation `app.config.OAS_AUTODOC = False` + +**Option 2**: Disable it for the single handler with the `@openapi.no_autodoc` decorator +``` + +.. column:: + +```` +```python +@app.get("/foo") +@openapi.no_autodoc +async def handler(request, something: str): + """This is a docstring about internal info only. Do not parse it. + """ + return text("...") +``` +```` diff --git a/guide/content/ko/plugins/sanic-ext/openapi/basics.md b/guide/content/ko/plugins/sanic-ext/openapi/basics.md new file mode 100644 index 0000000000..e461fa2c49 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/openapi/basics.md @@ -0,0 +1,84 @@ +--- +title: Sanic Extensions - Basic OAS +--- + +# Basics + +.. note:: + +``` +The OpenAPI implementation in Sanic Extensions is based upon the OAS3 implementation from [`sanic-openapi`](https://github.com/sanic-org/sanic-openapi). In fact, Sanic Extensions is in a large way the successor to that project, which entered maintenance mode upon the release of Sanic Extensions. If you were previously using OAS3 with `sanic-openapi` you should have an easy path to upgrading to Sanic Extensions. Unfortunately, this project does *NOT* support the OAS2 specification. +``` + +.. column:: + +``` +Out of the box, Sanic Extensions provides automatically generated API documentation using the [v3.0 OpenAPI specification](https://swagger.io/specification/). There is nothing special that you need to do +``` + +.. column:: + +```` +```python +from sanic import Sanic + +app = Sanic("MyApp") + +# Add all of your views +``` +```` + +After doing this, you will now have beautiful documentation already generated for you based upon your existing application: + +- [http://localhost:8000/docs](http://localhost:8000/docs) +- [http://localhost:8000/docs/redoc](http://localhost:8000/docs/redoc) +- [http://localhost:8000/docs/swagger](http://localhost:8000/docs/swagger) + +Checkout the [section on configuration](../configuration.md) to learn about changing the routes for the docs. You can also turn off one of the two UIs, and customize which UI will be available on the `/docs` route. + +.. column:: + +``` +Using [Redoc](https://github.com/Redocly/redoc) + +![Redoc](/assets/images/sanic-ext-redoc.png) +``` + +.. column:: + +``` +or [Swagger UI](https://github.com/swagger-api/swagger-ui) + +![Swagger UI](/assets/images/sanic-ext-swagger.png) +``` + +## Changing specification metadata + +.. column:: + +``` +If you want to change any of the metada, you should use the `describe` method. + +In this example `dedent` is being used with the `description` argument to make multi-line strings a little cleaner. This is not necessary, you can pass any string value here. +``` + +.. column:: + +```` +```python +from textwrap import dedent + +app.ext.openapi.describe( + "Testing API", + version="1.2.3", + description=dedent( + """ + # Info + This is a description. It is a good place to add some _extra_ doccumentation. + + **MARKDOWN** is supported. + """ + ), +) +``` +```` diff --git a/guide/content/ko/plugins/sanic-ext/openapi/decorators.md b/guide/content/ko/plugins/sanic-ext/openapi/decorators.md new file mode 100644 index 0000000000..63af9849ef --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/openapi/decorators.md @@ -0,0 +1,520 @@ +--- +title: Sanic Extensions - OAS Decorators +--- + +# Decorators + +The primary mechanism for adding content to your schema is by decorating your endpoints. If you have +used `sanic-openapi` in the past, this should be familiar to you. The decorators and their arguments match closely +the [OAS v3.0 specification](https://swagger.io/specification/). + +.. column:: + +``` +All of the examples show will wrap around a route definition. When you are creating these, you should make sure that +your Sanic route decorator (`@app.route`, `@app.get`, etc) is the outermost decorator. That is to say that you should +put that first and then one or more of the below decorators after. +``` + +.. column:: + +```` +```python +from sanic_ext import openapi + +@app.get("/path/to/") +@openapi.summary("This is a summary") +@openapi.description("This is a description") +async def handler(request, something: str): + ... +``` +```` + +.. column:: + +``` +You will also see a lot of the below examples reference a model object. For the sake of simplicity, the examples will +use `UserProfile` that will look like this. The point is that it can be any well-typed class. You could easily imagine +this being a `dataclass` or some other kind of model object. +``` + +.. column:: + +```` +```python +class UserProfile: + name: str + age: int + email: str +``` +```` + +## Definition decorator + +### `@openapi.definition` + +The `@openapi.definition` decorator allows you to define all parts of an operations on a path at once. It is an omnibums +decorator in that it has the same capabilities to create operation definitions as the rest of the decorators. Using +multiple field-specific decorators or a single decorator is a style choice for you the developer. + +The fields are purposely permissive in accepting multiple types to make it easiest for you to define your operation. + +**Arguments** + +| Field | Type | +| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `body` | **dict, RequestBody, _YourModel_** | +| `deprecated` | **bool** | +| `description` | **str** | +| `document` | **str, ExternalDocumentation** | +| `exclude` | **bool** | +| `operation` | **str** | +| `parameter` | **str, dict, Parameter, [str], [dict], [Parameter]** | +| `response` | **dict, Response, _YourModel_, [dict], [Response]** | +| `summary` | **str** | +| `tag` | **str, Tag, [str], [Tag]** | +| `secured` | **Dict[str, Any]** | + +**Examples** + +.. column:: + +```` +```python +@openapi.definition( + body=RequestBody(UserProfile, required=True), + summary="User profile update", + tag="one", + response=[Success, Response(Failure, status=400)], +) +``` +```` + +.. column:: + +_See below examples for more examples. Any of the values for the below decorators can be used in the corresponding +keyword argument._ + +## Field-specific decorators + +All the following decorators are based on `@openapi` + +### body + +**Arguments** + +| Field | Type | +| ----------- | ---------------------------------- | +| **content** | **_YourModel_, dict, RequestBody** | + +**Examples** + +.. column:: + +```` +```python +@openapi.body(UserProfile) +``` + +```python +@openapi.body({"application/json": UserProfile}) +``` + +```python +@openapi.body(RequestBody({"application/json": UserProfile})) +``` +```` + +.. column:: + +```` +```python +@openapi.body({"content": UserProfile}) +``` + +```python +@openapi.body(RequestBody(UserProfile)) +``` + +```python +@openapi.body({"application/json": {"description": ...}}) +``` +```` + +### deprecated + +**Arguments** + +_None_ + +**Examples** + +.. column:: + +```` +```python +@openapi.deprecated() +``` +```` + +.. column:: + +```` +```python +@openapi.deprecated +``` +```` + +### description + +**Arguments** + +| Field | Type | +| ------ | ------- | +| `text` | **str** | + +**Examples** + +.. column:: + +```` +```python +@openapi.description( + """This is a **description**. + +## You can use `markdown` + +- And +- make +- lists. +""" +) +``` +```` + +.. column:: + +### document + +**Arguments** + +| Field | Type | +| ------------- | ------- | +| `url` | **str** | +| `description` | **str** | + +**Examples** + +.. column:: + +```` +```python +@openapi.document("http://example.com/docs") +``` +```` + +.. column:: + +```` +```python +@openapi.document(ExternalDocumentation("http://example.com/more")) +``` +```` + +### exclude + +Can be used on route definitions like all of the other decorators, or can be called on a Blueprint + +**Arguments** + +| Field | Type | Default | +| ------ | ------------- | -------- | +| `flag` | **bool** | **True** | +| `bp` | **Blueprint** | | + +**Examples** + +.. column:: + +```` +```python +@openapi.exclude() +``` +```` + +.. column:: + +```` +```python +openapi.exclude(bp=some_blueprint) +``` +```` + +### operation + +Sets the operation ID. + +**Arguments** + +| Field | Type | +| ------ | ------- | +| `name` | **str** | + +**Examples** + +.. column:: + +```` +```python +@openapi.operation("doNothing") +``` +```` + +.. column:: + +**Arguments** + +| Field | Type | Default | +| ---------- | ----------------------------------------- | ----------- | +| `name` | **str** | | +| `schema` | _**type**_ | **str** | +| `location` | **"query", "header", "path" or "cookie"** | **"query"** | + +**Examples** + +.. column:: + +```` +```python +@openapi.parameter("thing") +``` + +```python +@openapi.parameter(parameter=Parameter("foobar", deprecated=True)) +``` +```` + +.. column:: + +```` +```python +@openapi.parameter("Authorization", str, "header") +``` + +```python +@openapi.parameter("thing", required=True, allowEmptyValue=False) +``` +```` + +### response + +**Arguments** + +If using a `Response` object, you should not pass any other arguments. + +| Field | Type | +| ------------- | ----------------------------- | +| `status` | **int** | +| `content` | **_type_, _YourModel_, dict** | +| `description` | **str** | +| `response` | **Response** | + +**Examples** + +.. column:: + +```` +```python +@openapi.response(200, str, "This is endpoint returns a string") +``` + +```python +@openapi.response(200, {"text/plain": str}, "...") +``` + +```python +@openapi.response(response=Response(UserProfile, description="...")) +``` + +```python +@openapi.response( + response=Response( + { + "application/json": UserProfile, + }, + description="...", + status=201, + ) +) +``` +```` + +.. column:: + +```` +```python +@openapi.response(200, UserProfile, "...") +``` + +```python +@openapi.response( + 200, + { + "application/json": UserProfile, + }, + "Description...", +) +``` +```` + +### summary + +**Arguments** + +| Field | Type | +| ------ | ------- | +| `text` | **str** | + +**Examples** + +.. column:: + +```` +```python +@openapi.summary("This is an endpoint") +``` +```` + +.. column:: + +### tag + +**Arguments** + +| Field | Type | +| ------- | ------------ | +| `*args` | **str, Tag** | + +**Examples** + +.. column:: + +```` +```python +@openapi.tag("foo") +``` +```` + +.. column:: + +```` +```python +@openapi.tag("foo", Tag("bar")) +``` +```` + +### secured + +**Arguments** + +| Field | Type | +| ----------------- | --------------------------------------------------------------------------- | +| `*args, **kwargs` | **str, Dict[str, Any]** | + +**Examples** + +.. column:: + +```` +```python +@openapi.secured() +``` +```` + +.. column:: + +.. column:: + +```` +```python +@openapi.secured("foo") +``` +```` + +.. column:: + +```` +```python +@openapi.secured("token1", "token2") +``` +```` + +.. column:: + +```` +```python +@openapi.secured({"my_api_key": []}) +``` +```` + +.. column:: + +```` +```python +@openapi.secured(my_api_key=[]) +``` +```` + +Do not forget to use `add_security_scheme`. See [security](./security.md) for more details. +\`\` + +## Integration with Pydantic + +Pydantic models have the ability to [generate OpenAPI schema](https://pydantic-docs.helpmanual.io/usage/schema/). + +.. column:: + +``` +To take advantage of Pydantic model schema generation, pass the output in place of the schema. +``` + +.. column:: + +```` +```python +from sanic import Sanic, json +from sanic_ext import validate, openapi +from pydantic import BaseModel, Field + +@openapi.component +class Item(BaseModel): + name: str + description: str = None + price: float + tax: float = None + +class ItemList(BaseModel): + items: List[Item] + +app = Sanic("test") + +@app.get("/") +@openapi.definition( + body={ + "application/json": ItemList.schema( + ref_template="#/components/schemas/{model}" + ) + }, +) +async def get(request): + return json({}) +``` +```` + +.. note:: + +``` +It is important to set that `ref_template`. By default Pydantic will select a template that is not standard OAS. This will cause the schema to not be found when generating the final document. +``` + +_Added in v22.9_ diff --git a/guide/content/ko/plugins/sanic-ext/openapi/security.md b/guide/content/ko/plugins/sanic-ext/openapi/security.md new file mode 100644 index 0000000000..416e794bf5 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/openapi/security.md @@ -0,0 +1,110 @@ +--- +title: Sanic Extensions - OAS Security Schemes +--- + +# Security Schemes + +To document authentication schemes, there are two steps. + +_Security is only available starting in v21.12.2_ + +## Document the scheme + +.. column:: + +```` +The first thing that you need to do is define one or more security schemes. The basic pattern will be to define it as: + +```python +add_security_scheme("", "") +``` + +The `type` should correspond to one of the allowed security schemes: `"apiKey"`, `"http"`, `"oauth2"`, `"openIdConnect"`. You can then pass appropriate keyword arguments as allowed by the specification. + +You should consult the [OpenAPI Specification](https://swagger.io/specification/) for details on what values are appropriate. +```` + +.. column:: + +```` +```python +app.ext.openapi.add_security_scheme("api_key", "apiKey") +app.ext.openapi.add_security_scheme( + "token", + "http", + scheme="bearer", + bearer_format="JWT", +) +app.ext.openapi.add_security_scheme("token2", "http") +app.ext.openapi.add_security_scheme( + "oldschool", + "http", + scheme="basic", +) +app.ext.openapi.add_security_scheme( + "oa2", + "oauth2", + flows={ + "implicit": { + "authorizationUrl": "http://example.com/auth", + "scopes": { + "on:two": "something", + "three:four": "something else", + "threefour": "something else...", + }, + } + }, +) +``` +```` + +## Document the endpoints + +.. column:: + +``` +There are two options, document _all_ endpoints. +``` + +.. column:: + +```` +```python +app.ext.openapi.secured() +app.ext.openapi.secured("token") +``` +```` + +.. column:: + +``` +Or, document only specific routes. +``` + +.. column:: + +```` +```python +@app.route("/one") +async def handler1(request): + """ + openapi: + --- + security: + - foo: [] + """ + +@app.route("/two") +@openapi.secured("foo") +@openapi.secured({"bar": []}) +@openapi.secured(baz=[]) +async def handler2(request): + ... + +@app.route("/three") +@openapi.definition(secured="foo") +@openapi.definition(secured={"bar": []}) +async def handler3(request): + ... +``` +```` diff --git a/guide/content/ko/plugins/sanic-ext/openapi/ui.md b/guide/content/ko/plugins/sanic-ext/openapi/ui.md new file mode 100644 index 0000000000..6e72718396 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/openapi/ui.md @@ -0,0 +1,30 @@ +--- +title: Sanic Extensions - OAS UI +--- + +# UI + +Sanic Extensions comes with both Redoc and Swagger interfaces. You have a choice to use one, or both of them. Out of the box, the following endpoints are setup for you, with the bare `/docs` displaying Redoc. + +- `/docs` +- `/docs/openapi.json` +- `/docs/redoc` +- `/docs/swagger` +- `/docs/openapi-config` + +## Config options + +| **Key** | **Type** | **Default** | **Desctiption** | +| -------------------------- | --------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `OAS_IGNORE_HEAD` | `bool` | `True` | Whether to display `HEAD` endpoints. | +| `OAS_IGNORE_OPTIONS` | `bool` | `True` | Whether to display `OPTIONS` endpoints. | +| `OAS_PATH_TO_REDOC_HTML` | `Optional[str]` | `None` | Path to HTML to override the default Redoc HTML | +| `OAS_PATH_TO_SWAGGER_HTML` | `Optional[str]` | `None` | Path to HTML to override the default Swagger HTML | +| `OAS_UI_DEFAULT` | `Optional[str]` | `"redoc"` | Can be set to `redoc` or `swagger`. Controls which UI to display on the base route. If set to `None`, then the base route will not be setup. | +| `OAS_UI_REDOC` | `bool` | `True` | Whether to enable Redoc UI. | +| `OAS_UI_SWAGGER` | `bool` | `True` | Whether to enable Swagger UI. | +| `OAS_URI_TO_CONFIG` | `str` | `"/openapi-config"` | URI path to the OpenAPI config used by Swagger | +| `OAS_URI_TO_JSON` | `str` | `"/openapi.json"` | URI path to the JSON document. | +| `OAS_URI_TO_REDOC` | `str` | `"/redoc"` | URI path to Redoc. | +| `OAS_URI_TO_SWAGGER` | `str` | `"/swagger"` | URI path to Swagger. | +| `OAS_URL_PREFIX` | `str` | `"/docs"` | URL prefix to use for the Blueprint for OpenAPI docs. | diff --git a/guide/content/ko/plugins/sanic-ext/templating/html5tagger.md b/guide/content/ko/plugins/sanic-ext/templating/html5tagger.md new file mode 100644 index 0000000000..0321dbdb63 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/templating/html5tagger.md @@ -0,0 +1,7 @@ +--- +title: Sanic Extensions - html5tagger +--- + +# Coming soon + +See [sanic-org/html5tagger on GitHub](https://github.com/sanic-org/html5tagger/) diff --git a/guide/content/ko/plugins/sanic-ext/templating/jinja.md b/guide/content/ko/plugins/sanic-ext/templating/jinja.md new file mode 100644 index 0000000000..1f3cefbb22 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/templating/jinja.md @@ -0,0 +1,171 @@ +--- +title: Sanic Extensions - Jinja +--- + +# Templating + +Sanic Extensions can easily help you integrate templates into your route handlers. + +## Dependencies + +**Currently, we only support [Jinja](https://github.com/pallets/jinja/).** + +[Read the Jinja docs first](https://jinja.palletsprojects.com/en/3.1.x/) if you are unfamiliar with how to create templates. + +Sanic Extensions will automatically setup and load Jinja for you if it is installed in your environment. Therefore, the only setup that you need to do is install Jinja: + +``` +pip install Jinja2 +``` + +## Rendering a template from a file + +There are three (3) ways for you: + +1. Using a decorator to pre-load the template file +2. Returning a rendered `HTTPResponse` object +3. Hybrid pattern that creates a `LazyResponse` + +Let's imagine you have a file called `./templates/foo.html`: + +```html + + + + + My Webpage + + + +

Hello, world!!!!

+
    + {% for item in seq %} +
  • {{ item }}
  • + {% endfor %} +
+ + + +``` + +Let's see how you could render it with Sanic + Jinja. + +### Option 1 - as a decorator + +.. column:: + +``` +The benefit of this approach is that the templates can be predefined at startup time. This will mean that less fetching needs to happen in the handler, and should therefore be the fastest option. +``` + +.. column:: + +```` +```python +@app.get("/") +@app.ext.template("foo.html") +async def handler(request: Request): + return {"seq": ["one", "two"]} +``` +```` + +### Option 2 - as a return object + +.. column:: + +``` +This is meant to mimic the `text`, `json`, `html`, `file`, etc pattern of core Sanic. It will allow the most customization to the response object since it has direct control of it. Just like in other `HTTPResponse` objects, you can control headers, cookies, etc. +``` + +.. column:: + +```` +```python +from sanic_ext import render + +@app.get("/alt") +async def handler(request: Request): + return await render( + "foo.html", context={"seq": ["three", "four"]}, status=400 + ) +``` +```` + +### Option 3 - hybrid/lazy + +.. column:: + +``` +In this approach, the template is defined up front and not inside the handler (for performance). Then, the `render` function returns a `LazyResponse` that can be used to build a proper `HTTPResponse` inside the decorator. +``` + +.. column:: + +```` +```python +from sanic_ext import render + +@app.get("/") +@app.ext.template("foo.html") +async def handler(request: Request): + return await render(context={"seq": ["five", "six"]}, status=400) +``` +```` + +## Rendering a template from a string + +.. column:: + +``` +Sometimes you may want to write (or generate) your template inside of Python code and _not_ read it from an HTML file. In this case, you can still use the `render` function we saw above. Just use `template_source`. +``` + +.. column:: + +```` +```python +from sanic_ext import render +from textwrap import dedent + +@app.get("/") +async def handler(request): + template = dedent(""" + + + + + My Webpage + + + +

Hello, world!!!!

+
    + {% for item in seq %} +
  • {{ item }}
  • + {% endfor %} +
+ + + + """) + return await render( + template_source=template, + context={"seq": ["three", "four"]}, + app=app, + ) +``` +```` + +.. note:: + +``` +In this example, we use `textwrap.dedent` to remove the whitespace in the beginning of each line of the multi-line string. It is not necessary, but just a nice touch to keep both the code and the generated source clean. +``` + +## Development and auto-reload + +If auto-reload is turned on, then changes to your template files should trigger a reload of the server. + +## Configuration + +See `templating_enable_async` and `templating_path_to_templates` in [settings](./configuration.md#settings). diff --git a/guide/content/ko/plugins/sanic-ext/validation.md b/guide/content/ko/plugins/sanic-ext/validation.md new file mode 100644 index 0000000000..504c931a88 --- /dev/null +++ b/guide/content/ko/plugins/sanic-ext/validation.md @@ -0,0 +1,201 @@ +--- +title: Sanic Extensions - Validation +--- + +# Validation + +One of the most commonly implemented features of a web application is user-input validation. For obvious reasons, this is not only a security issue, but also just plain good practice. You want to make sure your data conforms to expectations, and throw a `400` response when it does not. + +## Implementation + +### Validation with Dataclasses + +With the introduction of [Data Classes](https://docs.python.org/3/library/dataclasses.html), Python made it super simple to create objects that meet a defined schema. However, the standard library only supports type checking validation, **not** runtime validation. Sanic Extensions adds the ability to do runtime validations on incoming requests using `dataclasses` out of the box. If you also have either `pydantic` or `attrs` installed, you can alternatively use one of those libraries. + +.. column:: + +``` +First, define a model. +``` + +.. column:: + +```` +```python +@dataclass +class SearchParams: + q: str +``` +```` + +.. column:: + +``` +Then, attach it to your route +``` + +.. column:: + +```` +```python +from sanic_ext import validate + +@app.route("/search") +@validate(query=SearchParams) +async def handler(request, query: SearchParams): + return json(asdict(query)) +``` +```` + +.. column:: + +``` +You should now have validation on the incoming request. +``` + +.. column:: + +```` +``` +$ curl localhost:8000/search +⚠️ 400 — Bad Request +==================== +Invalid request body: SearchParams. Error: missing a required argument: 'q' +``` +``` +$ curl localhost:8000/search\?q=python +{"q":"python"} +``` +```` + +### Validation with Pydantic + +You can use Pydantic models also. + +.. column:: + +``` +First, define a model. +``` + +.. column:: + +```` +```python +class Person(BaseModel): + name: str + age: int +``` +```` + +.. column:: + +``` +Then, attach it to your route +``` + +.. column:: + +```` +```python +from sanic_ext import validate + +@app.post("/person") +@validate(json=Person) +async def handler(request, body: Person): + return json(body.dict()) +``` +```` + +.. column:: + +``` +You should now have validation on the incoming request. +``` + +.. column:: + +```` +``` +$ curl localhost:8000/person -d '{"name": "Alice", "age": 21}' -X POST +{"name":"Alice","age":21} +``` +```` + +### Validation with Attrs + +You can use Attrs also. + +.. column:: + +``` +First, define a model. +``` + +.. column:: + +```` +```python +@attrs.define +class Person: + name: str + age: int + +``` +```` + +.. column:: + +``` +Then, attach it to your route +``` + +.. column:: + +```` +```python +from sanic_ext import validate + +@app.post("/person") +@validate(json=Person) +async def handler(request, body: Person): + return json(attrs.asdict(body)) +``` +```` + +.. column:: + +``` +You should now have validation on the incoming request. +``` + +.. column:: + +```` +``` +$ curl localhost:8000/person -d '{"name": "Alice", "age": 21}' -X POST +{"name":"Alice","age":21} +``` +```` + +## What can be validated? + +The `validate` decorator can be used to validate incoming user data from three places: JSON body data (`request.json`), form body data (`request.form`), and query parameters (`request.args`). + +.. column:: + +``` +As you might expect, you can attach your model using the keyword arguments of the decorator. +``` + +.. column:: + +```` +```python +@validate( + json=ModelA, + query=ModelB, + form=ModelC, +) +``` +```` diff --git a/guide/content/ko/plugins/sanic-testing/clients.md b/guide/content/ko/plugins/sanic-testing/clients.md new file mode 100644 index 0000000000..7dfa3ea8c7 --- /dev/null +++ b/guide/content/ko/plugins/sanic-testing/clients.md @@ -0,0 +1,152 @@ +--- +title: Sanic Testing - Test Clients +--- + +# Test Clients + +There are three different test clients available to you, each of them presents different capabilities. + +## Regular sync client: `SanicTestClient` + +The `SanicTestClient` runs an actual version of the Sanic Server on your local network to run its tests. Each time it calls an endpoint it will spin up a version of the application and bind it to a socket on the host OS. Then, it will use `httpx` to make calls directly to that application. + +This is the typical way that Sanic applications are tested. + +.. column:: + +``` +Once installing Sanic Testing, the regular `SanicTestClient` can be used without further setup. This is because Sanic does the leg work for you under the hood. +``` + +.. column:: + +```` +```python +app.test_client.get("/path/to/endpoint") +``` +```` + +.. column:: + +``` +However, you may find it desirable to instantiate the client yourself. +``` + +.. column:: + +```` +```python +from sanic_testing.testing import SanicTestClient + +test_client = SanicTestClient(app) +test_client.get("/path/to/endpoint") +``` +```` + +.. column:: + +``` +A third option for starting the test client is to use the `TestManager`. This is a convenience object that sets up both the `SanicTestClient` and the `SanicASGITestClient`. +``` + +.. column:: + +```` +```python +from sanic_testing import TestManager + +mgr = TestManager(app) +app.test_client.get("/path/to/endpoint") +# or +mgr.test_client.get("/path/to/endpoint") +``` +```` + +You can make a request by using one of the following methods + +- `SanicTestClient.get` +- `SanicTestClient.post` +- `SanicTestClient.put` +- `SanicTestClient.patch` +- `SanicTestClient.delete` +- `SanicTestClient.options` +- `SanicTestClient.head` +- `SanicTestClient.websocket` +- `SanicTestClient.request` + +You can use these methods _almost_ identically as you would when using `httpx`. Any argument that you would pass to `httpx` will be accepted, **with one caveat**: If you are using `test_client.request` and want to manually specify the HTTP method, you should use: `http_method`: + +```python +test_client.request("/path/to/endpoint", http_method="get") +``` + +## ASGI async client: `SanicASGITestClient` + +Unlike the `SanicTestClient` that spins up a server on every request, the `SanicASGITestClient` does not. Instead it makes use of the `httpx` library to execute Sanic as an ASGI application to reach inside and execute the route handlers. + +.. column:: + +``` +This test client provides all of the same methods and generally works as the `SanicTestClient`. The only difference is that you will need to add an `await` to each call: +``` + +.. column:: + +```` +```python +await app.test_client.get("/path/to/endpoint") +``` +```` + +The `SanicASGITestClient` can be used in the exact same three ways as the `SanicTestClient`. + +.. note:: + +``` +The `SanicASGITestClient` does not need to only be used with ASGI applications. The same way that the `SanicTestClient` does not need to only test sync endpoints. Both of these clients are capable of testing *any* Sanic application. +``` + +## Persistent service client: `ReusableClient` + +This client works under a similar premise as the `SanicTestClient` in that it stands up an instance of your application and makes real HTTP requests to it. However, unlike the `SanicTestClient`, when using the `ReusableClient` you control the lifecycle of the application. + +That means that every request **does not** start a new web server. Instead you will start the server and stop it as needed and can make multiple requests to the same running instance. + +.. column:: + +``` +Unlike the other two clients, you **must** instantiate this client for use: +``` + +.. column:: + +```` +```python +from sanic_testing.reusable import ReusableClient + +client = ReusableClient(app) +``` +```` + +.. column:: + +``` +Once created, you will use the client inside of a context manager. Once outside of the scope of the manager, the server will shutdown. +``` + +.. column:: + +```` +```python +from sanic_testing.reusable import ReusableClient + +def test_multiple_endpoints_on_same_server(app): + client = ReusableClient(app) + with client: + _, response = client.get("/path/to/1") + assert response.status == 200 + + _, response = client.get("/path/to/2") + assert response.status == 200 +``` +```` diff --git a/guide/content/ko/plugins/sanic-testing/getting-started.md b/guide/content/ko/plugins/sanic-testing/getting-started.md new file mode 100644 index 0000000000..5dcdb0e38f --- /dev/null +++ b/guide/content/ko/plugins/sanic-testing/getting-started.md @@ -0,0 +1,85 @@ +--- +title: Sanic Testing - Getting Started +--- + +# Getting Started + +Sanic Testing is the _official_ testing client for Sanic. Its primary use is to power the tests of the Sanic project itself. However, it is also meant as an easy-to-use client for getting your API tests up and running quickly. + +## Minimum requirements + +- **Python**: 3.7+ +- **Sanic**: 21.3+ + +Versions of Sanic older than 21.3 have this module integrated into Sanic itself as `sanic.testing`. + +## Install + +Sanic Testing can be installed from PyPI: + +``` +pip install sanic-testing +``` + +## Basic Usage + +As long as the `sanic-testing` package is in the environment, there is nothing you need to do to start using it. + +### Writing a sync test + +In order to use the test client, you just need to access the property `test_client` on your application instance: + +```python +import pytest +from sanic import Sanic, response + +@pytest.fixture +def app(): + sanic_app = Sanic("TestSanic") + + @sanic_app.get("/") + def basic(request): + return response.text("foo") + + return sanic_app + +def test_basic_test_client(app): + request, response = app.test_client.get("/") + + assert request.method.lower() == "get" + assert response.body == b"foo" + assert response.status == 200 +``` + +### Writing an async test + +In order to use the async test client in `pytest`, you should install the `pytest-asyncio` plugin. + +``` +pip install pytest-asyncio +``` + +You can then create an async test and use the ASGI client: + +```python +import pytest +from sanic import Sanic, response + +@pytest.fixture +def app(): + sanic_app = Sanic(__name__) + + @sanic_app.get("/") + def basic(request): + return response.text("foo") + + return sanic_app + +@pytest.mark.asyncio +async def test_basic_asgi_client(app): + request, response = await app.asgi_client.get("/") + + assert request.method.lower() == "get" + assert response.body == b"foo" + assert response.status == 200 +``` diff --git a/guide/content/ko/release-notes/2021/v21.12.md b/guide/content/ko/release-notes/2021/v21.12.md new file mode 100644 index 0000000000..4d5da222a3 --- /dev/null +++ b/guide/content/ko/release-notes/2021/v21.12.md @@ -0,0 +1,531 @@ +--- +title: Version 21.12 (LTS) +--- + +# Version 21.12 (LTS) + +.. toc:: + +## Introduction + +This is the final release of the version 21 [release cycle](../../org/policies.md#release-schedule). Version 21 will now enter long-term support and will be supported for two years until December 2023. + +## What to know + +More details in the [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html). Notable new or breaking features, and what to upgrade... + +### Strict application and blueprint names + +In [v21.6](./v21.6.md#stricter-application-and-blueprint-names-and-deprecation) application and blueprint names were required to conform to a new set of restrictions. That change is now being enforced at startup time. + +Names **must**: + +1. Only use alphanumeric characters (`a-zA-Z0-9`) +2. May contain a hyphen (`-`) or an underscore (`_`) +3. Must begin with a letter or underscore (`a-zA-Z_`) + +### Strict application and blueprint properties + +The old leniency to allow directly setting properties of a `Sanic` or `Blueprint` object was deprecated and no longer allowed. You must use the `ctx` object. + +```python +app = Sanic("MyApp") +app.ctx.db = Database() +``` + +### Removals + +The following deprecated features no longer exist: + +- `sanic.exceptions.abort` +- `sanic.views.CompositionView` +- `sanic.response.StreamingHTTPResponse` + +### Upgrade your streaming responses (if not already) + +The `sanic.response.stream` response method has been **deprecated** and will be removed in v22.6. If you are sill using an old school streaming response, please upgrade it. + +**OLD - Deprecated** + +```python +async def sample_streaming_fn(response): + await response.write("foo,") + await response.write("bar") + +@app.route("/") +async def test(request: Request): + return stream(sample_streaming_fn, content_type="text/csv") +``` + +**Current** + +```python +async def sample_streaming_fn(response): + await response.write("foo,") + await response.write("bar") + +@app.route("/") +async def test(request: Request): + response = await request.respond(content_type="text/csv") + await response.send("foo,") + await response.send("bar") +``` + +### CLI overhaul and MOTD (Message of the Day) + +The Sanic CLI has received a fairly extensive upgrade. It adds a bunch of new features to make it on par with `app.run()`. It also includes a new MOTD display to provide quick, at-a-glance highlights about your running environment. The MOTD is TTY-aware, and therefore will be less verbose in server logs. It is mainly intended as a convenience during application development. + +``` +$ sanic --help +usage: sanic [-h] [--version] [--factory] [-s] [-H HOST] [-p PORT] [-u UNIX] [--cert CERT] [--key KEY] [--tls DIR] [--tls-strict-host] + [-w WORKERS | --fast] [--access-logs | --no-access-logs] [--debug] [-d] [-r] [-R PATH] [--motd | --no-motd] [-v] + [--noisy-exceptions | --no-noisy-exceptions] + module + + ▄███ █████ ██ ▄█▄ ██ █ █ ▄██████████ + ██ █ █ █ ██ █ █ ██ + ▀███████ ███▄ ▀ █ █ ██ ▄ █ ██ + ██ █████████ █ ██ █ █ ▄▄ + ████ ████████▀ █ █ █ ██ █ ▀██ ███████ + + To start running a Sanic application, provide a path to the module, where + app is a Sanic() instance: + + $ sanic path.to.server:app + + Or, a path to a callable that returns a Sanic() instance: + + $ sanic path.to.factory:create_app --factory + + Or, a path to a directory to run as a simple HTTP server: + + $ sanic ./path/to/static --simple + +Required +======== + Positional: + module Path to your Sanic app. Example: path.to.server:app + If running a Simple Server, path to directory to serve. Example: ./ + +Optional +======== + General: + -h, --help show this help message and exit + --version show program's version number and exit + + Application: + --factory Treat app as an application factory, i.e. a () -> callable + -s, --simple Run Sanic as a Simple Server, and serve the contents of a directory + (module arg should be a path) + + Socket binding: + -H HOST, --host HOST Host address [default 127.0.0.1] + -p PORT, --port PORT Port to serve on [default 8000] + -u UNIX, --unix UNIX location of unix socket + + TLS certificate: + --cert CERT Location of fullchain.pem, bundle.crt or equivalent + --key KEY Location of privkey.pem or equivalent .key file + --tls DIR TLS certificate folder with fullchain.pem and privkey.pem + May be specified multiple times to choose multiple certificates + --tls-strict-host Only allow clients that send an SNI matching server certs + + Worker: + -w WORKERS, --workers WORKERS Number of worker processes [default 1] + --fast Set the number of workers to max allowed + --access-logs Display access logs + --no-access-logs No display access logs + + Development: + --debug Run the server in debug mode + -d, --dev Currently is an alias for --debug. But starting in v22.3, + --debug will no longer automatically trigger auto_restart. + However, --dev will continue, effectively making it the + same as debug + auto_reload. + -r, --reload, --auto-reload Watch source directory for file changes and reload on changes + -R PATH, --reload-dir PATH Extra directories to watch and reload on changes + + Output: + --motd Show the startup display + --no-motd No show the startup display + -v, --verbosity Control logging noise, eg. -vv or --verbosity=2 [default 0] + --noisy-exceptions Output stack traces for all exceptions + --no-noisy-exceptions No output stack traces for all exceptions +``` + +### Server running modes and changes coming to `debug` + +There are now two running modes: `DEV` and `PRODUCTION`. By default, Sanic server will run under `PRODUCTION` mode. This is intended for deployments. + +Currently, `DEV` mode will operate very similarly to how `debug=True` does in older Sanic versions. However, in v22.3. `debug=True` will **no longer** enable auto-reload. If you would like to have debugging and auto-reload, you should enable `DEV` mode. + +**DEVELOPMENT** + +``` +$ sanic server:app --dev +``` + +```python +app.run(debug=True, auto_reload=True) +``` + +**PRODUCTION** + +``` +$ sanic server:app +``` + +```python +app.run() +``` + +Beginning in v22.3, `PRODUCTION` mode will no longer enable access logs by default. + +A summary of the changes are as follows: + +| Flag | Mode | Tracebacks | Logging | Access logs | Reload | Max workers | +| ------- | ----- | ---------- | ------- | ----------- | ------ | ----------- | +| --debug | DEBUG | yes | DEBUG | yes | ^1 | | +| | PROD | no | INFO ^2 | ^3 | | | +| --dev | DEBUG | yes | DEBUG | yes | yes | | +| --fast | | | | | | yes | + +- ^1 `--debug` to deprecate auto-reloading and remove in 22.3 +- ^2 After 22.3 this moves to WARNING +- ^3 After 22.3: no + +### Max allowed workers + +You can easily spin up the maximum number of allowed workers using `--fast`. + +``` +$ sanic server:app --fast +``` + +```python +app.run(fast=True) +``` + +### First-class Sanic Extensions support + +[Sanic Extensions](../../plugins/sanic-ext/getting-started.md) provides a number of additional features specifically intended for API developers. You can now easily implement all of the functionality it has to offer without additional setup as long as the package is in the environment. These features include: + +- Auto create `HEAD`, `OPTIONS`, and `TRACE` endpoints +- CORS protection +- Predefined, endpoint-specific response serializers +- Dependency injection into route handlers +- OpenAPI documentation with Redoc and/or Swagger +- Request query arguments and body input validation + +The preferred method is to install it along with Sanic, but you can also install the packages on their own. + +.. column:: + +```` +``` +$ pip install sanic[ext] +``` +```` + +.. column:: + +```` +``` +$ pip install sanic sanic-ext +``` +```` + +After that, no additional configuration is required. Sanic Extensions will be attached to your application and provide all of the additional functionality with **no further configuration**. + +If you want to change how it works, or provide additional configuration, you can change Sanic extensions using `app.extend`. The following examples are equivalent. The `Config` object is to provide helpful type annotations for IDE development. + +.. column:: + +```` +```python +# This is optional, not required +app = Sanic("MyApp") +app.extend(config={"oas_url_prefix": "/apidocs"}) +``` +```` + +.. column:: + +```` +```python +# This is optional, not required +app = Sanic("MyApp") +app.config.OAS_URL_PREFIX = "/apidocs" +``` +```` + +.. column:: + +```` +```python +# This is optional, not required +from sanic_ext import Config + +app = Sanic("MyApp") +app.extend(config=Config(oas_url_prefix="/apidocs")) +``` +```` + +.. column:: + +### Contextual exceptions + +In [v21.9](./v21.9.md#default-exception-messages) we added default messages to exceptions that simplify the ability to consistently raise exceptions throughout your application. + +```python +class TeapotError(SanicException): + status_code = 418 + message = "Sorry, I cannot brew coffee" + +raise TeapotError +``` + +But this lacked two things: + +1. A dynamic and predictable message format +2. The ability to add additional context to an error message (more on this in a moment) + +The current release allows any Sanic exception to have additional information to when raised to provide context when writing an error message: + +```python +class TeapotError(SanicException): + status_code = 418 + + @property + def message(self): + return f"Sorry {self.extra['name']}, I cannot make you coffee" + +raise TeapotError(extra={"name": "Adam"}) +``` + +The new feature allows the passing of `extra` meta to the exception instance. This `extra` info object **will be suppressed** when in `PRODUCTION` mode, but displayed in `DEVELOPMENT` mode. + +.. column:: + +``` +**PRODUCTION** + +![image](https://user-images.githubusercontent.com/166269/139014161-cda67cd1-843f-4ad2-9fa1-acb94a59fc4d.png) +``` + +.. column:: + +``` +**DEVELOPMENT** + +![image](https://user-images.githubusercontent.com/166269/139014121-0596b084-b3c5-4adb-994e-31ba6eba6dad.png) +``` + +Getting back to item 2 from above: _The ability to add additional context to an error message_ + +This is particularly useful when creating microservices or an API that you intend to pass error messages back in JSON format. In this use case, we want to have some context around the exception beyond just a parseable error message to return details to the client. + +```python +raise TeapotError(context={"foo": "bar"}) +``` + +This is information **that we want** to always be passed in the error (when it is available). Here is what it should look like: + +.. column:: + +```` +**PRODUCTION** + +```json +{ + "description": "I'm a teapot", + "status": 418, + "message": "Sorry Adam, I cannot make you coffee", + "context": { + "foo": "bar" + } +} +``` +```` + +.. column:: + +```` +**DEVELOPMENT** + +```json +{ + "description": "I'm a teapot", + "status": 418, + "message": "Sorry Adam, I cannot make you coffee", + "context": { + "foo": "bar" + }, + "extra": { + "name": "Adam", + "more": "lines", + "complex": { + "one": "two" + } + }, + "path": "/", + "args": {}, + "exceptions": [ + { + "type": "TeapotError", + "exception": "Sorry Adam, I cannot make you coffee", + "frames": [ + { + "file": "handle_request", + "line": 83, + "name": "handle_request", + "src": "" + }, + { + "file": "/tmp/p.py", + "line": 17, + "name": "handler", + "src": "raise TeapotError(" + } + ] + } + ] +} +``` +```` + +### Background task management + +When using the `app.add_task` method to create a background task, there now is the option to pass an optional `name` keyword argument that allows it to be fetched, or cancelled. + +```python +app.add_task(dummy, name="dummy_task") +task = app.get_task("dummy_task") + +app.cancel_task("dummy_task") +``` + +### Route context kwargs in definitions + +When a route is defined, you can add any number of keyword arguments with a `ctx_` prefix. These values will be injected into the route `ctx` object. + +```python +@app.get("/1", ctx_label="something") +async def handler1(request): + ... + +@app.get("/2", ctx_label="something") +async def handler2(request): + ... + +@app.get("/99") +async def handler99(request): + ... + +@app.on_request +async def do_something(request): + if request.route.ctx.label == "something": + ... +``` + +### Blueprints can be registered at any time + +In previous versions of Sanic, there was a strict ordering of when a Blueprint could be attached to an application. If you ran `app.blueprint(bp)` _before_ attaching all objects to the Blueprint instance, they would be missed. + +Now, you can attach a Blueprint at anytime and everything attached to it will be included at startup. + +### Noisy exceptions (force all exceptions to logs) + +There is a new `NOISY_EXCEPTIONS` config value. When it is `False` (which is the default), Sanic will respect the `quiet` property of any `SanicException`. This means that an exception with `quiet=True` will not be displayed to the log output. + +However, when setting `NOISY_EXCEPTIONS=True`, all exceptions will be logged regardless of the `quiet` value. + +This can be helpful when debugging. + +```python +app.config.NOISY_EXCEPTIONS = True +``` + +### Signal events as `Enum` + +There is an `Enum` with all of the built-in signal values for convenience. + +```python +from sanic.signals import Event + +@app.signal(Event.HTTP_LIFECYCLE_BEGIN) +async def connection_opened(conn_info): + ... +``` + +### Custom type casting of environment variables + +By default, Sanic will convert an `int`, `float`, or a `bool` value when applying environment variables to the `config` instance. You can extend this with your own converter: + +```python +app = Sanic(..., config=Config(converters=[UUID])) +``` + +### Disable `uvloop` by configuration value + +The usage of `uvloop` can be controlled by configuration value: + +```python +app.config.USE_UVLOOP = False +``` + +### Run Sanic server with multiple TLS certificates + +Sanic can be run with multiple TLS certificates: + +```python +app.run( + ssl=[ + "/etc/letsencrypt/live/example.com/", + "/etc/letsencrypt/live/mysite.example/", + ] +) +``` + +## News + +### Coming Soon: Python Web Development with Sanic + +A book about Sanic is coming soon by one of the core developers, [@ahopkins](https://github.com/ahopkins). + +Learn more at [sanicbook.com](https://sanicbook.com). + +> Get equipped with the practical knowledge of working with Sanic to increase the performance and scalability of your web applications. While doing that, we will level-up your development skills as you learn to customize your application to meet the changing business needs without having to significantly over-engineer the app. + +A portion of book proceeds goes into the Sanic Community Organization to help fund the development and operation of Sanic. So, buying the book is another way you can support Sanic. + +### Dark mode for the docs + +If you have not already noticed, this Sanic website is now available in a native dark mode. You can toggle the theme at the top right of the page. + +## Thank you + +Thank you to everyone that participated in this release: :clap: + +[@adarsharegmi](https://github.com/adarsharegmi) +[@ahopkins](https://github.com/ahopkins) +[@ashleysommer](https://github.com/ashleysommer) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@cnicodeme](https://github.com/cnicodeme) +[@kianmeng](https://github.com/kianmeng) +[@meysam81](https://github.com/meysam81) +[@nuxion](https://github.com/nuxion) +[@prryplatypus](https://github.com/prryplatypus) +[@realDragonium](https://github.com/realDragonium) +[@SaidBySolo](https://github.com/SaidBySolo) +[@sjsadowski](https://github.com/sjsadowski) +[@Tronic](https://github.com/tronic) +[@Varriount](https://github.com/Varriount) +[@vltr](https://github.com/vltr) +[@whos4n3](https://github.com/whos4n3) + +And, a special thank you to [@miss85246](https://github.com/miss85246) and [@ZinkLu](https://github.com/ZinkLu) for their tremendous work keeping the documentation synced and translated into Chinese. + +--- + +If you enjoy the project, please consider contributing. Of course we love code contributions, but we also love contributions in any form. Consider writing some documentation, showing off use cases, joining conversations and making your voice known, and if you are able: [financial contributions](https://opencollective.com/sanic-org/). diff --git a/guide/content/ko/release-notes/2021/v21.3.md b/guide/content/ko/release-notes/2021/v21.3.md new file mode 100644 index 0000000000..e06d93b290 --- /dev/null +++ b/guide/content/ko/release-notes/2021/v21.3.md @@ -0,0 +1,272 @@ +--- +title: Version 21.3 +--- + +# Version 21.3 + +.. toc:: + +## Introduction + +Sanic is now faster. + +Well, it already was fast. But with the first iteration of the v21 release, we incorporated a few major milestones that have made some tangible improvements. These encompass some ideas that have been in the works for years, and have finally made it into the released version. + +.. warning:: Breaking changes + +```` +Version 21.3 introduces a lot of new features. But, it also includes some breaking changes. This is why these changes were introduced after the last LTS. If you rely upon something that has been removed, you should continue to use v20.12LTS until you are able to upgrade. + +```bash +pip install "sanic>=20.12,<20.13" +pip freeze > requirements.txt +``` + +For most typical installations, you should be able to upgrade without a problem. +```` + +## What to know + +Notable new or breaking features, and what to upgrade... + +### Python 3.7+ Only + +This version drops Python 3.6 support. Version 20.12LTS will continue to support Python 3.6 until its EOL in December, 2022, and version 19.12LTS will support it until its EOL in December, 2021. + +Read more about our [LTS policy](../project/policies.md#long-term-support-v-interim-releases). + +### Streaming as first class citizen + +The biggest speed improvement came from unifying the request/response cycle into a single flow. Previously, there was a difference between regular cycles, and streaming cycles. This has been simplified under the hood, even though the API is staying the same right now for compatibility. The net benefit is that **all** requests now should see a new benefit. + +Read more about [streaming changes](../advanced/streaming.md#response-streaming). + +### Router overhaul + +The old Sanic router was based upon regular expressions. In addition it suffered from a number of quirks that made it hard to modify at run time, and resulted in some performance issues. This change has been years in the making and now [converts the router to a compiled tree at startup](https://community.sanicframework.org/t/a-fast-new-router/649/41). Look for additional improvements throughout the year. + +The outward facing API has kept backwards compatibility. However, if you were accessing anything inside the router specifically, you many notice some changes. For example: + +1. `Router.get()` has a new return value +2. `Route` is now a proper class object and not a `namedtuple` +3. If building the router manually, you will need to call `Router.finalize()` before it is usable +4. There is a new `` pattern that can be matched in your routes +5. You cannot startup an application without at least one route defined + +The router is now located in its own repository: [sanic-org/sanic-router](https://github.com/sanic-org/sanic-router) and is also its own [standalone package on PyPI](https://pypi.org/project/sanic-routing/). + +### Signals API ⭐️ + +_BETA Feature: API to be finalized in v21.6_ + +A side benefit of the new router is that it can do double duty also powering the [new signals API](https://github.com/sanic-org/sanic/issues/1630). This feature is being released for public usage now, and likely the public API will not change in its final form. + +The core ideas of this feature are: + +1. to allow the developer greater control and access to plugging into the server and request lifecycles, +2. to provide new tools to synchronize and send messages through your application, and +3. to ultimately further increase performance. + +The API introduces three new methods: + +- `@app.signal(...)` - For defining a signal handler. It looks and operates very much like a route. Whenever that signal is dispatched, this handler will be executed. +- `app.event(...)` - An awaitable that can be used anywhere in your application to pause execution until the event is triggered. +- `app.dispatch(...)` - Trigger an event and cause the signal handlers to execute. + +```python +@app.signal("foo.bar.") +async def signal_handler(thing, **kwargs): + print(f"[signal_handler] {thing=}", kwargs) + +async def wait_for_event(app): + while True: + print("> waiting") + await app.event("foo.bar.*") + print("> event found\n") + +@app.after_server_start +async def after_server_start(app, loop): + app.add_task(wait_for_event(app)) + +@app.get("/") +async def trigger(request): + await app.dispatch("foo.bar.baz") + return response.text("Done.") +``` + +### Route naming + +Routes used to be referenced by both `route.name` and `route.endpoint`. While similar, they were slightly different. Now, all routes will be **consistently** namespaced and referenced. + +```text +.[optional:.] +``` + +This new "name" is assigned to the property `route.name`. We are deprecating `route.endpoint`, and will remove that property in v21.9. Until then, it will be an alias for `route.name`. + +In addition, naming prefixes that had been in use for things like static, websocket, and blueprint routes have been removed. + +### New decorators + +Several new convenience decorators to help IDEs with autocomplete. + +```python +# Alias to @app.listener("...") +@app.before_server_start +@app.after_server_stop +@app.before_server_start +@app.after_server_stop + +# Alias to @app.middleware("...") +@app.on_request +@app.on_response +``` + +### Unquote in route + +If you have a route that uses non-ascii characters, Sanic will no longer `unquote` the text for you. You will need to specifically tell the route definition that it should do so. + +```python +@app.route("/overload/", methods=["GET"], unquote=True) +async def handler2(request, param): + return text("OK2 " + param) + +request, response = app.test_client.get("/overload/您好") +assert response.text == "OK2 您好" +``` + +If you forget to do so, your text will remain encoded. + +### Alter `Request.match_info` + +The `match_info` has always provided the data for the matched path parameters. You now have access to modify that, for example in middleware. + +```python +@app.on_request +def convert_to_snake_case(request): + request.match_info = to_snake(request.match_info) +``` + +### Version types in routes + +The `version` argument in routes can now be: + +- `str` +- `int` +- `float` + +```python +@app.route("/foo", version="2.1.1") +@app.route("/foo", version=2) +@app.route("/foo", version=2.1) +``` + +### Safe method handling with body + +Route handlers for `GET`, `HEAD`, `OPTIONS` and `DELETE` will not decode any HTTP body passed to it. You can override this: + +```python +@app.delete(..., ignore_body=False) +``` + +### Application, Blueprint and Blueprint Group parity + +The `Sanic` and `Blueprint` classes share a common base. Previously they duplicated a lot of functionality, that lead to slightly different implementations between them. Now that they both inherit the same base class, developers and plugins should have a more consistent API to work with. + +Also, Blueprint Groups now also support common URL extensions like the `version` and `strict_slashes` keyword arguments. + +### Dropped `httpx` from dependencies + +There is no longer a dependency on `httpx`. + +### Removed `testing` library + +Sanic internal testing client has been removed. It is now located in its own repository: [sanic-org/sanic-testing](https://github.com/sanic-org/sanic-testing) and is also its own [standalone package on PyPI](https://pypi.org/project/sanic-testing/). + +If you have `sanic-testing` installed, it will be available and usable on your `Sanic()` application instances as before. So, the **only** change you will need to make is to add `sanic-testing` to your test suite requirements. + +### Application and connection level context (`ctx`) objects + +Version 19.9 [added ](https://github.com/sanic-org/sanic/pull/1666/files) the `request.ctx` API. This helpful construct easily allows for attaching properties and data to a request object (for example, in middleware), and reusing the information elsewhere int he application. + +Similarly, this concept is being extended in two places: + +1. the application instance, and +2. a transport connection. + +#### Application context + +A common use case is to attach properties to the app instance. For the sake of consistency, and to avoid the issue of name collision with Sanic properties, the `ctx` object now exists on `Sanic` instances. + +```python +@app.before_server_startup +async def startup_db(app, _): + # WRONG + app.db = await connect_to_db() + + # CORRECT + app.ctx.db = await connect_to_db() +``` + +#### Connection context + +When a client sends a keep alive header, Sanic will attempt to keep the transport socket [open for a period of time](../deployment/configuration.md#keep-alive-timeout). That transport object now has a `ctx` object available on it. This effectively means that multiple requests from a single client (where the transport layer is being reused) may share state. + +```python +@app.on_request +async def increment_foo(request): + if not hasattr(request.conn_info.ctx, "foo"): + request.conn_info.ctx.foo = 0 + request.conn_info.ctx.foo += 1 + +@app.get("/") +async def count_foo(request): + return text(f"request.conn_info.ctx.foo={request.conn_info.ctx.foo}") +``` + +```bash +$ curl localhost:8000 localhost:8000 localhost:8000 +request.conn_info.ctx.foo=1 +request.conn_info.ctx.foo=2 +request.conn_info.ctx.foo=3 +``` + +.. warning:: + +``` +Connection level context is an experimental feature, and should be finalized in v21.6. +``` + +## News + +### A NEW frontpage 🎉 + +We have split the documentation into two. The docstrings inside the codebase will still continue to build sphinx docs to ReadTheDocs. However, it will be limited to API documentation. The new frontpage will house the "Sanic User Guide". + +The new site runs on Vuepress. Contributions are welcome. We also invite help in translating the documents. + +As a part of this, we also freshened up the RTD documentation and changed it to API docs only. + +### Chat has moved to Discord + +The Gitter chatroom has taken one step closer to being phased out. In its place we opened a [Discord server](https://discord.gg/FARQzAEMAA). + +### Open Collective + +The Sanic Community Organization has [opened a page on Open Collective](https://opencollective.com/sanic-org) to enable anyone that would like to financially support the development of Sanic. + +### 2021 Release Managers + +Thank you to @sjsadowski and @yunstanford for acting as release managers for both 2019 and 2020. This year's release managers are @ahopkins and @vltr. + +## Thank you + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) [@akshgpt7](https://github.com/akshgpt7) [@artcg](https://github.com/artcg) [@ashleysommer](https://github.com/ashleysommer) [@elis-k](https://github.com/elis-k) [@harshanarayana](https://github.com/harshanarayana) [@sjsadowski](https://github.com/sjsadowski) [@tronic](https://github.com/tronic) [@vltr](https://github.com/vltr), + +To [@ConnorZhang](https://github.com/miss85246) and [@ZinkLu](https://github.com/ZinkLu) for translating our documents into Chinese, + +--- + +Make sure to checkout the changelog to get links to all the PRs, etc. diff --git a/guide/content/ko/release-notes/2021/v21.6.md b/guide/content/ko/release-notes/2021/v21.6.md new file mode 100644 index 0000000000..f262e68e44 --- /dev/null +++ b/guide/content/ko/release-notes/2021/v21.6.md @@ -0,0 +1,352 @@ +--- +title: Version 21.6 +--- + +# Version 21.6 + +.. toc:: + +## Introduction + +This is the second release of the version 21 [release cycle](../project/policies.md#release-schedule). There will be one more release in September before version 21 is "finalized" in the December long-term support version. One thing users may have noticed starting in 21.3, the router was moved to its own package: [`sanic-routing`](https://pypi.org/project/sanic-routing). This change is likely to stay for now. Starting with this release, the minimum required version is 0.7.0. + +## What to know + +More details in the [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html). Notable new or breaking features, and what to upgrade... + +### Deprecation of `StreamingHTTPResponse` + +The use of `StreamingHTTPResponse` has been deprecated and will be removed in the 21.12 release. This impacts both `sanic.response.stream` and `sanic.response.file_stream`, which both under the hood instantiate `StreamingHTTPResponse`. + +Although the exact migration path has yet to be determined, `sanic.response.stream` and `sanic.response.file_stream` will continue to exist in v21.12 in some form as convenience operators. Look for more details throughout this Summer as we hope to have this finalized by the September release. + +### Deprecation of `CompositionView` + +Usage of `CompositionView` has been deprecated and will be removed in 21.12. + +### Deprecation of path parameter types: `string` and `number` + +Going forward, you should use `str` and `float` for path param types instead of `string` and `number`. + +```python +@app.get("//") +async def handler(request, foo: str, bar: float): + ... +``` + +Existing `string` and `number` types are aliased and will continue to work, but will be removed in v21.12. + +### Version 0.7 router upgrades + +This includes a number of bug fixes and more gracefully handles a wider array of edge cases than v0.6. If you experience any patterns that are not supported, [please report them](https://github.com/sanic-org/sanic-routing/issues). You can see some of the issues resolved on the `sanic-routing` [release notes](https://github.com/sanic-org/sanic-routing/releases). + +### Inline streaming with `eof()` + +Version 21.3 included [big changes in how streaming is handled](https://sanic.dev/en/guide/release-notes/v21.3.html#what-to-know). The pattern introduced will become the default (see below). As a convenience, a new `response.eof()` method has been included. It should be called once the final data has been pushed to the client: + +```python +@app.route("/") +async def test(request): + response = await request.respond(content_type="text/csv") + await response.send("foo,") + await response.send("bar") + await response.eof() + return response +``` + +### New path parameter type: `slug` + +You can now specify a dynamic path segment as a `slug` with appropriate matching: + +```python +@app.get("/articles/") +async def article(request, article_slug: str): + ... +``` + +Slugs must consist of lowercase letters or digits. They may contain a hyphen (`-`), but it cannot be the first character. + +``` +this-is-a-slug +with-123-is-also-a-slug +111-at-start-is-a-slug +NOT-a-slug +-NOT-a-slug +``` + +### Stricter application and blueprint names, and deprecation + +Your application and `Blueprint` instances must conform to a stricter set of requirements: + +1. Only consisting of alphanumeric characters +2. May contain a hyphen (`-`) or an underscore (`_`) +3. Must begin with a letter (uppercase or lowercase) + +The naming convention is similar to Python variable naming conventions, with the addition of allowing hyphens (`-`). + +The looser standard has been deprecatated. Beginning in 21.12, non-conformance will be a startup time error. + +### A new access on `Route` object: `route.uri` + +The `Route` object in v21.3 no longer had a `uri` attribute. Instead, the closes you could get was `route.path`. However, because of how `sanic-routing` works, the `path` property does _not_ have a leading `/`. This has been corrected so that now there is a `route.uri` with a leading slash: + +```python +route.uri == f"/{route.path}" +``` + +### A new accessor on `Request` object impacting IPs + +To access the IP address of the incoming request, Sanic has had a convenience accessor on the request object: `request.ip`. That is not new, and comes from an underlying object that provides details about the open HTTP connection: `request.conn_info`. + +The current version adds a new `client_ip` accessor to that `conn_info` object. For IPv4, you will not notice a difference. However, for IPv6 applications, the new accessor will provide an "unwrapped" version of the address. Consider the following example: + +```python +@app.get("/") +async def handler(request): + return json( + { + "request.ip": request.ip, + "request.conn_info.client": request.conn_info.client, + "request.conn_info.client_ip": request.conn_info.client_ip, + } + ) + +app.run(sock=my_ipv6_sock) +``` + +```bash +$ curl http://\[::1\]:8000 +{ + "request.ip": "::1", + "request.conn_info.client": "[::1]", + "request.conn_info.client_ip": "::1" +} + +``` + +### Alternate `Config` and `Sanic.ctx` objects + +You can now pass your own config and context objects to your Sanic applications. A custom configuration _should_ be a subclass of `sanic.config.Config`. The context object can be anything you want, with no restrictions whatsoever. + +```python +class CustomConfig(Config): + ... + +config = CustomConfig() +app = Sanic("custom", config=config) +assert isinstance(app.config, CustomConfig) +``` + +And... + +```python +class CustomContext: + ... + +ctx = CustomContext() +app = Sanic("custom", ctx=ctx) +assert isinstance(app.ctx, CustomContext) +``` + +### Sanic CLI improvements + +1. New flag for existing feature: `--auto-reload` +2. Some new shorthand flags for existing arguments +3. New feature: `--factory` +4. New feature: `--simple` +5. New feature: `--reload-dir` + +#### Factory applications + +For applications that follow the factory pattern (a function that returns a `sanic.Sanic` instance), you can now launch your application from the Sanic CLI using the `--factory` flag. + +```python +from sanic import Blueprint, Sanic, text + +bp = Blueprint(__file__) + +@bp.get("/") +async def handler(request): + return text("😎") + +def create_app() -> Sanic: + app = Sanic(__file__) + app.blueprint(bp) + return app +``` + +You can now run it: + +```bash +$ sanic path.to:create_app --factory +``` + +#### Sanic Simple Server + +Sanic CLI now includes a simple pattern to serve a directory as a web server. It will look for an `index.html` at the directory root. + +```bash +$ sanic ./path/to/dir --simple +``` + +.. warning:: + +``` +This feature is still in early *beta* mode. It is likely to change in scope. +``` + +#### Additional reload directories + +When using either `debug` or `auto-reload`, you can include additional directories for Sanic to watch for new files. + +```bash +sanic ... --reload-dir=/path/to/foo --reload-dir=/path/to/bar +``` + +.. tip:: + +``` +You do *NOT* need to include this on your application directory. Sanic will automatically reload when any Python file in your application changes. You should use the `reload-dir` argument when you want to listen and update your application when static files are updated. +``` + +### Version prefix + +When adding `version`, your route is prefixed with `/v`. This will always be at the beginning of the path. This is not new. + +```python +# /v1/my/path +app.route("/my/path", version=1) +``` + +Now, you can alter the prefix (and therefore add path segments _before_ the version). + +```python +# /api/v1/my/path +app.route("/my/path", version=1, version_prefix="/api/v") +``` + +The `version_prefix` argument is can be defined in: + +- `app.route` and `bp.route` decorators (and all the convenience decorators also) +- `Blueprint` instantiation +- `Blueprint.group` constructor +- `BlueprintGroup` instantiation +- `app.blueprint` registration + +### Signal event auto-registration + +Setting `config.EVENT_AUTOREGISTER` to `True` will allow you to await any signal event even if it has not previously been defined with a signal handler. + +```python +@app.signal("do.something.start") +async def signal_handler(): + await do_something() + await app.dispatch("do.something.complete") + +# somethere else in your app: +await app.event("do.something.complete") +``` + +### Infinitely reusable and nestable `Blueprint` and `BlueprintGroup` + +A single `Blueprint` may not be assigned and reused to multiple groups. The groups themselves can also by infinitely nested into one or more other groups. This allows for an unlimited range of composition. + +### HTTP methods as `Enum` + +Sanic now has `sanic.HTTPMethod`, which is an `Enum`. It can be used interchangeably with strings: + +```python +from sanic import Sanic, HTTPMethod + +@app.route("/", methods=["post", "PUT", HTTPMethod.PATCH]) +async def handler(...): + ... +``` + +### Expansion of `HTTPMethodView` + +Class based views may be attached now in one of three ways: + +**Option 1 - Existing** + +```python +class DummyView(HTTPMethodView): + ... + +app.add_route(DummyView.as_view(), "/dummy") +``` + +**Option 2 - From `attach` method** + +```python +class DummyView(HTTPMethodView): + ... + +DummyView.attach(app, "/") +``` + +**Option 3 - From class definition at `__init_subclass__`** + +```python +class DummyView(HTTPMethodView, attach=app, uri="/"): + ... +``` + +Options 2 and 3 are useful if your CBV is located in another file: + +```python +from sanic import Sanic, HTTPMethodView + +class DummyView(HTTPMethodView, attach=Sanic.get_app(), uri="/"): + ... +``` + +## News + +### Discord and support forums + +If you have not already joined our community, you can become a part by joining the [Discord server](https://discord.gg/FARQzAEMAA) and the [Community Forums](https://community.sanicframework.org/). Also, follow [@sanicframework](https://twitter.com/sanicframework) on Twitter. + +### SCO 2022 elections + +The Summer 🏝/Winter ❄️ (choose your Hemisphere) is upon us. That means we will be holding elections for the SCO. This year, we will have the following positions to fill: + +- Steering Council Member (2 year term) +- Steering Council Member (2 year term) +- Steering Council Member (1 year term) +- Release Manager v22 +- Release Manager v22 + +[@vltr](https://github.com/vltr) will be staying on to complete his second year on the Steering Council. + +If you are interested in learning more, you can read about the SCO [roles and responsibilities](../project/scope.md#roles-and-responsibilities), or Adam Hopkins on Discord. + +Nominations will begin September 1. More details will be available on the Forums as we get closer. + +### New project underway + +We have added a new project to the SCO umbrella: [`sanic-ext`](https://github.com/sanic-org/sanic-ext). It is not yet released, and in heavy active development. The goal for the project will ultimately be to replace [`sanic-openapi`](https://github.com/sanic-org/sanic-openapi) with something that provides more features for web application developers, including input validation, CORS handling, and HTTP auto-method handlers. If you are interested in helping out, let us know on Discord. Look for an initial release of this project sometime (hopefully) before the September release. + +## Thank you + +Thank you to everyone that participated in this release: :clap: + +[@aaugustin](https://github.com/aaugustin) +[@ahopkins](https://github.com/ahopkins) +[@ajaygupta2790](https://github.com/ajaygupta2790) +[@ashleysommer](https://github.com/ashleysommer) +[@ENT8R](https://github.com/ent8r) +[@fredlllll](https://github.com/fredlllll) +[@graingert](https://github.com/graingert) +[@harshanarayana](https://github.com/harshanarayana) +[@jdraymon](https://github.com/jdraymon) +[@Kyle-Verhoog](https://github.com/kyle-verhoog) +[@sanjeevanahilan](https://github.com/sanjeevanahilan) +[@sjsadowski](https://github.com/sjsadowski) +[@Tronic](https://github.com/tronic) +[@vltr](https://github.com/vltr) +[@ZinkLu](https://github.com/zinklu) + +--- + +If you enjoy the project, please consider contributing. Of course we love code contributions, but we also love contributions in any form. Consider writing some documentation, showing off use cases, joining conversations and making your voice known, and if you are able, [financial contributions](https://opencollective.com/sanic-org/). diff --git a/guide/content/ko/release-notes/2021/v21.9.md b/guide/content/ko/release-notes/2021/v21.9.md new file mode 100644 index 0000000000..7b53c52f6b --- /dev/null +++ b/guide/content/ko/release-notes/2021/v21.9.md @@ -0,0 +1,240 @@ +--- +title: Version 21.9 +--- + +# Version 21.9 + +.. toc:: + +## Introduction + +This is the third release of the version 21 [release cycle](../../org/policies.md#release-schedule). Version 21 will be "finalized" in the December long-term support version release. + +## What to know + +More details in the [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html). Notable new or breaking features, and what to upgrade... + +### Removal of config values: `WEBSOCKET_READ_LIMIT`, `WEBSOCKET_WRITE_LIMIT` and `WEBSOCKET_MAX_QUEUE` + +With the complete overhaul of the websocket implementation, these configuration values were removed. There currently is not a plan to replace them. + +### Deprecation of default value of `FALLBACK_ERROR_FORMAT` + +When no error handler is attached, Sanic has used `html` as the fallback format-type. This has been deprecated and will change to `text` starting in v22.3. While the value of this has changed to `auto`, it will still continue to use HTML as the last resort thru v21.12LTS before changing. + +### `ErrorHandler.lookup` signature deprecation + +The `ErrorHandler.lookup` now **requires** two positional arguments: + +```python +def lookup(self, exception, route_name: Optional[str]): +``` + +A non-conforming method will cause Blueprint-specific exception handlers to not properly attach. + +### Reminder of upcoming removals + +As a reminder, the following items have already been deprecated, and will be removed in version 21.12LTS + +- `CompositionView` +- `load_env` (use `env_prefix` instead) +- Sanic objects (application instances, blueprints, and routes) must by alphanumeric conforming to: `^[a-zA-Z][a-zA-Z0-9_\-]*$` +- Arbitrary assignment of objects to application and blueprint instances (use `ctx` instead; removal of this has been bumped from 21.9 to 21.12) + +### Overhaul of websockets + +There has been a huge overhaul to the handling of websocket connections. Thanks to [@aaugustin](https://github.com/aaugustin) the [`websockets`](https://websockets.readthedocs.io/en/stable/index.html) now has a new implementation that allows Sanic to handle the I/O of websocket connections on its own. Therefore, Sanic has bumped the minimum version to `websockets>=10.0`. + +The change should mostly be unnoticeable to developers, except that some of the oddities around websocket handlers in Sanic have been corrected. For example, you now should be able to catch the `CancellError` yourself when someone disconnects: + +```python +@app.websocket("/") +async def handler(request, ws): + try: + while True: + await asyncio.sleep(0.25) + except asyncio.CancelledError: + print("User closed connection") +``` + +### Built-in signals + +Version [21.3](./v21.3.md) introduced [signals](../advanced/signals.md). Now, Sanic dispatches signal events **from within the codebase** itself. This means that developers now have the ability to hook into the request/response cycle at a much closer level than before. + +Previously, if you wanted to inject some logic you were limited to middleware. Think of integrated signals as _super_-middleware. The events that are dispatched now include: + +- `http.lifecycle.begin` +- `http.lifecycle.complete` +- `http.lifecycle.exception` +- `http.lifecycle.handle` +- `http.lifecycle.read_body` +- `http.lifecycle.read_head` +- `http.lifecycle.request` +- `http.lifecycle.response` +- `http.lifecycle.send` +- `http.middleware.after` +- `http.middleware.before` +- `http.routing.after` +- `http.routing.before` +- `server.init.after` +- `server.init.before` +- `server.shutdown.after` +- `server.shutdown.before` + +.. note:: + +``` +The `server` signals are the same as the four (4) main server listener events. In fact, those listeners themselves are now just convenience wrappers to signal implementations. +``` + +### Smarter `auto` exception formatting + +Sanic will now try to respond with an appropriate exception format based upon the endpoint and the client. For example, if your endpoint always returns a `sanic.response.json` object, then any exceptions will automatically be formatted in JSON. The same is true for `text` and `html` responses. + +Furthermore, you now can _explicitly_ control which formatter to use on a route-by-route basis using the route definition: + +```python +@app.route("/", error_format="json") +async def handler(request): + pass +``` + +### Blueprint copying + +Blueprints can be copied to new instances. This will carry forward everything attached to it, like routes, middleware, etc. + +```python +v1 = Blueprint("Version1", version=1) + +@v1.route("/something") +def something(request): + pass + +v2 = v1.copy("Version2", version=2) + +app.blueprint(v1) +app.blueprint(v2) +``` + +``` +/v1/something +/v2/something +``` + +### Blueprint group convenience methods + +Blueprint groups should now have all of the same methods available to them as regular Blueprints. With this, along with Blueprint copying, Blueprints should now be very composable and flexible. + +### Accept header parsing + +Sanic `Request` objects can parse an `Accept` header to provide an ordered list of the client's content-type preference. You can simply access it as an accessor: + +```python +print(request.accept) +# ["*/*"] +``` + +It also is capable of handling wildcard matching. For example, assuming the incoming request included: + +``` +Accept: */* +``` + +Then, the following is `True`: + +```python +"text/plain" in request.accept +``` + +### Default exception messages + +Any exception that derives from `SanicException` can now define a default exception message. This makes it more convenient and maintainable to reuse the same exception in multiple places without running into DRY issues with the message that the exception provides. + +```python +class TeaError(SanicException): + message = "Tempest in a teapot" + +raise TeaError +``` + +### Type annotation conveniences + +It is now possible to control the path parameter types using Python's type annotations. Instead of doing this: + +```python +@app.route("///") +def handler(request: Request, one: int, two: float, three: UUID): + ... +``` + +You can now simply do this: + +```python +@app.route("///") +def handler(request: Request, one: int, two: float, three: UUID): + ... +``` + +Both of these examples will result in the same routing principles to be applied. + +### Explicit static resource type + +You can now explicitly tell a `static` endpoint whether it is supposed to treat the resource as a file or a directory: + +```python +static("/", "/path/to/some/file", resource_type="file")) +``` + +## News + +### Release of `sanic-ext` and deprecation of `sanic-openapi` + +One of the core principles of Sanic is that it is meant to be a tool, not a dictator. As the frontpage of this website states: + +> Build the way you want to build without letting your tooling constrain you. + +This means that a lot of common features used (specifically by Web API developers) do not exist in the `sanic` repository. This is for good reason. Being unopinionated provides the developer freedom and flexibility. + +But, sometimes you do not want to have to build and rebuild the same things. Sanic has until now really relied upon the awesome support of the community to fill in the gaps with plugins. + +From the early days, there has been an official `sanic-openapi` package that offered the ability to create OpenAPI documentation based upon your application. But, that project has been plagued over the years and has not been given as much priority as the main project. + +Starting with the release of v21.9, the SCO is deprecating the `sanic-openapi` package and moving it to maintenance mode. This means that it will continue to get updates as needed to maintain it for the current future, but it will not receive any new feature enhancements. + +A new project called `sanic-ext` is taking its place. This package provides not only the ability to build OAS3 documentation, but fills in many of the gaps that API developers may want in their applications. For example, out of the box it will setup CORS, and auto enable `HEAD` and `OPTIONS` responses where needed. It also has the ability validate incoming data using either standard library Dataclasses or Pydantic models. + +The list of goodies includes: + +- CORS protection +- incoming request validation +- auto OAS3 documentation using Redoc and/or Swagger UI +- auto `HEAD`, `OPTIONS`, and `TRACE` responses +- dependency injection +- response serialization + +This project is still in `alpha` mode for now and is subject to change. While it is considered to be production capable, there may be some need to change the API as we continue to add features. + +Checkout the [documentation](../../plugins/sanic-ext/getting-started.md) for more details. + +## Thank you + +Thank you to everyone that participated in this release: :clap: + +[@aaugustin](https://github.com/aaugustin) +[@ahopkins](https://github.com/ahopkins) +[@ashleysommer](https://github.com/ashleysommer) +[@cansarigol3megawatt](https://github.com/cansarigol3megawatt) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@gluhar2006](https://github.com/gluhar2006) +[@komar007](https://github.com/komar007) +[@ombe1229](https://github.com/ombe1229) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@Tronic](https://github.com/tronic) +[@vltr](https://github.com/vltr) + +And, a special thank you to [@miss85246](https://github.com/miss85246) and [@ZinkLu](https://github.com/ZinkLu) for their tremendous work keeping the documentation synced and translated into Chinese. + +--- + +If you enjoy the project, please consider contributing. Of course we love code contributions, but we also love contributions in any form. Consider writing some documentation, showing off use cases, joining conversations and making your voice known, and if you are able, [financial contributions](https://opencollective.com/sanic-org/). diff --git a/guide/content/ko/release-notes/2022/v22.12.md b/guide/content/ko/release-notes/2022/v22.12.md new file mode 100644 index 0000000000..c52c14d155 --- /dev/null +++ b/guide/content/ko/release-notes/2022/v22.12.md @@ -0,0 +1,190 @@ +--- +title: Version 22.12 (LTS) +--- + +# Version 22.12 (LTS) + +.. toc:: + +## Introduction + +This is the final release of the version 22 [release cycle](../../org/policies.md#release-schedule). As such it is a **long-term support** release, and will be supported as stated in the [policies](../../org/policies.md#long-term-support-v-interim-releases). + +## What to know + +More details in the [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html). Notable new or breaking features, and what to upgrade... + +### 🚨 _BREAKING CHANGE_ - Sanic Inspector is now an HTTP server + +Sanic v22.9 introduced the [Inspector](./v22.9.md#inspector) to allow live inspection of a running Sanic instance. This feature relied upon opening a TCP socket and communicating over a custom protocol. That basic TCP protocol has been dropped in favor of running a full HTTP service in its place. [Learn more about the Inspector](../deployment/inspector.md). + +The current release introduces a new HTTP server and a refreshed CLI experience. This enables several new features highlighted here. Perhaps the most significant change, however, is to move all of the Inspector's commands to a subparser on the CLI instance. + +``` +$ sanic inspect --help + + ▄███ █████ ██ ▄█▄ ██ █ █ ▄██████████ + ██ █ █ █ ██ █ █ ██ + ▀███████ ███▄ ▀ █ █ ██ ▄ █ ██ + ██ █████████ █ ██ █ █ ▄▄ + ████ ████████▀ █ █ █ ██ █ ▀██ ███████ + +Optional +======== + General: + -h, --help show this help message and exit + --host HOST, -H HOST Inspector host address [default 127.0.0.1] + --port PORT, -p PORT Inspector port [default 6457] + --secure, -s Whether to access the Inspector via TLS encryption + --api-key API_KEY, -k API_KEY Inspector authentication key + --raw Whether to output the raw response information + + Subcommands: + Run one or none of the below subcommands. Using inspect without a subcommand will fetch general information about the state of the application instance. + + Or, you can optionally follow inspect with a subcommand. If you have created a custom Inspector instance, then you can run custom commands. See https://sanic.dev/en/guide/deployment/inspector.html for more details. + + {reload,shutdown,scale,} + reload Trigger a reload of the server workers + shutdown Shutdown the application and all processes + scale Scale the number of workers + Run a custom command +``` + +#### CLI remote access now available + +The `host` and `port` of the Inspector are now explicitly exposed on the CLI as shown above. Previously in v22.9, they were inferred by reference to the application instance. Because of this change, it will be more possible to expose the Inspector on live production instances and access from a remote installation of the CLI. + +For example, you can check your running production deployment from your local development machine. + +``` +$ sanic inspect --host=1.2.3.4 +``` + +.. warning:: + +``` +For **production** instances, make sure you are _using TLS and authentication_ described below. +``` + +#### TLS encryption now available + +You can secure your remote Inspector access by providing a TLS certificate to encrypt the web traffic. + +```python +app.config.INSPECTOR_TLS_CERT = "/path/to/cert.pem" +app.config.INSPECTOR_TLS_KEY = "/path/to/key.pem" +``` + +To access an encrypted installation via the CLI, use the `--secure` flag. + +``` +$ sanic inspect --secure +``` + +#### Authentication now available + +To control access to the remote Inspector, you can protect the endpoints using an API key. + +```python +app.config.INSPECTOR_API_KEY = "Super-Secret-200" +``` + +To access a protected installation via the CLI, use the `--api-key` flag. + +``` +$ sanic inspect --api-key=Super-Secret-200 +``` + +This is equivalent to the header: `Authorization: Bearer `. + +``` +$ curl http://localhost:6457 -H "Authorization: Bearer Super-Secret-200" +``` + +### Scale number of running server workers + +The Inspector is now capable of scaling the number of worker processes. For example, to scale to 3 replicas, use the following command: + +``` +$ sanic inspect scale 3 +``` + +### Extend Inspector with custom commands + +The Inspector is now fully extendable to allow for adding custom commands to the CLI. For more information see [Custom Commands](../deployment/inspector.md#custom-commands). + +``` +$ sanic inspect foo --bar +``` + +### Early worker exit on failure + +The process manager shipped with v22.9 had a very short startup timeout. This was to protect against deadlock. This was increased to 30s, and a new mechanism has been added to fail early if there is a crash in a worker process on startup. + +### Introduce `JSONResponse` with convenience methods to update a JSON response body + +The `sanic.response.json` convenience method now returns a new subclass of `HTTPResponse` appropriately named: `JSONResponse`. This new type has some convenient methods for handling changes to a response body after its creation. + +```python +resp = json({"foo": "bar"}) +resp.update({"another": "value"}) +``` + +See [Returning JSON Data](../basics/response.md#returning-json-data) for more information. + +### Updates to downstream requirements: `uvloop` and `websockets` + +Minimum `uvloop` was set to `0.15.0`. Changes were added to make Sanic compliant with `websockets` version `11.0`. + +### Force exit on 2nd `ctrl+c` + +On supporting operating systems, the existing behavior is for Sanic server to try to perform a graceful shutdown when hitting `ctrl+c`. This new release will perform an immediate shutdown on subsequent `ctrl+c` after the initial shutdown has begun. + +### Deprecations and Removals + +1. _DEPRECATED_ - The `--inspect*` commands introduced in v22.9 have been replaced with a new subcommand parser available as `inspect`. The flag versions will continue to operate until v23.3. You are encouraged to use the replacements. While this short deprecation period is a deviation from the standard two-cycles, we hope this change will be minimally disruptive. + ``` + OLD sanic ... --inspect + NEW sanic ... inspect + + OLD sanic ... --inspect-raw + NEW sanic ... inspect --raw + + OLD sanic ... --inspect-reload + NEW sanic ... inspect reload + + OLD sanic ... --inspect-shutdown + NEW sanic ... inspect shutdown + ``` + +## News + +The Sanic Community Organization will be headed by a new Steering Council for 2023. There are two returning and two new members. + +[@ahopkins](https://github.com/ahopkins) _returning_ \ +[@prryplatypus](https://github.com/prryplatypus) _returning_ \ +[@sjsadowski](https://github.com/sjsadowski) _NEW_ \ +[@Tronic](https://github.com/Tronic) _NEW_ + +The 2023 release managers are [@ahopkins](https://github.com/ahopkins) and [@sjsadowski](https://github.com/sjsadowski). + +If you are interested in getting more involved with Sanic, contact us on the [Discord server](https://discord.gg/FARQzAEMAA). + +## Thank you + +Thank you to everyone that participated in this release: :clap: + +[@aaugustin](https://github.com/aaugustin) +[@ahopkins](https://github.com/ahopkins) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@kijk2869](https://github.com/kijk2869) +[@LiraNuna](https://github.com/LiraNuna) +[@prryplatypus](https://github.com/prryplatypus) +[@sjsadowski](https://github.com/sjsadowski) +[@todddialpad](https://github.com/todddialpad) +[@Tronic](https://github.com/Tronic) + +--- + +If you enjoy the project, please consider contributing. Of course we love code contributions, but we also love contributions in any form. Consider writing some documentation, showing off use cases, joining conversations and making your voice known, and if you are able: [financial contributions](https://opencollective.com/sanic-org/). diff --git a/guide/content/ko/release-notes/2022/v22.3.md b/guide/content/ko/release-notes/2022/v22.3.md new file mode 100644 index 0000000000..ecec7630ec --- /dev/null +++ b/guide/content/ko/release-notes/2022/v22.3.md @@ -0,0 +1,230 @@ +--- +title: Version 22.3 +--- + +# Version 22.3 + +.. toc:: + +## Introduction + +This is the first release of the version 22 [release cycle](../../org/policies.md#release-schedule). All of the standard SCO libraries are now entering the same release cycle and will follow the same versioning pattern. Those packages are: + +- [`sanic-routing`](https://github.com/sanic-org/sanic-routing) +- [`sanic-testing`](https://github.com/sanic-org/sanic-testing) +- [`sanic-ext`](https://github.com/sanic-org/sanic-ext) + +## What to know + +More details in the [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html). Notable new or breaking features, and what to upgrade... + +### Application multi-serve + +The Sanic server now has an API to allow you to run multiple applications side-by-side in the same process. This is done by calling `app.prepare(...)` on one or more application instances, one or many times. Each time it should be bound to a unique host/port combination. Then, you begin serving the applications by calling `Sanic.serve()`. + +```python +app = Sanic("One") +app2 = Sanic("Two") + +app.prepare(port=9999) +app.prepare(port=9998) +app.prepare(port=9997) +app2.prepare(port=8888) +app2.prepare(port=8887) + +Sanic.serve() +``` + +In the above snippet, there are two applications that will be run concurrently and bound to multiple ports. This feature is _not_ supported in the CLI. + +This pattern is meant to be an alternative to running `app.run(...)`. It should be noted that `app.run` is now just a shorthand for the above pattern and is still fully supported. + +### 👶 _BETA FEATURE_ - New path parameter type: file extensions + +A very common pattern is to create a route that dynamically generates a file. The endpoint is meant to match on a file with an extension. There is a new path parameter to match files: ``. + +```python +@app.get("/path/to/") +async def handler(request, filename, ext): + ... +``` + +This will catch any pattern that ends with a file extension. You may, however want to expand this by specifying which extensions, and also by using other path parameter types for the file name. + +For example, if you want to catch a `.jpg` file that is only numbers: + +```python +@app.get("/path/to/") +async def handler(request, filename, ext): + ... +``` + +Some potential examples: + +| definition | example | filename | extension | +| ------------------------------------------------------------------------------------ | ----------------------------------------------------------- | -------- | ---------- | +| \ | page.txt | `"page"` | `"txt"` | +| \ | cat.jpg | `"cat"` | `"jpg"` | +| \ | cat.jpg | `"cat"` | `"jpg"` | +| | 123.txt | `123` | `"txt"` | +| | 123.svg | `123` | `"svg"` | +| | 3.14.tar.gz | `3.14` | `"tar.gz"` | + +### 🚨 _BREAKING CHANGE_ - Path parameter matching of non-empty strings + +A dynamic path parameter will only match on a non-empty string. + +Previously a route with a dynamic string parameter (`/` or `/`) would match on any string, including empty strings. It will now only match a non-empty string. To retain the old behavior, you should use the new parameter type: `/`. + +```python +@app.get("/path/to/") +async def handler(request, foo) + ... +``` + +### 🚨 _BREAKING CHANGE_ - `sanic.worker.GunicornWorker` has been removed + +Departing from our normal deprecation policy, the `GunicornWorker` was removed as a part of the process of upgrading the Sanic server to include multi-serve. This decision was made largely in part because even while it existed it was not an optimal strategy for deploying Sanic. + +If you want to deploy Sanic using `gunicorn`, then you are advised to do it using [the strategy implemented by `uvicorn`](https://www.uvicorn.org/#running-with-gunicorn). This will effectively run Sanic as an ASGI application through `uvicorn`. You can upgrade to this pattern by installing `uvicorn`: + +``` +pip install uvicorn +``` + +Then, you should be able to run it with a pattern like this: + +``` +gunicorn path.to.sanic:app -k uvicorn.workers.UvicornWorker +``` + +### Authorization header parsing + +The `Authorization` header has been partially parseable for some time now. You have been able to use `request.token` to gain access to a header that was in one of the following two forms: + +``` +Authorization: Token +Authorization: Bearer +``` + +Sanic can now parse more credential types like `BASIC`: + +``` +Authorization: Basic Z2lsLWJhdGVzOnBhc3N3b3JkMTIz +``` + +This can be accessed now as `request.credentials`: + +```python +print(request.credentials) +# Credentials(auth_type='Basic', token='Z2lsLWJhdGVzOnBhc3N3b3JkMTIz', _username='gil-bates', _password='password123') +``` + +### CLI arguments optionally injected into application factory + +Sanic will now attempt to inject the parsed CLI arguments into your factory if you are using one. + +```python +def create_app(args): + app = Sanic("MyApp") + print(args) + return app +``` + +``` +$sanic p:create_app --factory +Namespace(module='p:create_app', factory=True, simple=False, host='127.0.0.1', port=8000, unix='', cert=None, key=None, tls=None, tlshost=False, workers=1, fast=False, access_log=False, debug=False, auto_reload=False, path=None, dev=False, motd=True, verbosity=None, noisy_exceptions=False) +``` + +If you are running the CLI with `--factory`, you also have the option of passing arbitrary arguments to the command, which will be injected into the argument `Namespace`. + +``` +sanic p:create_app --factory --foo=bar +Namespace(module='p:create_app', factory=True, simple=False, host='127.0.0.1', port=8000, unix='', cert=None, key=None, tls=None, tlshost=False, workers=1, fast=False, access_log=False, debug=False, auto_reload=False, path=None, dev=False, motd=True, verbosity=None, noisy_exceptions=False, foo='bar') +``` + +### New reloader process listener events + +When running Sanic server with auto-reload, there are two new events that trigger a listener _only_ on the reloader process: + +- `reload_process_start` +- `reload_process_stop` + +These are only triggered if the reloader is running. + +```python +@app.reload_process_start +async def reload_start(*_): + print(">>>>>> reload_start <<<<<<") + +@app.reload_process_stop +async def reload_stop(*_): + print(">>>>>> reload_stop <<<<<<") +``` + +### The event loop is no longer a required argument of a listener + +You can leave out the `loop` argument of a listener. Both of these examples work as expected: + +```python +@app.before_server_start +async def without(app): + ... + +@app.before_server_start +async def with(app, loop): + ... +``` + +### Removal - Debug mode does not automatically start the reloader + +When running with `--debug` or `debug=True`, the Sanic server will not automatically start the auto-reloader. This feature of doing both on debug was deprecated in v21 and removed in this release. If you would like to have _both_ debug mode and auto-reload, you can use `--dev` or `dev=True`. + +**dev = debug mode + auto reloader** + +### Deprecation - Loading of lower case environment variables + +Sanic loads prefixed environment variables as configuration values. It has not distinguished between uppercase and lowercase as long as the prefix matches. However, it has always been the convention that the keys should be uppercase. This is deprecated and you will receive a warning if the value is not uppercase. In v22.9 only uppercase and prefixed keys will be loaded. + +## News + +### Packt publishes new book on Sanic web development + +.. column:: + +``` +There is a new book on **Python Web Development with Sanic** by [@ahopkins](https://github.com/ahopkins). The book is endorsed by the SCO and part of the proceeds of all sales go directly to the SCO for further development of Sanic. + +You can learn more at [sanicbook.com](https://sanicbook.com/) +``` + +.. column:: + +``` +![Python Web Development with Sanic](https://sanicbook.com/images/SanicCoverFinal.png) +``` + +## Thank you + +Thank you to everyone that participated in this release: :clap: + +[@aericson](https://github.com/aericson) +[@ahankinson](https://github.com/ahankinson) +[@ahopkins](https://github.com/ahopkins) +[@ariebovenberg](https://github.com/ariebovenberg) +[@ashleysommer](https://github.com/ashleysommer) +[@Bluenix2](https://github.com/Bluenix2) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@dotlambda](https://github.com/dotlambda) +[@eric-spitler](https://github.com/eric-spitler) +[@howzitcdf](https://github.com/howzitcdf) +[@jonra1993](https://github.com/jonra1993) +[@prryplatypus](https://github.com/prryplatypus) +[@raphaelauv](https://github.com/raphaelauv) +[@SaidBySolo](https://github.com/SaidBySolo) +[@SerGeRybakov](https://github.com/SerGeRybakov) +[@Tronic](https://github.com/Tronic) + +--- + +If you enjoy the project, please consider contributing. Of course we love code contributions, but we also love contributions in any form. Consider writing some documentation, showing off use cases, joining conversations and making your voice known, and if you are able: [financial contributions](https://opencollective.com/sanic-org/). diff --git a/guide/content/ko/release-notes/2022/v22.6.md b/guide/content/ko/release-notes/2022/v22.6.md new file mode 100644 index 0000000000..18c212e82a --- /dev/null +++ b/guide/content/ko/release-notes/2022/v22.6.md @@ -0,0 +1,173 @@ +--- +title: Version 22.6 +--- + +# Version 22.6 + +.. toc:: + +## Introduction + +This is the second release of the version 22 [release cycle](../../org/policies.md#release-schedule). Version 22 will be "finalized" in the December long-term support version release. + +## What to know + +More details in the [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html). Notable new or breaking features, and what to upgrade... + +### Automatic TLS setup in `DEBUG` mode + +The Sanic server can automatically setup a TLS certificate using either [mkcert](https://github.com/FiloSottile/mkcert) or [trustme](https://github.com/python-trio/trustme). This certificate will enable `https://localhost` (or another local address) for local development environments. You must install either `mkcert` or `trustme` on your own for this to work. + +.. column:: + +```` +``` +$ sanic path.to.server:app --auto-tls --debug +``` +```` + +.. column:: + +```` +```python +app.run(debug=True, auto_tls=True) +``` +```` + +This feature is not available when running in `ASGI` mode, or in `PRODUCTION` mode. When running Sanic in production, you should be using a real TLS certificate either purchased through a legitimate vendor, or using [Let's Encrypt](https://letsencrypt.org/). + +### HTTP/3 Server 🚀 + +In June 2022, the IETF finalized and published [RFC 9114](https://www.rfc-editor.org/rfc/rfc9114.html), the specification for HTTP/3. In short, HTTP/3 is a **very** different protocol than HTTP/1.1 and HTTP/2 because it implements HTTP over UDP instead of TCP. The new HTTP protocol promises faster webpage load times and solving some of the problems of the older standards. You are encouraged to [read more about](https://http3-explained.haxx.se/) this new web technology. You likely will need to install a [capable client](https://curl.se/docs/http3.html) as traditional tooling will not work. + +Sanic server offers HTTP/3 support using [aioquic](https://github.com/aiortc/aioquic). This **must** be installed: + +``` +pip install sanic aioquic +``` + +``` +pip install sanic[http3] +``` + +To start HTTP/3, you must explicitly request it when running your application. + +.. column:: + +```` +``` +$ sanic path.to.server:app --http=3 +``` + +``` +$ sanic path.to.server:app -3 +``` +```` + +.. column:: + +```` +```python +app.run(version=3) +``` +```` + +To run both an HTTP/3 and HTTP/1.1 server simultaneously, you can use [application multi-serve](./v22.3.html#application-multi-serve) introduced in v22.3. + +.. column:: + +```` +``` +$ sanic path.to.server:app --http=3 --http=1 +``` + +``` +$ sanic path.to.server:app -3 -1 +``` +```` + +.. column:: + +```` +```python +app.prepre(version=3) +app.prepre(version=1) +Sanic.serve() +``` +```` + +Because HTTP/3 requires TLS, you cannot start a HTTP/3 server without a TLS certificate. You should [set it up yourself](../how-to/tls.html) or use `mkcert` if in `DEBUG` mode. Currently, automatic TLS setup for HTTP/3 is not compatible with `trustme`. + +**👶 This feature is being released as an _EARLY RELEASE FEATURE_.** It is **not** yet fully compliant with the HTTP/3 specification, lacking some features like [websockets](https://websockets.spec.whatwg.org/), [webtransport](https://w3c.github.io/webtransport/), and [push responses](https://http3-explained.haxx.se/en/h3/h3-push). Instead the intent of this release is to bring the existing HTTP request/response cycle towards feature parity with HTTP/3. Over the next several releases, more HTTP/3 features will be added and the API for it finalized. + +### Consistent exception naming + +Some of the Sanic exceptions have been renamed to be more compliant with standard HTTP response names. + +- `InvalidUsage` >> `BadRequest` +- `MethodNotSupported` >> `MethodNotAllowed` +- `ContentRangeError` >> `RangeNotSatisfiable` + +All old names have been aliased and will remain backwards compatible. + +### Current request getter + +Similar to the API to access an application (`Sanic.get_app()`), there is a new method for retrieving the current request when outside of a request handler. + +```python +from sanic import Request + +Request.get_current() +``` + +### Improved API support for setting cache control headers + +The `file` response helper has some added parameters to make it easier to handle setting of the [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) header. + +```python +file( + ..., + last_modified=..., + max_age=..., + no_store=..., +) +``` + +### Custom `loads` function + +Just like Sanic supports globally setting a custom `dumps`, you can now set a global custom `loads`. + +```python +from orjson import loads + +app = Sanic("Test", loads=loads) +``` + +### Deprecations and Removals + +1. _REMOVED_ - Applications may no longer opt-out of the application registry +2. _REMOVED_ - Custom exception handlers will no longer run after some part of an exception has been sent +3. _REMOVED_ - Fallback error formats cannot be set on the `ErrorHandler` and must **only** be set in the `Config` +4. _REMOVED_ - Setting a custom `LOGO` for startup is no longer allowed +5. _REMOVED_ - The old `stream` response convenience method has been removed +6. _REMOVED_ - `AsyncServer.init` is removed and no longer an alias of `AsyncServer.app.state.is_started` + +## Thank you + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@amitay87](https://github.com/amitay87) +[@ashleysommer](https://github.com/ashleysommer) +[@azimovMichael](https://github.com/azimovMichael) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@kijk2869](https://github.com/kijk2869) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@sjsadowski](https://github.com/sjsadowski) +[@timmo001](https://github.com/timmo001) +[@zozzz](https://github.com/zozzz) + +--- + +If you enjoy the project, please consider contributing. Of course we love code contributions, but we also love contributions in any form. Consider writing some documentation, showing off use cases, joining conversations and making your voice known, and if you are able: [financial contributions](https://opencollective.com/sanic-org/). diff --git a/guide/content/ko/release-notes/2022/v22.9.md b/guide/content/ko/release-notes/2022/v22.9.md new file mode 100644 index 0000000000..1e16034361 --- /dev/null +++ b/guide/content/ko/release-notes/2022/v22.9.md @@ -0,0 +1,349 @@ +--- +title: Version 22.9 +--- + +# Version 22.9 + +.. toc:: + +## Introduction + +This is the third release of the version 22 [release cycle](../../org/policies.md#release-schedule). Version 22 will be "finalized" in the December long-term support version release. + +## What to know + +More details in the [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html). Notable new or breaking features, and what to upgrade... + +### ⚠ _IMPORTANT_ - New worker manager 🚀 + +Sanic server has been overhauled to provide more consistency and flexbility in how it operates. More details about the motivations are outlined in [PR #2499](https://github.com/sanic-org/sanic/pull/2499) and discussed in a live stream [discussion held on YouTube](https://youtu.be/m8HCO8NK7HE). + +This **does NOT apply** to Sanic in ASGI mode + +#### Overview of the changes + +- The worker servers will **always** run in a child process. + - Previously this could change depending upon one versus many workers, and the usage or not of the reloader. This should lead to much more predictable development environments that more closely match their production counterparts. +- Multi-workers is **now supported on Windows**. + - This was impossible because Sanic relied upon the `multiprocessing` module using `fork`, which is not available on Windows. + - Now, Sanic will always use `spawn`. This does have some noticeable differences, particularly if you are running Sanic in the global scope with `app.run` (see below re: upgrade issues). +- The application instance now has a new `multiplexer` object that can be used to restart one or many workers. This could, for example, be triggered by a request. +- There is a new Inspector that can provide details on the state of your server. +- Sanic worker manager can run arbitrary processes. + - This allows developers to add any process they want from within Sanic. + - Possible use cases: + - Health monitor, see Sanic Extensions + - Logging queue, see Sanic Extensions + - Background worker queue in a seperate process + - Running another application, like a bot +- There is a new listener called: `main_process_ready`. It really should only be used for adding arbitrary processes to Sanic. +- Passing shared objects between workers. + - Python does allow some types of objects to share state between processes, whether through shared memory, pipes, etc. + - Sanic will now allow these types of object to be shared on the `app.shared_ctx` object. + - Since this feature relies upon Pythons `multiprocessing` library, it obviously will only work to share state between Sanic worker instances that are instantiated from the same execution. This is _not_ meant to provide an API for horizontal scaling across multiple machines for example. + +#### Adding a shared context object + +To share an object between worker processes, it _MUST_ be assigned inside of the `main_process_start` listener. + +```python +from multiprocessing import Queue + +@app.main_process_start +async def main_process_start(app): + app.shared_ctx.queue = Queue() +``` + +All objects on `shared_ctx` will be available now within each worker process. + +```python +@app.before_server_starts +async def before_server_starts(app): + assert isinstance(app.shared_ctx.queue, Queue) + +@app.on_request +async def on_request(request): + assert isinstance(request.app.shared_ctx.queue, Queue) + +@app.get("/") +async def handler(request): + assert isinstance(request.app.shared_ctx.queue, Queue) +``` + +_NOTE: Sanic will not stop you from registering an unsafe object, but may warn you. Be careful not to just add a regular list object, for example, and expect it to work. You should have an understanding of how to share state between processes._ + +#### Running arbitrary processes + +Sanic can run any arbitrary process for you. It should be capable of being stopped by a `SIGINT` or `SIGTERM` OS signal. + +These processes should be registered inside of the `main_process_ready` listener. + +```python +@app.main_process_ready +async def ready(app: Sanic, _): + app.manager.manage("MyProcess", my_process, {"foo": "bar"}) +# app.manager.manage(, , ) +``` + +#### Inspector + +Sanic ships with an optional Inspector, which is a special process that allows for the CLI to inspect the running state of an application and issue commands. It currently will only work if the CLI is being run on the same machine as the Sanic instance. + +``` +sanic path.to:app --inspect +``` + +![Sanic inspector](https://user-images.githubusercontent.com/166269/190099384-2f2f3fae-22d5-4529-b279-8446f6b5f9bd.png) + +The new CLI commands are: + +``` + --inspect Inspect the state of a running instance, human readable + --inspect-raw Inspect the state of a running instance, JSON output + --trigger-reload Trigger worker processes to reload + --trigger-shutdown Trigger all processes to shutdown +``` + +This is not enabled by default. In order to have it available, you must opt in: + +```python +app.config.INSPECTOR = True +``` + +\*Note: Sanic Extensions provides a [custom request](../basics/app.md#custom-requests) class that will add a request counter to the server state. + +#### Application multiplexer + +Many of the same information and functionality is available on the application instance itself. There is a new `multiplexer` object on the application instance that has the ability to restart one or more workers, and fetch information about the current state. + +You can access it as `app.multiplexer`, or more likely by its short alias `app.m`. + +```python +@app.on_request +async def print_state(request: Request): + print(request.app.m.state) +``` + +#### Potential upgrade issues + +Because of the switch from `fork` to `spawn`, if you try running the server in the global scope you will receive an error. If you see something like this: + +``` +sanic.exceptions.ServerError: Sanic server could not start: [Errno 98] Address already in use. +This may have happened if you are running Sanic in the global scope and not inside of a `if __name__ == "__main__"` block. +``` + +... then the change is simple. Make sure `app.run` is inside a block. + +```python +if __name__ == "__main__": + app.run(port=9999, dev=True) +``` + +#### Opting out of the new functionality + +If you would like to run Sanic without the new process manager, you may easily use the legacy runners. Please note that support for them **will be removed** in the future. A date has not yet been set, but will likely be sometime in 2023. + +To opt out of the new server and use the legacy, choose the appropriate method depending upon how you run Sanic: + +.. column:: + +``` +If you use the CLI... +``` + +.. column:: + +```` +``` +sanic path.to:app --legacy +``` +```` + +.. column:: + +``` +If you use `app.run`... +``` + +.. column:: + +```` +``` +app.run(..., legacy=True) +``` +```` + +.. column:: + +``` +If you `app.prepare`... +``` + +.. column:: + +```` +``` +app.prepare(...) +Sanic.serve_legacy() +``` +```` + +Similarly, you can force Sanic to run in a single process. This however means there will not be any access to the auto-reloader. + +.. column:: + +``` +If you use the CLI... +``` + +.. column:: + +```` +``` +sanic path.to:app --single-process +``` +```` + +.. column:: + +``` +If you use `app.run`... +``` + +.. column:: + +```` +``` +app.run(..., single_process=True) +``` +```` + +.. column:: + +``` +If you `app.prepare`... +``` + +.. column:: + +```` +``` +app.prepare(...) +Sanic.serve_single() +``` +```` + +### Middleware priority + +Middleware is executed in an order based upon when it was defined. Request middleware are executed in sequence and response middleware in reverse. This could have an unfortunate impact if your ordering is strictly based upon import ordering with global variables for example. + +A new addition is to break-out of the strict construct and allow a priority to be assigned to a middleware. The higher the number for a middleware definition, the earlier in the sequence it will be executed. This applies to **both** request and response middleware. + +```python +@app.on_request +async def low_priority(_): + ... + +@app.on_request(priority=10) +async def high_priority(_): + ... +``` + +In the above example, even though `low_priority` is defined first, `high_priority` will run first. + +### Custom `loads` function + +Sanic has supported the ability to add a [custom `dumps` function](https://sanic.readthedocs.io/en/stable/sanic/api/app.html#sanic.app.Sanic) when instantiating an app. The same functionality has been extended to `loads`, which will be used when deserializing. + +```python +from json import loads + +Sanic("Test", loads=loads) +``` + +### Websocket objects are now iterable + +Rather than calling `recv` in a loop on a `Websocket` object, you can iterate on it in a `for` loop. + +```python +from sanic import Request, Websocket + +@app.websocket("/ws") +async def ws_echo_handler(request: Request, ws: Websocket): + async for msg in ws: + await ws.send(msg) +``` + +### Appropriately respond with 304 on static files + +When serving a static file, the Sanic server can respond appropriately to a request with `If-Modified-Since` using a `304` response instead of resending a file. + +### Two new signals to wrap handler execution + +Two new [signals](../advanced/signals.md) have been added that wrap the execution of a request handler. + +- `http.handler.before` - runs after request middleware but before the route handler +- `http.handler.after` - runs after the route handler + - In _most_ circumstances, this also means that it will run before response middleware. However, if you call `request.respond` from inside of a route handler, then your middleware will come first + +### New Request properties for HTTP method information + +The HTTP specification defines which HTTP methods are: safe, idempotent, and cacheable. New properties have been added that will respond with a boolean flag to help identify the request property based upon the method. + +```python +request.is_safe +request.is_idempotent +request.is_cacheable +``` + +### 🚨 _BREAKING CHANGE_ - Improved cancel request exception + +In prior version of Sanic, if a `CancelledError` was caught it could bubble up and cause the server to respond with a `503`. This is not always the desired outcome, and it prevented the usage of that error in other circumstances. As a result, Sanic will now use a subclass of `CancelledError` called: `RequestCancelled` for this functionality. It likely should have little impact unless you were explicitly relying upon the old behavior. + +For more details on the specifics of these properties, checkout the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods). + +### New deprecation warning filter + +You can control the level of deprecation warnings from Sanic using [standard library warning filter values](https://docs.python.org/3/library/warnings.html#the-warnings-filter). Default is `"once"`. + +```python +app.config.DEPRECATION_FILTER = "ignore" +``` + +### Deprecations and Removals + +1. _DEPRECATED_ - Duplicate route names have been deprecated and will be removed in v23.3 +2. _DEPRECATED_ - Registering duplicate exception handlers has been deprecated and will be removed in v23.3 +3. _REMOVED_ - `route.ctx` not set by Sanic, and is a blank object for users, therefore ... + - `route.ctx.ignore_body` >> `route.extra.ignore_body` + - `route.ctx.stream` >> `route.extra.stream` + - `route.ctx.hosts` >> `route.extra.hosts` + - `route.ctx.static` >> `route.extra.static` + - `route.ctx.error_format` >> `route.extra.error_format` + - `route.ctx.websocket` >> `route.extra.websocket` +4. _REMOVED_ - `app.debug` is READ-ONLY +5. _REMOVED_ - `app.is_running` removed +6. _REMOVED_ - `app.is_stopping` removed +7. _REMOVED_ - `Sanic._uvloop_setting` removed +8. _REMOVED_ - Prefixed environment variables will be ignored if not uppercase + +## Thank you + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@azimovMichael](https://github.com/azimovMichael) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@huntzhan](https://github.com/huntzhan) +[@monosans](https://github.com/monosans) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@seemethere](https://github.com/seemethere) +[@sjsadowski](https://github.com/sjsadowski) +[@timgates42](https://github.com/timgates42) +[@Tronic](https://github.com/Tronic) + +--- + +If you enjoy the project, please consider contributing. Of course we love code contributions, but we also love contributions in any form. Consider writing some documentation, showing off use cases, joining conversations and making your voice known, and if you are able: [financial contributions](https://opencollective.com/sanic-org/). diff --git a/guide/content/ko/release-notes/2023/v23.12.md b/guide/content/ko/release-notes/2023/v23.12.md new file mode 100644 index 0000000000..c6a622ecb2 --- /dev/null +++ b/guide/content/ko/release-notes/2023/v23.12.md @@ -0,0 +1,185 @@ +--- +title: Version 23.12 +--- + +# Version 23.12 (LTS) + +.. toc:: + +## Introduction + +This is the final release of the version 23 [release cycle](../../organization/policies.md#release-schedule). It is designated as a **long-term support ("LTS") release**, which means it will receive support for two years as stated in the support policy. If you run into any issues, please raise a concern on [GitHub](https://github.com/sanic-org/sanic/issues/new/choose.) + +## What to know + +More details in the [Changelog](../changelog.html). Notable new or breaking features, and what to upgrade: + +### 🎉 Documentation now using {span:has-text-primary:Sanic} + +![](http://127.0.0.1:8000/assets/images/sanic-framework-logo-circle-128x128.png) + +You can read [all about how](/en/built-with-sanic.html), but we converted this documentation site to using the SHH 🤫 stack. + +- [Sanic](https://sanic.dev) +- [html5tagger](https://github.com/sanic-org/html5tagger) +- [HTMX](https://htmx.org/) + +### 👶 _BETA_ Welcome to the Sanic interactive console + +That's right, Sanic now ships with a REPL! + +![](/assets/images/repl.png) + +When using the Sanic CLI, you can pass the `--repl` argument to automatically run an interactive console along side your application. This is extremely helpful while developing, and allows you access to the application instance, as well as a built-in client enabled to send HTTP requests to the running instance. + +If you use the `--dev` flag, this feature is quasi-enabled by default. While it will not outright run the REPL, it will start the process and allow you to enter the REPL at anytime by hitting `` on your keyboard. + +_This is still in BETA mode. We would appreciate you letting us know about any any enhancement requests or issues._ + +### Python 3.12 support + +We have added Python 3.12 to the supported versions. + +### Start and restart arbitrary processes + +Using the [multiplexer](../../guide/running/manager.md#access-to-the-multiplexer), you can now start and restart arbitrary or pre-existing processes. This enabled the following new features in the way the multiplexer and worker manager operate: + +1. `multiplexer.restart("")` will now restart a targeted single process +2. `multiplexer.manage(...)` is a new method that works exactly like `manager.manage(...)` +3. The `manage` methods now have additional keyword arguments: + - `tracked` - whether the state of the process is tracked after the process completes + - `restartable` - whether the process should be allowed to be restarted + - `auto_start` - whether the process should be started immediately after it is created + +```python +def task(n: int = 10, **kwargs): + print("TASK STARTED", kwargs) + for i in range(n): + print(f"Running task - Step {i+1} of {n}") + sleep(1) + +@app.get("/restart") +async def restart_handler(request: Request): + request.app.m.restart("Sanic-TEST-0") + return json({"foo": request.app.m.name}) + + +@app.get("/start") +async def start_handler(request: Request): + request.app.m.manage("NEW", task, kwargs={"n": 7}, workers=2) + return json({"foo": request.app.m.name}) + +@app.main_process_ready +def start_process(app: Sanic): + app.manager.manage("TEST", task, kwargs={"n": 3}, restartable=True) +``` + +### Prioritized listeners and signals + +In [v22.9](../2022/v22.9.md) Sanic added prioritization to middleware to allow arbitrary ordering of middleware. This same concept has now been extended to listeners and signals. This will allow a priority number to be assigned at creation time that will override its default position in the execution timeline. + +```python +@app.before_server_start(priority=3) +async def sample(app): + ... +``` + +The higher the number, the higher priority it will receive. Overall the rules for deciding the order of execution are as follows: + +1. Priority in descending order +2. Application listeners before Blueprint listeners +3. Registration order + +_Remember, some listeners are executed in reverse order_ + +### Websocket signals + +We have added three new signals for websockets: + +1. `websocket.handler.before` +2. `websocket.handler.after` +3. `websocket.handler.exception` + +```python +@app.signal("websocket.handler.before") +async def ws_before(request: Request, websocket: Websocket): + ... + +@app.signal("websocket.handler.after") +async def ws_after(request: Request, websocket: Websocket): + ... + +@app.signal("websocket.handler.exception") +async def ws_exception( + request: Request, websocket: Websocket, exception: Exception +): + ... +``` + +![](https://camo.githubusercontent.com/ea2894c88bedf37a4f12f129569e8fd14bfceaa36d4452c7b7a1869d2f1cdb18/68747470733a2f2f7a692e66692f77732d7369676e616c732e706e67) + +### Simplified signals + +Sanic has always enforced a three part naming convention for signals: `one.two.three`. However, now you can create simpler names that are only a single part. + +```python +@app.signal("foo") +async def foo(): + ... +``` + +You can make that part dynamic just like with regular signals and routes: + +```python +@app.signal("") +async def handler(**kwargs): + print("foobar signal received") + print(kwargs) + + +@app.route("/") +async def test(request: Request): + await request.app.dispatch("foobar") + return json({"hello": "world"}) +``` + +If you need to have multiple dynamic signals, then you should use the longer three-part format. + +### The `event` method has been updated + +A number of changes have been made to both `app.event()` and `blueprint.event()`. + +- `condition` and `exclusive` are keywords to control matching conditions (similar to the `signal()` methods) +- You can pass either a `str` or an `Enum` (just like `signal()`) +- returns a copy of the context that was passed to the `dispatch()` method + +### Reload trigger gets changed files + +The files changed by the reloader are now injected into the listener. This will allow the trigger to do something with knowledge of what those changed files were. + +```python +@app.after_reload_trigger +async def after_reload_trigger(_, changed): + print(changed) +``` + +## Thank you + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@freddiewanah](https://github.com/freddiewanah) +[@gluhar2006](https://github.com/gluhar2006) +[@iAndriy](https://github.com/iAndriy) +[@MichaelHinrichs](https://github.com/MichaelHinrichs) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@sjsadowski](https://github.com/sjsadowski) +[@talljosh](https://github.com/talljosh) +[@tjni](https://github.com/tjni) +[@Tronic](https://github.com/Tronic) + +--- + +If you enjoy the project, please consider contributing. Of course we love code contributions, but we also love contributions in any form. Consider writing some documentation, showing off use cases, joining conversations and making your voice known, and if you are able: [financial contributions](https://opencollective.com/sanic-org/). diff --git a/guide/content/ko/release-notes/2023/v23.3.md b/guide/content/ko/release-notes/2023/v23.3.md new file mode 100644 index 0000000000..7b098e2430 --- /dev/null +++ b/guide/content/ko/release-notes/2023/v23.3.md @@ -0,0 +1,421 @@ +--- +title: Version 23.3 +--- + +# Version 23.3 + +.. toc:: + +## Introduction + +This is the first release of the version 23 [release cycle](../../org/policies.md#release-schedule). As such contains some deprecations and hopefully some _small_ breaking changes. If you run into any issues, please raise a concern on [GitHub](https://github.com/sanic-org/sanic/issues/new/choose). + +## What to know + +More details in the [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html). Notable new or breaking features, and what to upgrade... + +### Nicer traceback formatting + +The SCO adopted two projects into the Sanic namespace on GitHub: [tracerite](https://github.com/sanic-org/tracerite) and [html5tagger](https://github.com/sanic-org/html5tagger). These projects team up to provide and incredible new error page with more details to help the debugging process. + +This is provided out of the box, and will adjust to display only relevant information whether in DEBUG more or PROD mode. + +.. column:: + +``` +**Using PROD mode** +![image](/assets/images/error-html-no-debug.png) +``` + +.. column:: + +``` +**Using DEBUG mode** +![image](/assets/images/error-html-debug.png) +``` + +Light and dark mode HTML pages are available and will be used implicitly. + +### Basic file browser on directories + +When serving a directory from a static handler, Sanic can be configured to show a basic file browser instead using `directory_view=True`. + +.. column:: + +```` +```python +app.static("/uploads/", "/path/to/dir/", directory_view=True) +``` +```` + +.. column:: + +``` +![image](/assets/images/directory-view.png) +``` + +Light and dark mode HTML pages are available and will be used implicitly. + +### HTML templating with Python + +Because Sanic is using [html5tagger](https://github.com/sanic-org/html5tagger) under the hood to render the [new error pages](#nicer-traceback-formatting), you now have the package available to you to easily generate HTML pages in Python code: + +.. column:: + +```` +```python +from html5tagger import Document +from sanic import Request, Sanic, html + +app = Sanic("TestApp") + +@app.get("/") +async def handler(request: Request): + doc = Document("My Website") + doc.h1("Hello, world.") + with doc.table(id="data"): + doc.tr.th("First").th("Second").th("Third") + doc.tr.td(1).td(2).td(3) + doc.p(class_="text")("A paragraph with ") + doc.a(href="/files")("a link")(" and ").em("formatting") + return html(doc) +``` +```` + +.. column:: + +```` +```html + + +My Website +

Hello, world.

+ + + +
First + Second + Third +
1 + 2 + 3 +
+

+ A paragraph with a link and formatting +``` +```` + +### Auto-index serving is available on static handlers + +Sanic can now be configured to serve an index file when serving a static directory. + +```python +app.static("/assets/", "/path/to/some/dir", index="index.html") +``` + +When using the above, requests to `http://example.com/assets/` will automatically serve the `index.html` file located in that directory. + +### Simpler CLI targets + +It is common practice for Sanic applications to use the variable `app` as the application instance. Because of this, the CLI application target (the second value of the `sanic` CLI command) now tries to infer the application instance based upon what the target is. If the target is a module that contains an `app` variable, it will use that. + +There are now four possible ways to launch a Sanic application from the CLI. + +#### 1. Application instance + +As normal, providing a path to a module and an application instance will work as expected. + +```sh +sanic path.to.module:app # global app instance +``` + +#### 2. Application factory + +Previously, to serve the factory pattern, you would need to use the `--factory` flag. This can be omitted now. + +```sh +sanic path.to.module:create_app # factory pattern +``` + +#### 3. Path to launch Sanic Simple Server + +Similarly, to launch the Sanic simple server (serve static directory), you previously needed to use the `--simple` flag. This can be omitted now, and instead simply provide the path to the directory. + +```sh +sanic ./path/to/directory/ # simple serve +``` + +#### 4. Python module containing an `app` variable + +As stated above, if the target is a module that contains an `app` variable, it will use that (assuming that `app` variable is a `Sanic` instance). + +```sh +sanic path.to.module # module with app instance +``` + +### More convenient methods for setting and deleting cookies + +The old cookie pattern was awkward and clunky. It didn't look like regular Python because of the "magic" going on under the hood. + +.. column:: + +``` +😱 This is not intuitive and is confusing for newcomers. +``` + +.. column:: + +```` +```python +response = text("There's a cookie up in this response") +response.cookies["test"] = "It worked!" +response.cookies["test"]["domain"] = ".yummy-yummy-cookie.com" +response.cookies["test"]["httponly"] = True +``` +```` + +There are now new methods (and completely overhauled `Cookie` and `CookieJar` objects) to make this process more convenient. + +.. column:: + +``` +😌 Ahh... Much nicer. +``` + +.. column:: + +```` +```python +response = text("There's a cookie up in this response") +response.add_cookie( + "test", + "It worked!", + domain=".yummy-yummy-cookie.com", + httponly=True +) +``` +```` + +### Better cookie compatibility + +Sanic has added support for [cookie prefixes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#cookie_prefixes), making it seemless and easy to read and write cookies with the values. + +While setting the cookie... + +```py +response.cookies.add_cookie("foo", "bar", host_prefix=True) +``` + +This will create the prefixed cookie: `__Host-foo`. However, when accessing the cookie on an incoming request, you can do so without knowing about the existence of the header. + +```py +request.cookies.get("foo") +``` + +It should also be noted, cookies can be accessed as properties just like [headers](#access-any-header-as-a-property). + +```python +request.cookies.foo +``` + +And, cookies are similar to the `request.args` and `request.form` objects in that multiple values can be retrieved using `getlist`. + +```py +request.cookies.getlist("foo") +``` + +Also added is support for creating [partitioned cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#partitioned_cookie). + +```py +response.cookies.add_cookie(..., partitioned=True) +``` + +### 🚨 _BREAKING CHANGE_ - More consistent and powerful `SanicException` + +Sanic has for a while included the `SanicException` as a base class exception. This could be extended to add `status_code`, etc. [See more details](http://localhost:8080/en/guide/best-practices/exceptions.html). + +**NOW**, using all of the various exceptions has become easier. The commonly used exceptions can be imported directly from the root level module. + +```python +from sanic import NotFound, Unauthorized, BadRequest, ServerError +``` + +In addition, all of these arguments are available as keyword arguments on every exception type: + +| argument | type | description | +| --------- | ------ | ----------------------------------------------------------- | +| `quiet` | `bool` | Suppress the traceback from the logs | +| `context` | `dict` | Additional information shown in error pages _always_ | +| `extra` | `dict` | Additional information shown in error pages in _DEBUG_ mode | +| `headers` | `dict` | Additional headers sent in the response | + +None of these are themselves new features. However, they are more consistent in how you can use them, thus creating a powerful way to control error responses directly. + +```py +raise ServerError(headers={"foo": "bar"}) +``` + +The part of this that is a breaking change is that some formerly positional arguments are now keyword only. + +You are encouraged to look at the specific implementations for each error in the [API documents](https://sanic.readthedocs.io/en/stable/sanic/api/exceptions.html#module-sanic.exceptions). + +### 🚨 _BREAKING CHANGE_ - Refresh `Request.accept` functionality to be more performant and spec-compliant + +Parsing od the `Accept` headers into the `Request.accept` accessor has been improved. If you were using this property and relying upon its equality operation, this has changed. You should probably transition to using the `request.accept.match()` method. + +### Access any header as a property + +To simplify access to headers, you can access a raw (unparsed) version of the header as a property. The name of the header is the name of the property in all lowercase letters, and switching any hyphens (`-`) to underscores (`_`). + +For example: + +.. column:: + +```` +``` +GET /foo/bar HTTP/1.1 +Host: localhost +User-Agent: curl/7.88.1 +X-Request-ID: 123ABC +``` +```` + +.. column:: + +```` +```py +request.headers.host +request.headers.user_agent +request.headers.x_request_id +``` +```` + +### Consume `DELETE` body by default + +By default, the body of a `DELETE` request will now be consumed and read onto the `Request` object. This will make `body` available like on `POST`, `PUT`, and `PATCH` requests without any further action. + +### Custom `CertLoader` for direct control of creating `SSLContext` + +Sometimes you may want to create your own `SSLContext` object. To do this, you can create your own subclass of `CertLoader` that will generate your desired context object. + +```python +from sanic.worker.loader import CertLoader + +class MyCertLoader(CertLoader): + def load(self, app: Sanic) -> SSLContext: + ... + +app = Sanic(..., certloader_class=MyCertLoader) +``` + +### Deprecations and Removals + +1. _DEPRECATED_ - Dict-style cookie setting +2. _DEPRECATED_ - Using existence of JSON data on the request for one factor in using JSON error formatter +3. _REMOVED_ - Remove deprecated `__blueprintname__` property +4. _REMOVED_ - duplicate route names +5. _REMOVED_ - duplicate exception handler definitions +6. _REMOVED_ - inspector CLI with flags +7. _REMOVED_ - legacy server (including `sanic.server.serve_single` and `sanic.server.serve_multiple`) +8. _REMOVED_ - serving directory with bytes string +9. _REMOVED_ - `Request.request_middleware_started` +10. _REMOVED_ - `Websocket.connection` + +#### Duplicated route names are no longer allowed + +In version 22.9, Sanic announced that v23.3 would deprecate allowing routes to be registered with duplicate names. If you see the following error, it is because of that change: + +> sanic.exceptions.ServerError: Duplicate route names detected: SomeApp.some_handler. You should rename one or more of them explicitly by using the `name` param, or changing the implicit name derived from the class and function name. For more details, please see https://sanic.dev/en/guide/release-notes/v23.3.html#duplicated-route-names-are-no-longer-allowed + +If you are seeing this, you should opt-in to using explicit names for your routes. + +.. column:: + +```` +**BAD** +```python +app = Sanic("SomeApp") + +@app.get("/") +@app.get("/foo") +async def handler(request: Request): +``` +```` + +.. column:: + +```` +**GOOD** +```python +app = Sanic("SomeApp") + +@app.get("/", name="root") +@app.get("/foo", name="foo") +async def handler(request: Request): +``` +```` + +#### Response cookies + +Response cookies act as a `dict` for compatibility purposes only. In version 24.3, all `dict` methods will be removed and response cookies will be objects only. + +Therefore, if you are using this pattern to set cookie properties, you will need to upgrade it before version 24.3. + +```python +resp = HTTPResponse() +resp.cookies["foo"] = "bar" +resp.cookies["foo"]["httponly"] = True +``` + +Instead, you should be using the `add_cookie` method: + +```python +resp = HTTPResponse() +resp.add_cookie("foo", "bar", httponly=True) +``` + +#### Request cookies + +Sanic has added support for reading duplicated cookie keys to be more in compliance with RFC specifications. To retain backwards compatibility, accessing a cookie value using `__getitem__` will continue to work to fetch the first value sent. Therefore, in version 23.3 and prior versions this will be `True`. + +```python +assert request.cookies["foo"] == "bar" +assert request.cookies.get("foo") == "bar" +``` + +Version 23.3 added `getlist` + +```python +assert request.cookies.getlist("foo") == ["bar"] +``` + +As stated above, the `get` and `getlist` methods are available similar to how they exist on other request properties (`request.args`, `request.form`, etc). Starting in v24.3, the `__getitem__` method for cookies will work exactly like those properties. This means that `__getitem__` will return a list of values. + +Therefore, if you are relying upon this functionality to return only one value, you should upgrade to the following pattern before v24.3. + +```python +assert request.cookies["foo"] == ["bar"] +assert request.cookies.get("foo") == "bar" +assert request.cookies.getlist("foo") == ["bar"] +``` + +## Thank you + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@deounix](https://github.com/deounix) +[@Kludex](https://github.com/Kludex) +[@mbendiksen](https://github.com/mbendiksen) +[@prryplatypus](https://github.com/prryplatypus) +[@r0x0d](https://github.com/r0x0d) +[@SaidBySolo](https://github.com/SaidBySolo) +[@sjsadowski](https://github.com/sjsadowski) +[@stricaud](https://github.com/stricaud) +[@Tracyca209](https://github.com/Tracyca209) +[@Tronic](https://github.com/Tronic) + +--- + +If you enjoy the project, please consider contributing. Of course we love code contributions, but we also love contributions in any form. Consider writing some documentation, showing off use cases, joining conversations and making your voice known, and if you are able: [financial contributions](https://opencollective.com/sanic-org/). diff --git a/guide/content/ko/release-notes/2023/v23.6.md b/guide/content/ko/release-notes/2023/v23.6.md new file mode 100644 index 0000000000..0444dfd3dd --- /dev/null +++ b/guide/content/ko/release-notes/2023/v23.6.md @@ -0,0 +1,199 @@ +--- +title: Version 23.6 +--- + +# Version 23.6 + +.. toc:: + +## Introduction + +This is the second release of the version 23 [release cycle](../../org/policies.md#release-schedule). If you run into any issues, please raise a concern on [GitHub](https://github.com/sanic-org/sanic/issues/new/choose). + +## What to know + +More details in the [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html). Notable new or breaking features, and what to upgrade... + +### Remove Python 3.7 support + +Python 3.7 is due to reach its scheduled upstream end-of-life on 2023-06-27. Sanic is now dropping support for Python 3.7, and requires Python 3.8 or newer. + +See [#2777](https://github.com/sanic-org/sanic/pull/2777). + +### Resolve pypy compatibility issues + +A small patch was added to the `os` module to once again allow for Sanic to run with PyPy. This workaround replaces the missing `readlink` function (missing in PyPy `os` module) with the function `os.path.realpath`, which serves to the same purpose. + +See [#2782](https://github.com/sanic-org/sanic/pull/2782). + +### Add custom typing to config and ctx objects + +The `sanic.Sanic` and `sanic.Request` object have become generic types that will make it more convenient to have fully typed `config` and `ctx` objects. + +In the most simple form, the `Sanic` object is typed as: + +```python +from sanic import Sanic +app = Sanic("test") +reveal_type(app) # N: Revealed type is "sanic.app.Sanic[sanic.config.Config, types.SimpleNamespace]" +``` + +.. tip:: Note + +``` +It should be noted, there is *no* requirement to use the generic types. The default types are `sanic.config.Config` and `types.SimpleNamespace`. This new feature is just an option for those that want to use it and existing types of `app: Sanic` and `request: Request` should work just fine. +``` + +Now it is possible to have a fully-type `app.config`, `app.ctx`, and `request.ctx` objects though generics. This allows for better integration with auto completion tools in IDEs improving the developer experience. + +```python +from sanic import Request, Sanic +from sanic.config import Config + +class CustomConfig(Config): + pass + +class Foo: + pass + +class RequestContext: + foo: Foo + +class CustomRequest(Request[Sanic[CustomConfig, Foo], RequestContext]): + @staticmethod + def make_context() -> RequestContext: + ctx = RequestContext() + ctx.foo = Foo() + return ctx + +app = Sanic( + "test", config=CustomConfig(), ctx=Foo(), request_class=CustomRequest +) + +@app.get("/") +async def handler(request: CustomRequest): + ... +``` + +As a side effect, now `request.ctx` is lazy initialized, which should reduce some overhead when the `request.ctx` is unused. + +One further change you may have noticed in the above snippet is the `make_context` method. This new method can be used by custom `Request` types to inject an object different from a `SimpleNamespace` similar to how Sanic has allowed custom application context objects for a while. + +For a more thorough discussion, see [custom typed application](../basics/app.md#custom-typed-application) and [custom typed request](../basics/app.md#custom-typed-request). + +See [#2785](https://github.com/sanic-org/sanic/pull/2785). + +### Universal exception signal + +A new exception signal added for **ALL** exceptions raised while the server is running: `"server.exception.reporting"`. This is a universal signal that will be emitted for any exception raised, and dispatched as its own task. This means that it will _not_ block the request handler, and will _not_ be affected by any middleware. + +This is useful for catching exceptions that may occur outside of the request handler (for example in signals, or in a background task), and it intended for use to create a consistent error handling experience for the user. + +```python +from sanic.signals import Event + +@app.signal(Event.SERVER_LIFECYCLE_EXCEPTION) +async def catch_any_exception(app: Sanic, exception: Exception): + app.ctx.my_error_reporter_utility.error(exception) +``` + +This pattern can be simplified with a new decorator `@app.report_exception`: + +```python +@app.report_exception +async def catch_any_exception(app: Sanic, exception: Exception): + print("Caught exception:", exception) +``` + +It should be pointed out that this happens in a background task and is **NOT** for manipulation of an error response. It is only for reporting, logging, or other purposes that should be triggered when an application error occurs. + +See [#2724](https://github.com/sanic-org/sanic/pull/2724) and [#2792](https://github.com/sanic-org/sanic/pull/2792). + +### Add name prefixing to BP groups + +Sanic had been raising a warning on duplicate route names for a while, and started to enforce route name uniqueness in [v23.3](https://sanic.dev/en/guide/release-notes/v23.3.html#deprecations-and-removals). This created a complication for blueprint composition. + +New name prefixing parameter for blueprints groups has been added to alleviate this issue. It allows nesting of blueprints and groups to make them composable. + +The addition is the new `name_prefix` parameter as shown in this snippet. + +```python +bp1 = Blueprint("bp1", url_prefix="/bp1") +bp2 = Blueprint("bp2", url_prefix="/bp2") + +bp1.add_route(lambda _: ..., "/", name="route1") +bp2.add_route(lambda _: ..., "/", name="route2") + +group_a = Blueprint.group( + bp1, bp2, url_prefix="/group-a", name_prefix="group-a" +) +group_b = Blueprint.group( + bp1, bp2, url_prefix="/group-b", name_prefix="group-b" +) + +app = Sanic("TestApp") +app.blueprint(group_a) +app.blueprint(group_b) +``` + +The routes built will be named as follows: + +- `TestApp.group-a_bp1.route1` +- `TestApp.group-a_bp2.route2` +- `TestApp.group-b_bp1.route1` +- `TestApp.group-b_bp2.route2` + +See [#2727](https://github.com/sanic-org/sanic/pull/2727). + +### Add `request.client_ip` + +Sanic has introduced `request.client_ip`, a new accessor that provides client's IP address from both local and proxy data. It allows running the application directly on Internet or behind a proxy. This is equivalent to `request.remote_addr or request.ip`, providing the client IP regardless of how the application is deployed. + +See [#2790](https://github.com/sanic-org/sanic/pull/2790). + +### Increase of `KEEP_ALIVE_TIMEOUT` default to 120 seconds + +The default `KEEP_ALIVE_TIMEOUT` value changed from 5 seconds to 120 seconds. It is of course still configurable, but this change should improve performance on long latency connections, where reconnecting is expensive, and better fits typical user flow browsing pages with longer-than-5-second intervals. + +Sanic has historically used 5 second timeouts to quickly close idle connections. The chosen value of **120 seconds** is indeed larger than Nginx default of 75, and is the same value that Caddy server has by default. + +Related to [#2531](https://github.com/sanic-org/sanic/issues/2531) and +[#2681](https://github.com/sanic-org/sanic/issues/2681). + +See [#2670](https://github.com/sanic-org/sanic/pull/2670). + +### Set multiprocessing start method early + +Due to how Python handles `multiprocessing`, it may be confusing to some users how to properly create synchronization primitives. This is due to how Sanic creates the `multiprocessing` context. This change sets the start method early so that any primitives created will properly attach to the correct context. + +For most users, this should not be noticeable or impactful. But, it should make creation of something like this easier and work as expected. + +```python +from multiprocessing import Queue + +@app.main_process_start +async def main_process_start(app): + app.shared_ctx.queue = Queue() +``` + +See [#2776](https://github.com/sanic-org/sanic/pull/2776). + +## Thank you + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@chuckds](https://github.com/chuckds) +[@deounix](https://github.com/deounix) +[@guacs](https://github.com/guacs) +[@liamcoatman](https://github.com/liamcoatman) +[@moshe742](https://github.com/moshe742) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@Thirumalai](https://github.com/Thirumalai) +[@Tronic](https://github.com/Tronic) + +--- + +If you enjoy the project, please consider contributing. Of course we love code contributions, but we also love contributions in any form. Consider writing some documentation, showing off use cases, joining conversations and making your voice known, and if you are able: [financial contributions](https://opencollective.com/sanic-org/). diff --git a/guide/content/ko/release-notes/2023/v23.9.md b/guide/content/ko/release-notes/2023/v23.9.md new file mode 100644 index 0000000000..630da3625b --- /dev/null +++ b/guide/content/ko/release-notes/2023/v23.9.md @@ -0,0 +1,7 @@ +--- +title: Version 23.9 +--- + +# Version 23.9 + +_Due to circumstances at the time, v.23.9 was skipped._ diff --git a/guide/content/ko/release-notes/2024/v24.12.md b/guide/content/ko/release-notes/2024/v24.12.md new file mode 100644 index 0000000000..227cb5aaa5 --- /dev/null +++ b/guide/content/ko/release-notes/2024/v24.12.md @@ -0,0 +1,80 @@ +--- +title: Version 24.12 +--- + +# Version 24.12 + +.. toc:: + +## Introduction + +This is the first release of the version 24 [release cycle](../../organization/policies.md#release-schedule). The release cadence for v24 may be slightly altered from years past. Make sure to stay up to date in the Discord server for latest updates. If you run into any issues, please raise a concern on [GitHub](https://github.com/sanic-org/sanic/issues/new/choose). + +## What to know + +More details in the [Changelog](../changelog.html). Notable new or breaking features, and what to upgrade: + +### 👶 _BETA_ Custom CLI commands + +The `sanic` CLI utility now allows for custom commands to be invoked. Commands can be added using the decorator syntax below. + +```python +@app.command +async def foo(one, two: str, three: str = "..."): + logger.info(f"FOO {one=} {two=} {three=}") + + +@app.command +def bar(): + logger.info("BAR") + + +@app.command(name="qqq") +async def baz(): + logger.info("BAZ") +``` + +These are invoked using the `exec` command as follows. + +```sh +sanic server:app exec [--arg=value] +``` + +Any arguments in the function's signature will be added as arguments. For example: + +```sh +sanic server:app exec command --one=1 --two=2 --three=3 +``` + +.. warning:: + +``` +This is in **BETA** and the functionality is subject to change in upcoming versions. +``` + +### Add Python 3.13 support + +We have added Python 3.13 to the supported versions. + +### Remove Python 3.8 support + +Python 3.8 reached end-of-life. Sanic is now dropping support for Python 3.8, and requires Python 3.9 or newer. + +### Old response cookie accessors removed + +Prior to v23, cookies on `Response` objects were set and accessed as dictionary objects. That was deprecated in v23.3 when the new [convenience methods](../2023/v23.3.html#more-convenient-methods-for-setting-and-deleting-cookies) were added. The old patterns have been removed. + +## Thank you + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@C5H12O5](https://github.com/C5H12O5) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@HyperKiko](https://github.com/HyperKiko) +[@imnotjames](https://github.com/imnotjames) +[@pygeek](https://github.com/pygeek) + +--- + +If you enjoy the project, please consider contributing. Of course we love code contributions, but we also love contributions in any form. Consider writing some documentation, showing off use cases, joining conversations and making your voice known, and if you are able: [financial contributions](https://opencollective.com/sanic-org/). diff --git a/guide/content/ko/release-notes/2024/v24.6.md b/guide/content/ko/release-notes/2024/v24.6.md new file mode 100644 index 0000000000..5d3adebd60 --- /dev/null +++ b/guide/content/ko/release-notes/2024/v24.6.md @@ -0,0 +1,135 @@ +--- +title: Version 24.6 +--- + +# Version 24.6 + +.. toc:: + +## Introduction + +This is the first release of the version 24 [release cycle](../../organization/policies.md#release-schedule). The release cadence for v24 may be slightly altered from years past. Make sure to stay up to date in the Discord server for latest updates. If you run into any issues, please raise a concern on [GitHub](https://github.com/sanic-org/sanic/issues/new/choose). + +## What to know + +More details in the [Changelog](../changelog.html). Notable new or breaking features, and what to upgrade: + +### Logging improvements + +The default logging patterns have been cleaned up to make them much more developer-friendly when viewing from a terminal session. This includes the use of color and less verbose formatting. + +Sanic will select between two slight variations depending upon whether your server is in DEBUG mode. You can always opt to remove colors by using: + +```python +app.config.NO_COLOR = True +``` + +The color will automatically be stripped out from logs not in TTY terminal. + +Sanic will switch between the DEBUG and PROD formatters automatically using `sanic.logging.formatter.AutoFormatter` and `sanic.logging.formatter.AutoAccessFormatter`. Of course, you can force one version or the other using the appropriately named formatters + +#### In DEBUG mode + +```python +sanic.logging.formatter.DebugFormatter +sanic.logging.formatter.DebugAccessFormatter +``` + +![](/assets/images/logging-dev.png) + +#### In PROD mode + +```python +sanic.logging.formatter.ProdFormatter +sanic.logging.formatter.ProdAccessFormatter +``` + +![](/assets/images/logging-prod.png) + +#### Legacy + +If you prefer the old-style of logging, these have been preserved for you as logging formatters: `sanic.logging.formatter.LegacyFormatter` and `sanic.logging.formatter.LegacyAccessFormatter`. + +One way to implement these formatters: + +```python +from sanic.log import LOGGING_CONFIG_DEFAULTS + +LOGGING_CONFIG_DEFAULTS["formatters"] = { + "generic": { + "class": "sanic.logging.formatter.LegacyFormatter" + }, + "access": { + "class": "sanic.logging.formatter.LegacyAccessFormatter" + }, +} +``` + +#### New JSON formatter + +There also is a new JSON log formatter that will output the logs in JSON format for integration with other third part logging platforms. + +```python +from sanic.log import LOGGING_CONFIG_DEFAULTS + +LOGGING_CONFIG_DEFAULTS["formatters"] = { + "generic": { + "class": "sanic.logging.formatter.JSONFormatter" + }, + "access": { + "class": "sanic.logging.formatter.JSONAccessFormatter" + }, +} +``` + +### Using Paths in unix sockets + +When creating a unix socket for your server, you can now perform that by passing a `pathlib.Path` object instead of just a string-based path + +### Custom route names + +You can override the `generate_name` method on either a custom `Sanic` or a `Blueprint`. This will allow you to modify the route names at will. + +```python +from sanic import Sanic, text, + +class Custom(Sanic): + def generate_name(self, *objects): + existing = self._generate_name(*objects) + return existing.upper() + +app = Sanic("Foo") + +@app.get("/") +async def handler(request): + return text(request.name) # FOO.HANDLER + + +return app +``` + +### 🚨 BREAKING CHANGES + +1. `Request.cookies.getlist` always returns a `list`. This means when no cookie of `key` exists, it will be an empty `list` instead of `None`. Use `Request.cookies.getlist("something", None)` to retain existing behavior. + +## Thank you + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@ashleysommer](https://github.com/ashleysommer) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@DeJayDev](https://github.com/DeJayDev) +[@ekzhang](https://github.com/ekzhang) +[@Huy-Ngo](https://github.com/Huy-Ngo) +[@iAndriy](https://github.com/iAndriy) +[@jakkaz](https://github.com/jakkaz) +[@Nano112](https://github.com/Nano112) +[@prryplatypus](https://github.com/prryplatypus) +[@razodactyl](https://github.com/razodactyl) +[@Tronic](https://github.com/Tronic) +[@wieczorek1990](https://github.com/wieczorek1990) + +--- + +If you enjoy the project, please consider contributing. Of course we love code contributions, but we also love contributions in any form. Consider writing some documentation, showing off use cases, joining conversations and making your voice known, and if you are able: [financial contributions](https://opencollective.com/sanic-org/). diff --git a/guide/content/ko/release-notes/changelog.md b/guide/content/ko/release-notes/changelog.md new file mode 100644 index 0000000000..8e7597b27a --- /dev/null +++ b/guide/content/ko/release-notes/changelog.md @@ -0,0 +1,1598 @@ +--- +content_class: changelog +--- + +# Changelog + +🔶 Current release\ +🔷 In support LTS release + +## Version 24.12.0 🔶🔷 + +_Current version_ + +### Features + +- [#3019](https://github.com/sanic-org/sanic/pull/3019) Add custom commands to `sanic` CLI + +### Bugfixes + +- [#2992](https://github.com/sanic-org/sanic/pull/2992) Fix `mixins.startup.serve` UnboundLocalError +- [#3000](https://github.com/sanic-org/sanic/pull/3000) Fix type annocation for `JSONResponse` method for return type `bytes` allowed for `dumps` callable +- [#3009](https://github.com/sanic-org/sanic/pull/3009) Fix `SanicException.quiet` attribute handling when set to `False` +- [#3014](https://github.com/sanic-org/sanic/pull/3014) Cleanup some typing +- [#3015](https://github.com/sanic-org/sanic/pull/3015) Kill the entire process group if applicable +- [#3016](https://github.com/sanic-org/sanic/pull/3016) Fix incompatible type annotation of get method in the HTTPMethodView class + +### Deprecations and Removals + +- [#3020](https://github.com/sanic-org/sanic/pull/3020) Remove Python 3.8 support + +### Developer infrastructure + +- [#3017](https://github.com/sanic-org/sanic/pull/3017) Cleanup setup.cfg + +### Improved Documentation + +- [#3007](https://github.com/sanic-org/sanic/pull/3007) Fix typo in documentation for `sanic-ext` + +## Version 24.6.0 + +### Features + +- [#2838](https://github.com/sanic-org/sanic/pull/2838) Simplify request cookies `getlist` +- [#2850](https://github.com/sanic-org/sanic/pull/2850) Unix sockets can now use `pathlib.Path` +- [#2931](https://github.com/sanic-org/sanic/pull/2931) [#2958](https://github.com/sanic-org/sanic/pull/2958) Logging improvements +- [#2947](https://github.com/sanic-org/sanic/pull/2947) Make the .message field on exceptions non-empty +- [#2961](https://github.com/sanic-org/sanic/pull/2961) [#2964](https://github.com/sanic-org/sanic/pull/2964) Allow for custom name generation + +### Bugfixes + +- [#2919](https://github.com/sanic-org/sanic/pull/2919) Remove deprecation notice in websockets +- [#2937](https://github.com/sanic-org/sanic/pull/2937) Resolve response streaming error when in ASGI mode +- [#2959](https://github.com/sanic-org/sanic/pull/2959) Resolve Python 3.12 deprecation notic +- [#2960](https://github.com/sanic-org/sanic/pull/2960) Ensure proper intent for noisy exceptions +- [#2970](https://github.com/sanic-org/sanic/pull/2970) [#2978](https://github.com/sanic-org/sanic/pull/2978) Fix missing dependencies for 3.12 +- [#2971](https://github.com/sanic-org/sanic/pull/2971) Fix middleware exceptions on Not Found routes with error in middleware +- [#2973](https://github.com/sanic-org/sanic/pull/2973) Resolve cheduling logic for `transport.close` and `transport.abort` +- [#2976](https://github.com/sanic-org/sanic/pull/2976) Fix deleting a cookie that was created with `secure=False` +- [#2979](https://github.com/sanic-org/sanic/pull/2979) Throw error on bad body length +- [#2980](https://github.com/sanic-org/sanic/pull/2980) Throw error on bad body encoding + +### Deprecations and Removals + +- [#2899](https://github.com/sanic-org/sanic/pull/2899) Remove erroneous line from REPL impacting environments without HTTPX +- [#2962](https://github.com/sanic-org/sanic/pull/2962) Merge entity header removal + +### Developer infrastructure + +- [#2882](https://github.com/sanic-org/sanic/pull/2882) [#2896](https://github.com/sanic-org/sanic/pull/2896) Apply dynamic port fixture for improving tests with port selection +- [#2887](https://github.com/sanic-org/sanic/pull/2887) Updates to docker image builds +- [#2932](https://github.com/sanic-org/sanic/pull/2932) Cleanup code base with Ruff + +### Improved Documentation + +- [#2924](https://github.com/sanic-org/sanic/pull/2924) Cleanup markdown on html5tagger page +- [#2930](https://github.com/sanic-org/sanic/pull/2930) Cleanup typo on Sanic Extensions README.md +- [#2934](https://github.com/sanic-org/sanic/pull/2934) Add more context to the health check documents +- [#2936](https://github.com/sanic-org/sanic/pull/2936) Improve worker manager documentation +- [#2955](https://github.com/sanic-org/sanic/pull/2955) Fixed wrong formatting in `request.md` + +## Version 23.12.0 🔷 + +### Features + +- [#2775](https://github.com/sanic-org/sanic/pull/2775) Start and restart arbitrary processes +- [#2811](https://github.com/sanic-org/sanic/pull/2811) Cleaner process management in shutdown +- [#2812](https://github.com/sanic-org/sanic/pull/2812) Suppress task cancel traceback on open websocket +- [#2822](https://github.com/sanic-org/sanic/pull/2822) Listener and signal prioritization +- [#2831](https://github.com/sanic-org/sanic/pull/2831) Reduce memory consumption +- [#2837](https://github.com/sanic-org/sanic/pull/2837) Accept bare cookies +- [#2841](https://github.com/sanic-org/sanic/pull/2841) Add `websocket.handler.` signals +- [#2805](https://github.com/sanic-org/sanic/pull/2805) Add changed files to reload trigger listeners +- [#2813](https://github.com/sanic-org/sanic/pull/2813) Allow for simple signals +- [#2827](https://github.com/sanic-org/sanic/pull/2827) Improve functionality and consistency of `Sanic.event()` +- [#2851](https://github.com/sanic-org/sanic/pull/2851) Allow range requests for a single byte +- [#2854](https://github.com/sanic-org/sanic/pull/2854) Better `Request.scheme` for websocket requests +- [#2858](https://github.com/sanic-org/sanic/pull/2858) Convert Sanic `Request` to a Websockets `Request` for handshake +- [#2859](https://github.com/sanic-org/sanic/pull/2859) Add a REPL to the `sanic` CLI +- [#2870](https://github.com/sanic-org/sanic/pull/2870) Add Python 3.12 support +- [#2875](https://github.com/sanic-org/sanic/pull/2875) Better exception on multiprocessing context conflicts + +### Bugfixes + +- [#2803](https://github.com/sanic-org/sanic/pull/2803) Fix MOTD display for extra data + +### Developer infrastructure + +- [#2796](https://github.com/sanic-org/sanic/pull/2796) Refactor unit test cases +- [#2801](https://github.com/sanic-org/sanic/pull/2801) Fix `test_fast` when there is only one CPU +- [#2807](https://github.com/sanic-org/sanic/pull/2807) Add constraint for autodocsum (lint issue in old package version) +- [#2808](https://github.com/sanic-org/sanic/pull/2808) Refactor GitHub Actions +- [#2814](https://github.com/sanic-org/sanic/pull/2814) Run CI pipeline on git push +- [#2846](https://github.com/sanic-org/sanic/pull/2846) Drop old performance tests/benchmarks +- [#2848](https://github.com/sanic-org/sanic/pull/2848) Makefile cleanup +- [#2865](https://github.com/sanic-org/sanic/pull/2865) + [#2869](https://github.com/sanic-org/sanic/pull/2869) + [#2872](https://github.com/sanic-org/sanic/pull/2872) + [#2879](https://github.com/sanic-org/sanic/pull/2879) + Add ruff to toolchain +- [#2866](https://github.com/sanic-org/sanic/pull/2866) Fix the alt svc test to run locally with explicit buffer nbytes +- [#2877](https://github.com/sanic-org/sanic/pull/2877) Use Python's trusted publisher in deployments +- [#2882](https://github.com/sanic-org/sanic/pull/2882) Introduce dynamic port fixture in targeted locations in the test suite + +### Improved Documentation + +- [#2781](https://github.com/sanic-org/sanic/pull/2781) + [#2821](https://github.com/sanic-org/sanic/pull/2821) + [#2861](https://github.com/sanic-org/sanic/pull/2861) + [#2863](https://github.com/sanic-org/sanic/pull/2863) + Conversion of User Guide to the SHH (Sanic, html5tagger, HTMX) stack +- [#2810](https://github.com/sanic-org/sanic/pull/2810) Update README +- [#2855](https://github.com/sanic-org/sanic/pull/2855) Edit Discord badge +- [#2864](https://github.com/sanic-org/sanic/pull/2864) Adjust documentation for using state properties within http/https redirection document + +## Version 23.9.0 + +_Due to circumstances at the time, v.23.9 was skipped._ + +## Version 23.6.0 + +### Features + +- [#2670](https://github.com/sanic-org/sanic/pull/2670) Increase `KEEP_ALIVE_TIMEOUT` default to 120 seconds +- [#2716](https://github.com/sanic-org/sanic/pull/2716) Adding allow route overwrite option in blueprint +- [#2724](https://github.com/sanic-org/sanic/pull/2724) and [#2792](https://github.com/sanic-org/sanic/pull/2792) Add a new exception signal for ALL exceptions raised anywhere in application +- [#2727](https://github.com/sanic-org/sanic/pull/2727) Add name prefixing to BP groups +- [#2754](https://github.com/sanic-org/sanic/pull/2754) Update request type on middleware types +- [#2770](https://github.com/sanic-org/sanic/pull/2770) Better exception message on startup time application induced import error +- [#2776](https://github.com/sanic-org/sanic/pull/2776) Set multiprocessing start method early +- [#2785](https://github.com/sanic-org/sanic/pull/2785) Add custom typing to config and ctx objects +- [#2790](https://github.com/sanic-org/sanic/pull/2790) Add `request.client_ip` + +### Bugfixes + +- [#2728](https://github.com/sanic-org/sanic/pull/2728) Fix traversals for intended results +- [#2729](https://github.com/sanic-org/sanic/pull/2729) Handle case when headers argument of ResponseStream constructor is None +- [#2737](https://github.com/sanic-org/sanic/pull/2737) Fix type annotation for `JSONREsponse` default content type +- [#2740](https://github.com/sanic-org/sanic/pull/2740) Use Sanic's serializer for JSON responses in the Inspector +- [#2760](https://github.com/sanic-org/sanic/pull/2760) Support for `Request.get_current` in ASGI mode +- [#2773](https://github.com/sanic-org/sanic/pull/2773) Alow Blueprint routes to explicitly define error_format +- [#2774](https://github.com/sanic-org/sanic/pull/2774) Resolve headers on different renderers +- [#2782](https://github.com/sanic-org/sanic/pull/2782) Resolve pypy compatibility issues + +### Deprecations and Removals + +- [#2777](https://github.com/sanic-org/sanic/pull/2777) Remove Python 3.7 support + +### Developer infrastructure + +- [#2766](https://github.com/sanic-org/sanic/pull/2766) Unpin setuptools version +- [#2779](https://github.com/sanic-org/sanic/pull/2779) Run keep alive tests in loop to get available port + +### Improved Documentation + +- [#2741](https://github.com/sanic-org/sanic/pull/2741) Better documentation examples about running Sanic + From that list, the items to highlight in the release notes: + +## Version 23.3.0 + +### Features + +- [#2545](https://github.com/sanic-org/sanic/pull/2545) Standardize init of exceptions for more consistent control of HTTP responses using exceptions +- [#2606](https://github.com/sanic-org/sanic/pull/2606) Decode headers as UTF-8 also in ASGI +- [#2646](https://github.com/sanic-org/sanic/pull/2646) Separate ASGI request and lifespan callables +- [#2659](https://github.com/sanic-org/sanic/pull/2659) Use `FALLBACK_ERROR_FORMAT` for handlers that return `empty()` +- [#2662](https://github.com/sanic-org/sanic/pull/2662) Add basic file browser (HTML page) and auto-index serving +- [#2667](https://github.com/sanic-org/sanic/pull/2667) Nicer traceback formatting (HTML page) +- [#2668](https://github.com/sanic-org/sanic/pull/2668) Smarter error page rendering format selection; more reliant upon header and "common sense" defaults +- [#2680](https://github.com/sanic-org/sanic/pull/2680) Check the status of socket before shutting down with `SHUT_RDWR` +- [#2687](https://github.com/sanic-org/sanic/pull/2687) Refresh `Request.accept` functionality to be more performant and spec-compliant +- [#2696](https://github.com/sanic-org/sanic/pull/2696) Add header accessors as properties + ``` + Example-Field: Foo, Bar + Example-Field: Baz + ``` + ```python + request.headers.example_field == "Foo, Bar,Baz" + ``` +- [#2700](https://github.com/sanic-org/sanic/pull/2700) Simpler CLI targets + + ```sh + $ sanic path.to.module:app # global app instance + $ sanic path.to.module:create_app # factory pattern + $ sanic ./path/to/directory/ # simple serve + ``` +- [#2701](https://github.com/sanic-org/sanic/pull/2701) API to define a number of workers in managed processes +- [#2704](https://github.com/sanic-org/sanic/pull/2704) Add convenience for dynamic changes to routing +- [#2706](https://github.com/sanic-org/sanic/pull/2706) Add convenience methods for cookie creation and deletion + + ```python + response = text("...") + response.add_cookie("test", "It worked!", domain=".yummy-yummy-cookie.com") + ``` +- [#2707](https://github.com/sanic-org/sanic/pull/2707) Simplified `parse_content_header` escaping to be RFC-compliant and remove outdated FF hack +- [#2710](https://github.com/sanic-org/sanic/pull/2710) Stricter charset handling and escaping of request URLs +- [#2711](https://github.com/sanic-org/sanic/pull/2711) Consume body on `DELETE` by default +- [#2719](https://github.com/sanic-org/sanic/pull/2719) Allow `password` to be passed to TLS context +- [#2720](https://github.com/sanic-org/sanic/pull/2720) Skip middleware on `RequestCancelled` +- [#2721](https://github.com/sanic-org/sanic/pull/2721) Change access logging format to `%s` +- [#2722](https://github.com/sanic-org/sanic/pull/2722) Add `CertLoader` as application option for directly controlling `SSLContext` objects +- [#2725](https://github.com/sanic-org/sanic/pull/2725) Worker sync state tolerance on race condition + +### Bugfixes + +- [#2651](https://github.com/sanic-org/sanic/pull/2651) ASGI websocket to pass thru bytes as is +- [#2697](https://github.com/sanic-org/sanic/pull/2697) Fix comparison between datetime aware and naive in `file` when using `If-Modified-Since` + +### Deprecations and Removals + +- [#2666](https://github.com/sanic-org/sanic/pull/2666) Remove deprecated `__blueprintname__` property + +### Improved Documentation + +- [#2712](https://github.com/sanic-org/sanic/pull/2712) Improved example using `'https'` to create the redirect + +## Version 22.12.0 + +_Current LTS version_ + +### Features + +- [#2569](https://github.com/sanic-org/sanic/pull/2569) Add `JSONResponse` class with some convenient methods when updating a response object +- [#2598](https://github.com/sanic-org/sanic/pull/2598) Change `uvloop` requirement to `>=0.15.0` +- [#2609](https://github.com/sanic-org/sanic/pull/2609) Add compatibility with `websockets` v11.0 +- [#2610](https://github.com/sanic-org/sanic/pull/2610) Kill server early on worker error + - Raise deadlock timeout to 30s +- [#2617](https://github.com/sanic-org/sanic/pull/2617) Scale number of running server workers +- [#2621](https://github.com/sanic-org/sanic/pull/2621) [#2634](https://github.com/sanic-org/sanic/pull/2634) Send `SIGKILL` on subsequent `ctrl+c` to force worker exit +- [#2622](https://github.com/sanic-org/sanic/pull/2622) Add API to restart all workers from the multiplexer +- [#2624](https://github.com/sanic-org/sanic/pull/2624) Default to `spawn` for all subprocesses unless specifically set: + ```python + from sanic import Sanic + + Sanic.start_method = "fork" + ``` +- [#2625](https://github.com/sanic-org/sanic/pull/2625) Filename normalisation of form-data/multipart file uploads +- [#2626](https://github.com/sanic-org/sanic/pull/2626) Move to HTTP Inspector: + - Remote access to inspect running Sanic instances + - TLS support for encrypted calls to Inspector + - Authentication to Inspector with API key + - Ability to extend Inspector with custom commands +- [#2632](https://github.com/sanic-org/sanic/pull/2632) Control order of restart operations +- [#2633](https://github.com/sanic-org/sanic/pull/2633) Move reload interval to class variable +- [#2636](https://github.com/sanic-org/sanic/pull/2636) Add `priority` to `register_middleware` method +- [#2639](https://github.com/sanic-org/sanic/pull/2639) Add `unquote` to `add_route` method +- [#2640](https://github.com/sanic-org/sanic/pull/2640) ASGI websockets to receive `text` or `bytes` + +### Bugfixes + +- [#2607](https://github.com/sanic-org/sanic/pull/2607) Force socket shutdown before close to allow rebinding +- [#2590](https://github.com/sanic-org/sanic/pull/2590) Use actual `StrEnum` in Python 3.11+ +- [#2615](https://github.com/sanic-org/sanic/pull/2615) Ensure middleware executes only once per request timeout +- [#2627](https://github.com/sanic-org/sanic/pull/2627) Crash ASGI application on lifespan failure +- [#2635](https://github.com/sanic-org/sanic/pull/2635) Resolve error with low-level server creation on Windows + +### Deprecations and Removals + +- [#2608](https://github.com/sanic-org/sanic/pull/2608) [#2630](https://github.com/sanic-org/sanic/pull/2630) Signal conditions and triggers saved on `signal.extra` +- [#2626](https://github.com/sanic-org/sanic/pull/2626) Move to HTTP Inspector + - 🚨 _BREAKING CHANGE_: Moves the Inspector to a Sanic app from a simple TCP socket with a custom protocol + - _DEPRECATE_: The `--inspect*` commands have been deprecated in favor of `inspect ...` commands +- [#2628](https://github.com/sanic-org/sanic/pull/2628) Replace deprecated `distutils.strtobool` + +### Developer infrastructure + +- [#2612](https://github.com/sanic-org/sanic/pull/2612) Add CI testing for Python 3.11 + +## Version 22.9.1 + +### Features + +- [#2585](https://github.com/sanic-org/sanic/pull/2585) Improved error message when no applications have been registered + +### Bugfixes + +- [#2578](https://github.com/sanic-org/sanic/pull/2578) Add certificate loader for in process certificate creation +- [#2591](https://github.com/sanic-org/sanic/pull/2591) Do not use sentinel identity for `spawn` compatibility +- [#2592](https://github.com/sanic-org/sanic/pull/2592) Fix properties in nested blueprint groups +- [#2595](https://github.com/sanic-org/sanic/pull/2595) Introduce sleep interval on new worker reloader + +### Deprecations and Removals + +### Developer infrastructure + +- [#2588](https://github.com/sanic-org/sanic/pull/2588) Markdown templates on issue forms + +### Improved Documentation + +- [#2556](https://github.com/sanic-org/sanic/pull/2556) v22.9 documentation +- [#2582](https://github.com/sanic-org/sanic/pull/2582) Cleanup documentation on Windows support + +## Version 22.9.0 + +### Features + +- [#2445](https://github.com/sanic-org/sanic/pull/2445) Add custom loads function +- [#2490](https://github.com/sanic-org/sanic/pull/2490) Make `WebsocketImplProtocol` async iterable +- [#2499](https://github.com/sanic-org/sanic/pull/2499) Sanic Server WorkerManager refactor +- [#2506](https://github.com/sanic-org/sanic/pull/2506) Use `pathlib` for path resolution (for static file serving) +- [#2508](https://github.com/sanic-org/sanic/pull/2508) Use `path.parts` instead of `match` (for static file serving) +- [#2513](https://github.com/sanic-org/sanic/pull/2513) Better request cancel handling +- [#2516](https://github.com/sanic-org/sanic/pull/2516) Add request properties for HTTP method info: + - `request.is_safe` + - `request.is_idempotent` + - `request.is_cacheable` + - _See_ [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) _for more information about when these apply_ +- [#2522](https://github.com/sanic-org/sanic/pull/2522) Always show server location in ASGI +- [#2526](https://github.com/sanic-org/sanic/pull/2526) Cache control support for static files for returning 304 when appropriate +- [#2533](https://github.com/sanic-org/sanic/pull/2533) Refactor `_static_request_handler` +- [#2540](https://github.com/sanic-org/sanic/pull/2540) Add signals before and after handler execution + - `http.handler.before` + - `http.handler.after` +- [#2542](https://github.com/sanic-org/sanic/pull/2542) Add _[redacted]_ to CLI :) +- [#2546](https://github.com/sanic-org/sanic/pull/2546) Add deprecation warning filter +- [#2550](https://github.com/sanic-org/sanic/pull/2550) Middleware priority and performance enhancements + +### Bugfixes + +- [#2495](https://github.com/sanic-org/sanic/pull/2495) Prevent directory traversion with static files +- [#2515](https://github.com/sanic-org/sanic/pull/2515) Do not apply double slash to paths in certain static dirs in Blueprints + +### Deprecations and Removals + +- [#2525](https://github.com/sanic-org/sanic/pull/2525) Warn on duplicate route names, will be prevented outright in v23.3 +- [#2537](https://github.com/sanic-org/sanic/pull/2537) Raise warning and deprecation notice on duplicate exceptions, will be prevented outright in v23.3 + +### Developer infrastructure + +- [#2504](https://github.com/sanic-org/sanic/pull/2504) Cleanup test suite +- [#2505](https://github.com/sanic-org/sanic/pull/2505) Replace Unsupported Python Version Number from the Contributing Doc +- [#2530](https://github.com/sanic-org/sanic/pull/2530) Do not include tests folder in installed package resolver + +### Improved Documentation + +- [#2502](https://github.com/sanic-org/sanic/pull/2502) Fix a few typos +- [#2517](https://github.com/sanic-org/sanic/pull/2517) [#2536](https://github.com/sanic-org/sanic/pull/2536) Add some type hints + +## Version 22.6.2 + +### Bugfixes + +- [#2522](https://github.com/sanic-org/sanic/pull/2522) Always show server location in ASGI + +## Version 22.6.1 + +### Bugfixes + +- [#2477](https://github.com/sanic-org/sanic/pull/2477) Sanic static directory fails when folder name ends with ".." + +## Version 22.6.0 + +### Features + +- [#2378](https://github.com/sanic-org/sanic/pull/2378) Introduce HTTP/3 and autogeneration of TLS certificates in `DEBUG` mode + - 👶 _EARLY RELEASE FEATURE_: Serving Sanic over HTTP/3 is an early release feature. It does not yet fully cover the HTTP/3 spec, but instead aims for feature parity with Sanic's existing HTTP/1.1 server. Websockets, WebTransport, push responses are examples of some features not yet implemented. + - 📦 _EXTRA REQUIREMENT_: Not all HTTP clients are capable of interfacing with HTTP/3 servers. You may need to install a [HTTP/3 capable client](https://curl.se/docs/http3.html). + - 📦 _EXTRA REQUIREMENT_: In order to use TLS autogeneration, you must install either [mkcert](https://github.com/FiloSottile/mkcert) or [trustme](https://github.com/python-trio/trustme). +- [#2416](https://github.com/sanic-org/sanic/pull/2416) Add message to `task.cancel` +- [#2420](https://github.com/sanic-org/sanic/pull/2420) Add exception aliases for more consistent naming with standard HTTP response types (`BadRequest`, `MethodNotAllowed`, `RangeNotSatisfiable`) +- [#2432](https://github.com/sanic-org/sanic/pull/2432) Expose ASGI `scope` as a property on the `Request` object +- [#2438](https://github.com/sanic-org/sanic/pull/2438) Easier access to websocket class for annotation: `from sanic import Websocket` +- [#2439](https://github.com/sanic-org/sanic/pull/2439) New API for reading form values with options: `Request.get_form` +- [#2445](https://github.com/sanic-org/sanic/pull/2445) Add custom `loads` function +- [#2447](https://github.com/sanic-org/sanic/pull/2447), [#2486](https://github.com/sanic-org/sanic/pull/2486) Improved API to support setting cache control headers +- [#2453](https://github.com/sanic-org/sanic/pull/2453) Move verbosity filtering to logger +- [#2475](https://github.com/sanic-org/sanic/pull/2475) Expose getter for current request using `Request.get_current()` + +### Bugfixes + +- [#2448](https://github.com/sanic-org/sanic/pull/2448) Fix to allow running with `pythonw.exe` or places where there is no `sys.stdout` +- [#2451](https://github.com/sanic-org/sanic/pull/2451) Trigger `http.lifecycle.request` signal in ASGI mode +- [#2455](https://github.com/sanic-org/sanic/pull/2455) Resolve typing of stacked route definitions +- [#2463](https://github.com/sanic-org/sanic/pull/2463) Properly catch websocket CancelledError in websocket handler in Python 3.7 + +### Deprecations and Removals + +- [#2487](https://github.com/sanic-org/sanic/pull/2487) v22.6 deprecations and changes + 1. Optional application registry + 2. Execution of custom handlers after some part of response was sent + 3. Configuring fallback handlers on the `ErrorHandler` + 4. Custom `LOGO` setting + 5. `sanic.response.stream` + 6. `AsyncioServer.init` + +### Developer infrastructure + +- [#2449](https://github.com/sanic-org/sanic/pull/2449) Clean up `black` and `isort` config +- [#2479](https://github.com/sanic-org/sanic/pull/2479) Fix some flappy tests + +### Improved Documentation + +- [#2461](https://github.com/sanic-org/sanic/pull/2461) Update example to match current application naming standards +- [#2466](https://github.com/sanic-org/sanic/pull/2466) Better type annotation for `Extend` +- [#2485](https://github.com/sanic-org/sanic/pull/2485) Improved help messages in CLI + +## Version 22.3.0 + +### Features + +- [#2347](https://github.com/sanic-org/sanic/pull/2347) API for multi-application server + - 🚨 _BREAKING CHANGE_: The old `sanic.worker.GunicornWorker` has been **removed**. To run Sanic with `gunicorn`, you should use it thru `uvicorn` [as described in their docs](https://www.uvicorn.org/#running-with-gunicorn). + - 🧁 _SIDE EFFECT_: Named background tasks are now supported, even in Python 3.7 +- [#2357](https://github.com/sanic-org/sanic/pull/2357) Parse `Authorization` header as `Request.credentials` +- [#2361](https://github.com/sanic-org/sanic/pull/2361) Add config option to skip `Touchup` step in application startup +- [#2372](https://github.com/sanic-org/sanic/pull/2372) Updates to CLI help messaging +- [#2382](https://github.com/sanic-org/sanic/pull/2382) Downgrade warnings to backwater debug messages +- [#2396](https://github.com/sanic-org/sanic/pull/2396) Allow for `multidict` v0.6 +- [#2401](https://github.com/sanic-org/sanic/pull/2401) Upgrade CLI catching for alternative application run types +- [#2402](https://github.com/sanic-org/sanic/pull/2402) Conditionally inject CLI arguments into factory +- [#2413](https://github.com/sanic-org/sanic/pull/2413) Add new start and stop event listeners to reloader process +- [#2414](https://github.com/sanic-org/sanic/pull/2414) Remove loop as required listener arg +- [#2415](https://github.com/sanic-org/sanic/pull/2415) Better exception for bad URL parsing +- [sanic-routing#47](https://github.com/sanic-org/sanic-routing/pull/47) Add a new extention parameter type: ``, ``, ``, ``, ``, `` + - 👶 _BETA FEATURE_: This feature will not work with `path` type matching, and is being released as a beta feature only. +- [sanic-routing#57](https://github.com/sanic-org/sanic-routing/pull/57) Change `register_pattern` to accept a `str` or `Pattern` +- [sanic-routing#58](https://github.com/sanic-org/sanic-routing/pull/58) Default matching on non-empty strings only, and new `strorempty` pattern type + - 🚨 _BREAKING CHANGE_: Previously a route with a dynamic string parameter (`/` or `/`) would match on any string, including empty strings. It will now **only** match a non-empty string. To retain the old behavior, you should use the new parameter type: `/`. + +### Bugfixes + +- [#2373](https://github.com/sanic-org/sanic/pull/2373) Remove `error_logger` on websockets +- [#2381](https://github.com/sanic-org/sanic/pull/2381) Fix newly assigned `None` in task registry +- [sanic-routing#52](https://github.com/sanic-org/sanic-routing/pull/52) Add type casting to regex route matching +- [sanic-routing#60](https://github.com/sanic-org/sanic-routing/pull/60) Add requirements check on regex routes (this resolves, for example, multiple static directories with differing `host` values) + +### Deprecations and Removals + +- [#2362](https://github.com/sanic-org/sanic/pull/2362) 22.3 Deprecations and changes + 1. `debug=True` and `--debug` do _NOT_ automatically run `auto_reload` + 2. Default error render is with plain text (browsers still get HTML by default because `auto` looks at headers) + 3. `config` is required for `ErrorHandler.finalize` + 4. `ErrorHandler.lookup` requires two positional args + 5. Unused websocket protocol args removed +- [#2344](https://github.com/sanic-org/sanic/pull/2344) Deprecate loading of lowercase environment variables + +### Developer infrastructure + +- [#2363](https://github.com/sanic-org/sanic/pull/2363) Revert code coverage back to Codecov +- [#2405](https://github.com/sanic-org/sanic/pull/2405) Upgrade tests for `sanic-routing` changes +- [sanic-testing#35](https://github.com/sanic-org/sanic-testing/pull/35) Allow for httpx v0.22 + +### Improved Documentation + +- [#2350](https://github.com/sanic-org/sanic/pull/2350) Fix link in README for ASGI +- [#2398](https://github.com/sanic-org/sanic/pull/2398) Document middleware on_request and on_response +- [#2409](https://github.com/sanic-org/sanic/pull/2409) Add missing documentation for `Request.respond` + +### Miscellaneous + +- [#2376](https://github.com/sanic-org/sanic/pull/2376) Fix typing for `ListenerMixin.listener` +- [#2383](https://github.com/sanic-org/sanic/pull/2383) Clear deprecation warning in `asyncio.wait` +- [#2387](https://github.com/sanic-org/sanic/pull/2387) Cleanup `__slots__` implementations +- [#2390](https://github.com/sanic-org/sanic/pull/2390) Clear deprecation warning in `asyncio.get_event_loop` + +## Version 21.12.1 + +- [#2349](https://github.com/sanic-org/sanic/pull/2349) Only display MOTD on startup +- [#2354](https://github.com/sanic-org/sanic/pull/2354) Ignore name argument in Python 3.7 +- [#2355](https://github.com/sanic-org/sanic/pull/2355) Add config.update support for all config values + +## Version 21.12.0 + +### Features + +- [#2260](https://github.com/sanic-org/sanic/pull/2260) Allow early Blueprint registrations to still apply later added objects +- [#2262](https://github.com/sanic-org/sanic/pull/2262) Noisy exceptions - force logging of all exceptions +- [#2264](https://github.com/sanic-org/sanic/pull/2264) Optional `uvloop` by configuration +- [#2270](https://github.com/sanic-org/sanic/pull/2270) Vhost support using multiple TLS certificates +- [#2277](https://github.com/sanic-org/sanic/pull/2277) Change signal routing for increased consistency + - _BREAKING CHANGE_: If you were manually routing signals there is a breaking change. The signal router's `get` is no longer 100% determinative. There is now an additional step to loop thru the returned signals for proper matching on the requirements. If signals are being dispatched using `app.dispatch` or `bp.dispatch`, there is no change. +- [#2290](https://github.com/sanic-org/sanic/pull/2290) Add contextual exceptions +- [#2291](https://github.com/sanic-org/sanic/pull/2291) Increase join concat performance +- [#2295](https://github.com/sanic-org/sanic/pull/2295), [#2316](https://github.com/sanic-org/sanic/pull/2316), [#2331](https://github.com/sanic-org/sanic/pull/2331) Restructure of CLI and application state with new displays and more command parity with `app.run` +- [#2302](https://github.com/sanic-org/sanic/pull/2302) Add route context at definition time +- [#2304](https://github.com/sanic-org/sanic/pull/2304) Named tasks and new API for managing background tasks +- [#2307](https://github.com/sanic-org/sanic/pull/2307) On app auto-reload, provide insight of changed files +- [#2308](https://github.com/sanic-org/sanic/pull/2308) Auto extend application with [Sanic Extensions](https://sanicframework.org/en/plugins/sanic-ext/getting-started.html) if it is installed, and provide first class support for accessing the extensions +- [#2309](https://github.com/sanic-org/sanic/pull/2309) Builtin signals changed to `Enum` +- [#2313](https://github.com/sanic-org/sanic/pull/2313) Support additional config implementation use case +- [#2321](https://github.com/sanic-org/sanic/pull/2321) Refactor environment variable hydration logic +- [#2327](https://github.com/sanic-org/sanic/pull/2327) Prevent sending multiple or mixed responses on a single request +- [#2330](https://github.com/sanic-org/sanic/pull/2330) Custom type casting on environment variables +- [#2332](https://github.com/sanic-org/sanic/pull/2332) Make all deprecation notices consistent +- [#2335](https://github.com/sanic-org/sanic/pull/2335) Allow underscore to start instance names + +### Bugfixes + +- [#2273](https://github.com/sanic-org/sanic/pull/2273) Replace assignation by typing for `websocket_handshake` +- [#2285](https://github.com/sanic-org/sanic/pull/2285) Fix IPv6 display in startup logs +- [#2299](https://github.com/sanic-org/sanic/pull/2299) Dispatch `http.lifecyle.response` from exception handler + +### Deprecations and Removals + +- [#2306](https://github.com/sanic-org/sanic/pull/2306) Removal of deprecated items + - `Sanic` and `Blueprint` may no longer have arbitrary properties attached to them + - `Sanic` and `Blueprint` forced to have compliant names + - alphanumeric + `_` + `-` + - must start with letter or `_` + - `load_env` keyword argument of `Sanic` + - `sanic.exceptions.abort` + - `sanic.views.CompositionView` + - `sanic.response.StreamingHTTPResponse` + - _NOTE:_ the `stream()` response method (where you pass a callable streaming function) has been deprecated and will be removed in v22.6. You should upgrade all streaming responses to the new style: https://sanicframework.org/en/guide/advanced/streaming.html#response-streaming +- [#2320](https://github.com/sanic-org/sanic/pull/2320) Remove app instance from Config for error handler setting + +### Developer infrastructure + +- [#2251](https://github.com/sanic-org/sanic/pull/2251) Change dev install command +- [#2286](https://github.com/sanic-org/sanic/pull/2286) Change codeclimate complexity threshold from 5 to 10 +- [#2287](https://github.com/sanic-org/sanic/pull/2287) Update host test function names so they are not overwritten +- [#2292](https://github.com/sanic-org/sanic/pull/2292) Fail CI on error +- [#2311](https://github.com/sanic-org/sanic/pull/2311), [#2324](https://github.com/sanic-org/sanic/pull/2324) Do not run tests for draft PRs +- [#2336](https://github.com/sanic-org/sanic/pull/2336) Remove paths from coverage checks +- [#2338](https://github.com/sanic-org/sanic/pull/2338) Cleanup ports on tests + +### Improved Documentation + +- [#2269](https://github.com/sanic-org/sanic/pull/2269), [#2329](https://github.com/sanic-org/sanic/pull/2329), [#2333](https://github.com/sanic-org/sanic/pull/2333) Cleanup typos and fix language + +### Miscellaneous + +- [#2257](https://github.com/sanic-org/sanic/pull/2257), [#2294](https://github.com/sanic-org/sanic/pull/2294), [#2341](https://github.com/sanic-org/sanic/pull/2341) Add Python 3.10 support +- [#2279](https://github.com/sanic-org/sanic/pull/2279), [#2317](https://github.com/sanic-org/sanic/pull/2317), [#2322](https://github.com/sanic-org/sanic/pull/2322) Add/correct missing type annotations +- [#2305](https://github.com/sanic-org/sanic/pull/2305) Fix examples to use modern implementations + +## Version 21.9.3 + +_Rerelease of v21.9.2 with some cleanup_ + +## Version 21.9.2 + +- [#2268](https://github.com/sanic-org/sanic/pull/2268) Make HTTP connections start in IDLE stage, avoiding delays and error messages +- [#2310](https://github.com/sanic-org/sanic/pull/2310) More consistent config setting with post-FALLBACK_ERROR_FORMAT apply + +## Version 21.9.1 + +- [#2259](https://github.com/sanic-org/sanic/pull/2259) Allow non-conforming ErrorHandlers + +## Version 21.9.0 + +### Features + +- [#2158](https://github.com/sanic-org/sanic/pull/2158), [#2248](https://github.com/sanic-org/sanic/pull/2248) Complete overhaul of I/O to websockets +- [#2160](https://github.com/sanic-org/sanic/pull/2160) Add new 17 signals into server and request lifecycles +- [#2162](https://github.com/sanic-org/sanic/pull/2162) Smarter `auto` fallback formatting upon exception +- [#2184](https://github.com/sanic-org/sanic/pull/2184) Introduce implementation for copying a Blueprint +- [#2200](https://github.com/sanic-org/sanic/pull/2200) Accept header parsing +- [#2207](https://github.com/sanic-org/sanic/pull/2207) Log remote address if available +- [#2209](https://github.com/sanic-org/sanic/pull/2209) Add convenience methods to BP groups +- [#2216](https://github.com/sanic-org/sanic/pull/2216) Add default messages to SanicExceptions +- [#2225](https://github.com/sanic-org/sanic/pull/2225) Type annotation convenience for annotated handlers with path parameters +- [#2236](https://github.com/sanic-org/sanic/pull/2236) Allow Falsey (but not-None) responses from route handlers +- [#2238](https://github.com/sanic-org/sanic/pull/2238) Add `exception` decorator to Blueprint Groups +- [#2244](https://github.com/sanic-org/sanic/pull/2244) Explicit static directive for serving file or dir (ex: `static(..., resource_type="file")`) +- [#2245](https://github.com/sanic-org/sanic/pull/2245) Close HTTP loop when connection task cancelled + +### Bugfixes + +- [#2188](https://github.com/sanic-org/sanic/pull/2188) Fix the handling of the end of a chunked request +- [#2195](https://github.com/sanic-org/sanic/pull/2195) Resolve unexpected error handling on static requests +- [#2208](https://github.com/sanic-org/sanic/pull/2208) Make blueprint-based exceptions attach and trigger in a more intuitive manner +- [#2211](https://github.com/sanic-org/sanic/pull/2211) Fixed for handling exceptions of asgi app call +- [#2213](https://github.com/sanic-org/sanic/pull/2213) Fix bug where ws exceptions not being logged +- [#2231](https://github.com/sanic-org/sanic/pull/2231) Cleaner closing of tasks by using `abort()` in strategic places to avoid dangling sockets +- [#2247](https://github.com/sanic-org/sanic/pull/2247) Fix logging of auto-reload status in debug mode +- [#2246](https://github.com/sanic-org/sanic/pull/2246) Account for BP with exception handler but no routes + +### Developer infrastructure + +- [#2194](https://github.com/sanic-org/sanic/pull/2194) HTTP unit tests with raw client +- [#2199](https://github.com/sanic-org/sanic/pull/2199) Switch to codeclimate +- [#2214](https://github.com/sanic-org/sanic/pull/2214) Try Reopening Windows Tests +- [#2229](https://github.com/sanic-org/sanic/pull/2229) Refactor `HttpProtocol` into a base class +- [#2230](https://github.com/sanic-org/sanic/pull/2230) Refactor `server.py` into multi-file module + +### Miscellaneous + +- [#2173](https://github.com/sanic-org/sanic/pull/2173) Remove Duplicated Dependencies and PEP 517 Support +- [#2193](https://github.com/sanic-org/sanic/pull/2193), [#2196](https://github.com/sanic-org/sanic/pull/2196), [#2217](https://github.com/sanic-org/sanic/pull/2217) Type annotation changes + +## Version 21.6.1 + +**Bugfixes** + +- [#2178](https://github.com/sanic-org/sanic/pull/2178) Update + sanic-routing to allow for better splitting of complex URI + templates +- [#2183](https://github.com/sanic-org/sanic/pull/2183) Proper + handling of chunked request bodies to resolve phantom 503 in logs +- [#2181](https://github.com/sanic-org/sanic/pull/2181) Resolve + regression in exception logging +- [#2201](https://github.com/sanic-org/sanic/pull/2201) Cleanup + request info in pipelined requests + +## Version 21.6.0 + +**Features** + +- [#2094](https://github.com/sanic-org/sanic/pull/2094) Add + `response.eof()` method for closing a stream in a handler + +- [#2097](https://github.com/sanic-org/sanic/pull/2097) Allow + case-insensitive HTTP Upgrade header + +- [#2104](https://github.com/sanic-org/sanic/pull/2104) Explicit + usage of CIMultiDict getters + +- [#2109](https://github.com/sanic-org/sanic/pull/2109) Consistent + use of error loggers + +- [#2114](https://github.com/sanic-org/sanic/pull/2114) New + `client_ip` access of connection info instance + +- [#2119](https://github.com/sanic-org/sanic/pull/2119) Alternatate + classes on instantiation for `Config` and `Sanic.ctx` + +- [#2133](https://github.com/sanic-org/sanic/pull/2133) Implement + new version of AST router + + - Proper differentiation between `alpha` and `string` param + types + - Adds a `slug` param type, example: `` + - Deprecates `` in favor of `` + - Deprecates `` in favor of `` + - Adds a `route.uri` accessor + +- [#2136](https://github.com/sanic-org/sanic/pull/2136) CLI + improvements with new optional params + +- [#2137](https://github.com/sanic-org/sanic/pull/2137) Add + `version_prefix` to URL builders + +- [#2140](https://github.com/sanic-org/sanic/pull/2140) Event + autoregistration with `EVENT_AUTOREGISTER` + +- [#2146](https://github.com/sanic-org/sanic/pull/2146), + [#2147](https://github.com/sanic-org/sanic/pull/2147) Require + stricter names on `Sanic()` and `Blueprint()` + +- [#2150](https://github.com/sanic-org/sanic/pull/2150) Infinitely + reusable and nestable `Blueprint` and `BlueprintGroup` + +- [#2154](https://github.com/sanic-org/sanic/pull/2154) Upgrade + `websockets` dependency to min version + +- [#2155](https://github.com/sanic-org/sanic/pull/2155) Allow for + maximum header sizes to be increased: `REQUEST_MAX_HEADER_SIZE` + +- [#2157](https://github.com/sanic-org/sanic/pull/2157) Allow app + factory pattern in CLI + +- [#2165](https://github.com/sanic-org/sanic/pull/2165) Change HTTP + methods to enums + +- [#2167](https://github.com/sanic-org/sanic/pull/2167) Allow + auto-reloading on additional directories + +- [#2168](https://github.com/sanic-org/sanic/pull/2168) Add simple + HTTP server to CLI + +- [#2170](https://github.com/sanic-org/sanic/pull/2170) Additional + methods for attaching `HTTPMethodView` + +**Bugfixes** + +- [#2091](https://github.com/sanic-org/sanic/pull/2091) Fix + `UserWarning` in ASGI mode for missing `__slots__` +- [#2099](https://github.com/sanic-org/sanic/pull/2099) Fix static + request handler logging exception on 404 +- [#2110](https://github.com/sanic-org/sanic/pull/2110) Fix + request.args.pop removes parameters inconsistently +- [#2107](https://github.com/sanic-org/sanic/pull/2107) Fix type + hinting for load_env +- [#2127](https://github.com/sanic-org/sanic/pull/2127) Make sure + ASGI ws subprotocols is a list +- [#2128](https://github.com/sanic-org/sanic/pull/2128) Fix issue + where Blueprint exception handlers do not consistently route to + proper handler + +**Deprecations and Removals** + +- [#2156](https://github.com/sanic-org/sanic/pull/2156) Remove + config value `REQUEST_BUFFER_QUEUE_SIZE` +- [#2170](https://github.com/sanic-org/sanic/pull/2170) + `CompositionView` deprecated and marked for removal in 21.12 +- [#2172](https://github.com/sanic-org/sanic/pull/2170) Deprecate + StreamingHTTPResponse + +**Developer infrastructure** + +- [#2149](https://github.com/sanic-org/sanic/pull/2149) Remove + Travis CI in favor of GitHub Actions + +**Improved Documentation** + +- [#2164](https://github.com/sanic-org/sanic/pull/2164) Fix typo in + documentation +- [#2100](https://github.com/sanic-org/sanic/pull/2100) Remove + documentation for non-existent arguments + +## Version 21.3.2 + +**Bugfixes** + +- [#2081](https://github.com/sanic-org/sanic/pull/2081) Disable + response timeout on websocket connections +- [#2085](https://github.com/sanic-org/sanic/pull/2085) Make sure + that blueprints with no slash is maintained when applied + +## Version 21.3.1 + +**Bugfixes** + +- [#2076](https://github.com/sanic-org/sanic/pull/2076) Static files + inside subfolders are not accessible (404) + +## Version 21.3.0 + +Release +Notes + +**Features** + +- [#1876](https://github.com/sanic-org/sanic/pull/1876) Unified + streaming server +- [#2005](https://github.com/sanic-org/sanic/pull/2005) New + `Request.id` property +- [#2008](https://github.com/sanic-org/sanic/pull/2008) Allow + Pathlib Path objects to be passed to `app.static()` helper +- [#2010](https://github.com/sanic-org/sanic/pull/2010), + [#2031](https://github.com/sanic-org/sanic/pull/2031) New + startup-optimized router +- [#2018](https://github.com/sanic-org/sanic/pull/2018) + [#2064](https://github.com/sanic-org/sanic/pull/2064) Listeners + for main server process +- [#2032](https://github.com/sanic-org/sanic/pull/2032) Add raw + header info to request object +- [#2042](https://github.com/sanic-org/sanic/pull/2042) + [#2060](https://github.com/sanic-org/sanic/pull/2060) + [#2061](https://github.com/sanic-org/sanic/pull/2061) Introduce + Signals API +- [#2043](https://github.com/sanic-org/sanic/pull/2043) Add + `__str__` and `__repr__` to Sanic and Blueprint +- [#2047](https://github.com/sanic-org/sanic/pull/2047) Enable + versioning and strict slash on BlueprintGroup +- [#2053](https://github.com/sanic-org/sanic/pull/2053) Make + `get_app` name argument optional +- [#2055](https://github.com/sanic-org/sanic/pull/2055) JSON encoder + change via app +- [#2063](https://github.com/sanic-org/sanic/pull/2063) App and + connection level context objects + +**Bugfixes** + +- Resolve [#1420](https://github.com/sanic-org/sanic/pull/1420) + `url_for` where `strict_slashes` are on for a path ending in `/` +- Resolve [#1525](https://github.com/sanic-org/sanic/pull/1525) + Routing is incorrect with some special characters +- Resolve [#1653](https://github.com/sanic-org/sanic/pull/1653) ASGI + headers in body +- Resolve [#1722](https://github.com/sanic-org/sanic/pull/1722) + Using curl in chunk mode +- Resolve [#1730](https://github.com/sanic-org/sanic/pull/1730) + Extra content in ASGI streaming response +- Resolve [#1749](https://github.com/sanic-org/sanic/pull/1749) + Restore broken middleware edge cases +- Resolve [#1785](https://github.com/sanic-org/sanic/pull/1785) + [#1804](https://github.com/sanic-org/sanic/pull/1804) Synchronous + error handlers +- Resolve [#1790](https://github.com/sanic-org/sanic/pull/1790) + Protocol errors did not support async error handlers #1790 +- Resolve [#1824](https://github.com/sanic-org/sanic/pull/1824) + Timeout on specific methods +- Resolve [#1875](https://github.com/sanic-org/sanic/pull/1875) + Response timeout error from all routes after returning several + timeouts from a specific route +- Resolve [#1988](https://github.com/sanic-org/sanic/pull/1988) + Handling of safe methods with body +- [#2001](https://github.com/sanic-org/sanic/pull/2001) Raise + ValueError when cookie max-age is not an integer + +**Deprecations and Removals** + +- [#2007](https://github.com/sanic-org/sanic/pull/2007) \* Config + using `from_envvar` \* Config using `from_pyfile` \* Config using + `from_object` +- [#2009](https://github.com/sanic-org/sanic/pull/2009) Remove Sanic + test client to its own package +- [#2036](https://github.com/sanic-org/sanic/pull/2036), + [#2037](https://github.com/sanic-org/sanic/pull/2037) Drop Python + 3.6 support +- `Request.endpoint` deprecated in favor of `Request.name` +- handler type name prefixes removed (static, websocket, etc) + +**Developer infrastructure** + +- [#1995](https://github.com/sanic-org/sanic/pull/1995) Create + FUNDING.yml +- [#2013](https://github.com/sanic-org/sanic/pull/2013) Add codeql + to CI pipeline +- [#2038](https://github.com/sanic-org/sanic/pull/2038) Codecov + configuration updates +- [#2049](https://github.com/sanic-org/sanic/pull/2049) Updated + setup.py to use `find_packages` + +**Improved Documentation** + +- [#1218](https://github.com/sanic-org/sanic/pull/1218) + Documentation for sanic.log.\* is missing +- [#1608](https://github.com/sanic-org/sanic/pull/1608) Add + documentation on calver and LTS +- [#1731](https://github.com/sanic-org/sanic/pull/1731) Support + mounting application elsewhere than at root path +- [#2006](https://github.com/sanic-org/sanic/pull/2006) Upgraded + type annotations and improved docstrings and API documentation +- [#2052](https://github.com/sanic-org/sanic/pull/2052) Fix some + examples and docs + +**Miscellaneous** + +- `Request.route` property +- Better websocket subprotocols support +- Resolve bug with middleware in Blueprint Group when passed + callable +- Moves common logic between Blueprint and Sanic into mixins +- Route naming changed to be more consistent + - request endpoint is the route name + - route names are fully namespaced +- Some new convenience decorators: + - `@app.main_process_start` + - `@app.main_process_stop` + - `@app.before_server_start` + - `@app.after_server_start` + - `@app.before_server_stop` + - `@app.after_server_stop` + - `@app.on_request` + - `@app.on_response` +- Fixes `Allow` header that did not include `HEAD` +- Using \"name\" keyword in `url_for` for a \"static\" route where + name does not exist +- Cannot have multiple `app.static()` without using the named param +- Using \"filename\" keyword in `url_for` on a file route +- `unquote` in route def (not automatic) +- `routes_all` is tuples +- Handler arguments are kwarg only +- `request.match_info` is now a cached (and not computed) property +- Unknown static file mimetype is sent as `application/octet-stream` +- `_host` keyword in `url_for` +- Add charset default to `utf-8` for text and js content types if + not specified +- Version for a route can be str, float, or int +- Route has ctx property +- App has `routes_static`, `routes_dynamic`, `routes_regex` +- [#2044](https://github.com/sanic-org/sanic/pull/2044) Code cleanup + and refactoring +- [#2072](https://github.com/sanic-org/sanic/pull/2072) Remove + `BaseSanic` metaclass +- [#2074](https://github.com/sanic-org/sanic/pull/2074) Performance + adjustments in `handle_request_` + +## Version 20.12.3 + +**Bugfixes** + +- [#2021](https://github.com/sanic-org/sanic/pull/2021) Remove + prefix from websocket handler name + +## Version 20.12.2 + +**Dependencies** + +- [#2026](https://github.com/sanic-org/sanic/pull/2026) Fix uvloop + to 0.14 because 0.15 drops Python 3.6 support +- [#2029](https://github.com/sanic-org/sanic/pull/2029) Remove old + chardet requirement, add in hard multidict requirement + +## Version 19.12.5 + +**Dependencies** + +- [#2025](https://github.com/sanic-org/sanic/pull/2025) Fix uvloop + to 0.14 because 0.15 drops Python 3.6 support +- [#2027](https://github.com/sanic-org/sanic/pull/2027) Remove old + chardet requirement, add in hard multidict requirement + +## Version 20.12.0 + +**Features** + +- [#1993](https://github.com/sanic-org/sanic/pull/1993) Add disable + app registry +- [#1945](https://github.com/sanic-org/sanic/pull/1945) Static route + more verbose if file not found +- [#1954](https://github.com/sanic-org/sanic/pull/1954) Fix static + routes registration on a blueprint +- [#1961](https://github.com/sanic-org/sanic/pull/1961) Add Python + 3.9 support +- [#1962](https://github.com/sanic-org/sanic/pull/1962) Sanic CLI + upgrade +- [#1967](https://github.com/sanic-org/sanic/pull/1967) Update + aiofile version requirements +- [#1969](https://github.com/sanic-org/sanic/pull/1969) Update + multidict version requirements +- [#1970](https://github.com/sanic-org/sanic/pull/1970) Add py.typed + file +- [#1972](https://github.com/sanic-org/sanic/pull/1972) Speed + optimization in request handler +- [#1979](https://github.com/sanic-org/sanic/pull/1979) Add app + registry and Sanic class level app retrieval + +**Bugfixes** + +- [#1965](https://github.com/sanic-org/sanic/pull/1965) Fix Chunked + Transport-Encoding in ASGI streaming response + +**Deprecations and Removals** + +- [#1981](https://github.com/sanic-org/sanic/pull/1981) Cleanup and + remove deprecated code + +**Developer infrastructure** + +- [#1956](https://github.com/sanic-org/sanic/pull/1956) Fix load + module test +- [#1973](https://github.com/sanic-org/sanic/pull/1973) Transition + Travis from .org to .com +- [#1986](https://github.com/sanic-org/sanic/pull/1986) Update tox + requirements + +**Improved Documentation** + +- [#1951](https://github.com/sanic-org/sanic/pull/1951) + Documentation improvements +- [#1983](https://github.com/sanic-org/sanic/pull/1983) Remove + duplicate contents in testing.rst +- [#1984](https://github.com/sanic-org/sanic/pull/1984) Fix typo in + routing.rst + +## Version 20.9.1 + +**Bugfixes** + +- [#1954](https://github.com/sanic-org/sanic/pull/1954) Fix static + route registration on blueprints +- [#1957](https://github.com/sanic-org/sanic/pull/1957) Removes + duplicate headers in ASGI streaming body + +## Version 19.12.3 + +**Bugfixes** + +- [#1959](https://github.com/sanic-org/sanic/pull/1959) Removes + duplicate headers in ASGI streaming body + +## Version 20.9.0 + +**Features** + +- [#1887](https://github.com/sanic-org/sanic/pull/1887) Pass + subprotocols in websockets (both sanic server and ASGI) +- [#1894](https://github.com/sanic-org/sanic/pull/1894) + Automatically set `test_mode` flag on app instance +- [#1903](https://github.com/sanic-org/sanic/pull/1903) Add new + unified method for updating app values +- [#1906](https://github.com/sanic-org/sanic/pull/1906), + [#1909](https://github.com/sanic-org/sanic/pull/1909) Adds + WEBSOCKET_PING_TIMEOUT and WEBSOCKET_PING_INTERVAL configuration + values +- [#1935](https://github.com/sanic-org/sanic/pull/1935) httpx + version dependency updated, it is slated for removal as a + dependency in v20.12 +- [#1937](https://github.com/sanic-org/sanic/pull/1937) Added auto, + text, and json fallback error handlers (in v21.3, the default will + change form html to auto) + +**Bugfixes** + +- [#1897](https://github.com/sanic-org/sanic/pull/1897) Resolves + exception from unread bytes in stream + +**Deprecations and Removals** + +- [#1903](https://github.com/sanic-org/sanic/pull/1903) + config.from_envar, config.from_pyfile, and config.from_object are + deprecated and set to be removed in v21.3 + +**Developer infrastructure** + +- [#1890](https://github.com/sanic-org/sanic/pull/1890), + [#1891](https://github.com/sanic-org/sanic/pull/1891) Update isort + calls to be compatible with new API +- [#1893](https://github.com/sanic-org/sanic/pull/1893) Remove + version section from setup.cfg +- [#1924](https://github.com/sanic-org/sanic/pull/1924) Adding + \--strict-markers for pytest + +**Improved Documentation** + +- [#1922](https://github.com/sanic-org/sanic/pull/1922) Add explicit + ASGI compliance to the README + +## Version 20.6.3 + +**Bugfixes** + +- [#1884](https://github.com/sanic-org/sanic/pull/1884) Revert + change to multiprocessing mode + +## Version 20.6.2 + +**Features** + +- [#1641](https://github.com/sanic-org/sanic/pull/1641) Socket + binding implemented properly for IPv6 and UNIX sockets + +## Version 20.6.1 + +**Features** + +- [#1760](https://github.com/sanic-org/sanic/pull/1760) Add version + parameter to websocket routes +- [#1866](https://github.com/sanic-org/sanic/pull/1866) Add `sanic` + as an entry point command +- [#1880](https://github.com/sanic-org/sanic/pull/1880) Add handler + names for websockets for url_for usage + +**Bugfixes** + +- [#1776](https://github.com/sanic-org/sanic/pull/1776) Bug fix for + host parameter issue with lists +- [#1842](https://github.com/sanic-org/sanic/pull/1842) Fix static + \_handler pickling error +- [#1827](https://github.com/sanic-org/sanic/pull/1827) Fix reloader + on OSX py38 and Windows +- [#1848](https://github.com/sanic-org/sanic/pull/1848) Reverse + named_response_middlware execution order, to match normal response + middleware execution order +- [#1853](https://github.com/sanic-org/sanic/pull/1853) Fix pickle + error when attempting to pickle an application which contains + websocket routes + +**Deprecations and Removals** + +- [#1739](https://github.com/sanic-org/sanic/pull/1739) Deprecate + body_bytes to merge into body + +**Developer infrastructure** + +- [#1852](https://github.com/sanic-org/sanic/pull/1852) Fix naming + of CI test env on Python nightlies +- [#1857](https://github.com/sanic-org/sanic/pull/1857) Adjust + websockets version to setup.py +- [#1869](https://github.com/sanic-org/sanic/pull/1869) Wrap + run()\'s \"protocol\" type annotation in Optional\[\] + +**Improved Documentation** + +- [#1846](https://github.com/sanic-org/sanic/pull/1846) Update docs + to clarify response middleware execution order +- [#1865](https://github.com/sanic-org/sanic/pull/1865) Fixing rst + format issue that was hiding documentation + +## Version 20.6.0 + +_Released, but unintentionally omitting PR #1880, so was replaced by +20.6.1_ + +## Version 20.3.0 + +**Features** + +- [#1762](https://github.com/sanic-org/sanic/pull/1762) Add + `srv.start_serving()` and `srv.serve_forever()` to `AsyncioServer` +- [#1767](https://github.com/sanic-org/sanic/pull/1767) Make Sanic + usable on `hypercorn -k trio myweb.app` +- [#1768](https://github.com/sanic-org/sanic/pull/1768) No + tracebacks on normal errors and prettier error pages +- [#1769](https://github.com/sanic-org/sanic/pull/1769) Code cleanup + in file responses +- [#1793](https://github.com/sanic-org/sanic/pull/1793) and + [#1819](https://github.com/sanic-org/sanic/pull/1819) Upgrade + `str.format()` to f-strings +- [#1798](https://github.com/sanic-org/sanic/pull/1798) Allow + multiple workers on MacOS with Python 3.8 +- [#1820](https://github.com/sanic-org/sanic/pull/1820) Do not set + content-type and content-length headers in exceptions + +**Bugfixes** + +- [#1748](https://github.com/sanic-org/sanic/pull/1748) Remove loop + argument in `asyncio.Event` in Python 3.8 +- [#1764](https://github.com/sanic-org/sanic/pull/1764) Allow route + decorators to stack up again +- [#1789](https://github.com/sanic-org/sanic/pull/1789) Fix tests + using hosts yielding incorrect `url_for` +- [#1808](https://github.com/sanic-org/sanic/pull/1808) Fix Ctrl+C + and tests on Windows + +**Deprecations and Removals** + +- [#1800](https://github.com/sanic-org/sanic/pull/1800) Begin + deprecation in way of first-class streaming, removal of + `body_init`, `body_push`, and `body_finish` +- [#1801](https://github.com/sanic-org/sanic/pull/1801) Complete + deprecation from + [#1666](https://github.com/sanic-org/sanic/pull/1666) of + dictionary context on `request` objects. +- [#1807](https://github.com/sanic-org/sanic/pull/1807) Remove + server config args that can be read directly from app +- [#1818](https://github.com/sanic-org/sanic/pull/1818) Complete + deprecation of `app.remove_route` and `request.raw_args` + +**Dependencies** + +- [#1794](https://github.com/sanic-org/sanic/pull/1794) Bump `httpx` + to 0.11.1 +- [#1806](https://github.com/sanic-org/sanic/pull/1806) Import + `ASGIDispatch` from top-level `httpx` (from third-party + deprecation) + +**Developer infrastructure** + +- [#1833](https://github.com/sanic-org/sanic/pull/1833) Resolve + broken documentation builds + +**Improved Documentation** + +- [#1755](https://github.com/sanic-org/sanic/pull/1755) Usage of + `response.empty()` +- [#1778](https://github.com/sanic-org/sanic/pull/1778) Update + README +- [#1783](https://github.com/sanic-org/sanic/pull/1783) Fix typo +- [#1784](https://github.com/sanic-org/sanic/pull/1784) Corrected + changelog for docs move of MD to RST + ([#1691](https://github.com/sanic-org/sanic/pull/1691)) +- [#1803](https://github.com/sanic-org/sanic/pull/1803) Update + config docs to match DEFAULT_CONFIG +- [#1814](https://github.com/sanic-org/sanic/pull/1814) Update + getting_started.rst +- [#1821](https://github.com/sanic-org/sanic/pull/1821) Update to + deployment +- [#1822](https://github.com/sanic-org/sanic/pull/1822) Update docs + with changes done in 20.3 +- [#1834](https://github.com/sanic-org/sanic/pull/1834) Order of + listeners + +## Version 19.12.0 + +**Bugfixes** + +- Fix blueprint middleware application + + Currently, any blueprint middleware registered, irrespective of + which blueprint was used to do so, was being applied to all of the + routes created by the `@app` and `@blueprint` alike. + + As part of this change, the blueprint based middleware application + is enforced based on where they are registered. + + - If you register a middleware via `@blueprint.middleware` then it + will apply only to the routes defined by the blueprint. + - If you register a middleware via `@blueprint_group.middleware` + then it will apply to all blueprint based routes that are part + of the group. + - If you define a middleware via `@app.middleware` then it will be + applied on all available routes + ([#37](https://github.com/sanic-org/sanic/issues/37)) + +- Fix [url_for]{.title-ref} behavior with missing SERVER_NAME + + If the [SERVER_NAME]{.title-ref} was missing in the + [app.config]{.title-ref} entity, the [url_for]{.title-ref} on the + [request]{.title-ref} and [app]{.title-ref} were failing due to an + [AttributeError]{.title-ref}. This fix makes the availability of + [SERVER_NAME]{.title-ref} on our [app.config]{.title-ref} an + optional behavior. + ([#1707](https://github.com/sanic-org/sanic/issues/1707)) + +**Improved Documentation** + +- Move docs from MD to RST + + Moved all docs from markdown to restructured text like the rest of + the docs to unify the scheme and make it easier in the future to + update documentation. + ([#1691](https://github.com/sanic-org/sanic/issues/1691)) + +- Fix documentation for [get]{.title-ref} and [getlist]{.title-ref} of + the [request.args]{.title-ref} + + Add additional example for showing the usage of + [getlist]{.title-ref} and fix the documentation string for + [request.args]{.title-ref} behavior + ([#1704](https://github.com/sanic-org/sanic/issues/1704)) + +## Version 19.6.3 + +**Features** + +- Enable Towncrier Support + + As part of this feature, [towncrier]{.title-ref} is being introduced + as a mechanism to partially automate the process of generating and + managing change logs as part of each of pull requests. + ([#1631](https://github.com/sanic-org/sanic/issues/1631)) + +**Improved Documentation** + +- Documentation infrastructure changes + - Enable having a single common [CHANGELOG]{.title-ref} file for + both GitHub page and documentation + - Fix Sphinix deprecation warnings + - Fix documentation warnings due to invalid [rst]{.title-ref} + indentation + - Enable common contribution guidelines file across GitHub and + documentation via [CONTRIBUTING.rst]{.title-ref} + ([#1631](https://github.com/sanic-org/sanic/issues/1631)) + +## Version 19.6.2 + +**Features** + +- [#1562](https://github.com/sanic-org/sanic/pull/1562) Remove + `aiohttp` dependency and create new `SanicTestClient` based upon + [requests-async](https://github.com/encode/requests-async) +- [#1475](https://github.com/sanic-org/sanic/pull/1475) Added ASGI + support (Beta) +- [#1436](https://github.com/sanic-org/sanic/pull/1436) Add + Configure support from object string + +**Bugfixes** + +- [#1587](https://github.com/sanic-org/sanic/pull/1587) Add missing + handle for Expect header. +- [#1560](https://github.com/sanic-org/sanic/pull/1560) Allow to + disable Transfer-Encoding: chunked. +- [#1558](https://github.com/sanic-org/sanic/pull/1558) Fix graceful + shutdown. +- [#1594](https://github.com/sanic-org/sanic/pull/1594) Strict + Slashes behavior fix + +**Deprecations and Removals** + +- [#1544](https://github.com/sanic-org/sanic/pull/1544) Drop + dependency on distutil +- [#1562](https://github.com/sanic-org/sanic/pull/1562) Drop support + for Python 3.5 +- [#1568](https://github.com/sanic-org/sanic/pull/1568) Deprecate + route removal. + +.. warning:: Warning + +``` +Sanic will not support Python 3.5 from version 19.6 and forward. +However, version 18.12LTS will have its support period extended thru +December 2020, and therefore passing Python\'s official support version +3.5, which is set to expire in September 2020. +``` + +## Version 19.3 + +**Features** + +- [#1497](https://github.com/sanic-org/sanic/pull/1497) Add support + for zero-length and RFC 5987 encoded filename for + multipart/form-data requests. + +- [#1484](https://github.com/sanic-org/sanic/pull/1484) The type of + `expires` attribute of `sanic.cookies.Cookie` is now enforced to + be of type `datetime`. + +- [#1482](https://github.com/sanic-org/sanic/pull/1482) Add support + for the `stream` parameter of `sanic.Sanic.add_route()` available + to `sanic.Blueprint.add_route()`. + +- [#1481](https://github.com/sanic-org/sanic/pull/1481) Accept + negative values for route parameters with type `int` or `number`. + +- [#1476](https://github.com/sanic-org/sanic/pull/1476) Deprecated + the use of `sanic.request.Request.raw_args` - it has a fundamental + flaw in which is drops repeated query string parameters. Added + `sanic.request.Request.query_args` as a replacement for the + original use-case. + +- [#1472](https://github.com/sanic-org/sanic/pull/1472) Remove an + unwanted `None` check in Request class `repr` implementation. This + changes the default `repr` of a Request from `` to + `` + +- [#1470](https://github.com/sanic-org/sanic/pull/1470) Added 2 new + parameters to `sanic.app.Sanic.create_server`: + + - `return_asyncio_server` - whether to return an + asyncio.Server. + - `asyncio_server_kwargs` - kwargs to pass to + `loop.create_server` for the event loop that sanic is using. + + > + + This is a breaking change. + +- [#1499](https://github.com/sanic-org/sanic/pull/1499) Added a set + of test cases that test and benchmark route resolution. + +- [#1457](https://github.com/sanic-org/sanic/pull/1457) The type of + the `"max-age"` value in a `sanic.cookies.Cookie` is now enforced + to be an integer. Non-integer values are replaced with `0`. + +- [#1445](https://github.com/sanic-org/sanic/pull/1445) Added the + `endpoint` attribute to an incoming `request`, containing the name + of the handler function. + +- [#1423](https://github.com/sanic-org/sanic/pull/1423) Improved + request streaming. `request.stream` is now a bounded-size buffer + instead of an unbounded queue. Callers must now call + `await request.stream.read()` instead of + `await request.stream.get()` to read each portion of the body. + + This is a breaking change. + +**Bugfixes** + +- [#1502](https://github.com/sanic-org/sanic/pull/1502) Sanic was + prefetching `time.time()` and updating it once per second to avoid + excessive `time.time()` calls. The implementation was observed to + cause memory leaks in some cases. The benefit of the prefetch + appeared to negligible, so this has been removed. Fixes + [#1500](https://github.com/sanic-org/sanic/pull/1500) +- [#1501](https://github.com/sanic-org/sanic/pull/1501) Fix a bug in + the auto-reloader when the process was launched as a module i.e. + `python -m init0.mod1` where the sanic server is started in + `init0/mod1.py` with `debug` enabled and imports another module in + `init0`. +- [#1376](https://github.com/sanic-org/sanic/pull/1376) Allow sanic + test client to bind to a random port by specifying `port=None` + when constructing a `SanicTestClient` +- [#1399](https://github.com/sanic-org/sanic/pull/1399) Added the + ability to specify middleware on a blueprint group, so that all + routes produced from the blueprints in the group have the + middleware applied. +- [#1442](https://github.com/sanic-org/sanic/pull/1442) Allow the + the use the `SANIC_ACCESS_LOG` environment variable to + enable/disable the access log when not explicitly passed to + `app.run()`. This allows the access log to be disabled for example + when running via gunicorn. + +**Developer infrastructure** + +- [#1529](https://github.com/sanic-org/sanic/pull/1529) Update + project PyPI credentials +- [#1515](https://github.com/sanic-org/sanic/pull/1515) fix linter + issue causing travis build failures (fix #1514) +- [#1490](https://github.com/sanic-org/sanic/pull/1490) Fix python + version in doc build +- [#1478](https://github.com/sanic-org/sanic/pull/1478) Upgrade + setuptools version and use native docutils in doc build +- [#1464](https://github.com/sanic-org/sanic/pull/1464) Upgrade + pytest, and fix caplog unit tests + +**Improved Documentation** + +- [#1516](https://github.com/sanic-org/sanic/pull/1516) Fix typo at + the exception documentation +- [#1510](https://github.com/sanic-org/sanic/pull/1510) fix typo in + Asyncio example +- [#1486](https://github.com/sanic-org/sanic/pull/1486) + Documentation typo +- [#1477](https://github.com/sanic-org/sanic/pull/1477) Fix grammar + in README.md +- [#1489](https://github.com/sanic-org/sanic/pull/1489) Added + \"databases\" to the extensions list +- [#1483](https://github.com/sanic-org/sanic/pull/1483) Add + sanic-zipkin to extensions list +- [#1487](https://github.com/sanic-org/sanic/pull/1487) Removed link + to deleted repo, Sanic-OAuth, from the extensions list +- [#1460](https://github.com/sanic-org/sanic/pull/1460) 18.12 + changelog +- [#1449](https://github.com/sanic-org/sanic/pull/1449) Add example + of amending request object +- [#1446](https://github.com/sanic-org/sanic/pull/1446) Update + README +- [#1444](https://github.com/sanic-org/sanic/pull/1444) Update + README +- [#1443](https://github.com/sanic-org/sanic/pull/1443) Update + README, including new logo +- [#1440](https://github.com/sanic-org/sanic/pull/1440) fix minor + type and pip install instruction mismatch +- [#1424](https://github.com/sanic-org/sanic/pull/1424) + Documentation Enhancements + +Note: 19.3.0 was skipped for packagement purposes and not released on +PyPI + +## Version 18.12 + +### 18.12.0 + +- Changes: + + - Improved codebase test coverage from 81% to 91%. + - Added stream_large_files and host examples in static_file + document + - Added methods to append and finish body content on Request + (#1379) + - Integrated with .appveyor.yml for windows ci support + - Added documentation for AF_INET6 and AF_UNIX socket usage + - Adopt black/isort for codestyle + - Cancel task when connection_lost + - Simplify request ip and port retrieval logic + - Handle config error in load config file. + - Integrate with codecov for CI + - Add missed documentation for config section. + - Deprecate Handler.log + - Pinned httptools requirement to version 0.0.10+ + +- Fixes: + + - Fix `remove_entity_headers` helper function (#1415) + - Fix TypeError when use Blueprint.group() to group blueprint + with default url_prefix, Use os.path.normpath to avoid invalid + url_prefix like api//v1 f8a6af1 Rename the `http` module to + `helpers` to prevent conflicts with the built-in Python http + library (fixes #1323) + - Fix unittests on windows + - Fix Namespacing of sanic logger + - Fix missing quotes in decorator example + - Fix redirect with quoted param + - Fix doc for latest blueprint code + - Fix build of latex documentation relating to markdown lists + - Fix loop exception handling in app.py + - Fix content length mismatch in windows and other platform + - Fix Range header handling for static files (#1402) + - Fix the logger and make it work (#1397) + - Fix type pikcle-\>pickle in multiprocessing test + - Fix pickling blueprints Change the string passed in the + \"name\" section of the namedtuples in Blueprint to match the + name of the Blueprint module attribute name. This allows + blueprints to be pickled and unpickled, without errors, which + is a requirement of running Sanic in multiprocessing mode in + Windows. Added a test for pickling and unpickling blueprints + Added a test for pickling and unpickling sanic itself Added a + test for enabling multiprocessing on an app with a blueprint + (only useful to catch this bug if the tests are run on + Windows). + - Fix document for logging + +## Version 0.8 + +**0.8.3** + +- Changes: + - Ownership changed to org \'sanic-org\' + +**0.8.0** + +- Changes: + - Add Server-Sent Events extension (Innokenty Lebedev) + - Graceful handling of request_handler_task cancellation (Ashley + Sommer) + - Sanitize URL before redirection (aveao) + - Add url_bytes to request (johndoe46) + - py37 support for travisci (yunstanford) + - Auto reloader support for OSX (garyo) + - Add UUID route support (Volodymyr Maksymiv) + - Add pausable response streams (Ashley Sommer) + - Add weakref to request slots (vopankov) + - remove ubuntu 12.04 from test fixture due to deprecation + (yunstanford) + - Allow streaming handlers in add_route (kinware) + - use travis_retry for tox (Raphael Deem) + - update aiohttp version for test client (yunstanford) + - add redirect import for clarity (yingshaoxo) + - Update HTTP Entity headers (Arnulfo Solís) + - Add register_listener method (Stephan Fitzpatrick) + - Remove uvloop/ujson dependencies for Windows (abuckenheimer) + - Content-length header on 204/304 responses (Arnulfo Solís) + - Extend WebSocketProtocol arguments and add docs (Bob Olde + Hampsink, yunstanford) + - Update development status from pre-alpha to beta (Maksim + Anisenkov) + - KeepAlive Timeout log level changed to debug (Arnulfo Solís) + - Pin pytest to 3.3.2 because of pytest-dev/pytest#3170 (Maksim + Aniskenov) + - Install Python 3.5 and 3.6 on docker container for tests (Shahin + Azad) + - Add support for blueprint groups and nesting (Elias Tarhini) + - Remove uvloop for windows setup (Aleksandr Kurlov) + - Auto Reload (Yaser Amari) + - Documentation updates/fixups (multiple contributors) +- Fixes: + - Fix: auto_reload in Linux (Ashley Sommer) + - Fix: broken tests for aiohttp \>= 3.3.0 (Ashley Sommer) + - Fix: disable auto_reload by default on windows (abuckenheimer) + - Fix (1143): Turn off access log with gunicorn (hqy) + - Fix (1268): Support status code for file response (Cosmo Borsky) + - Fix (1266): Add content_type flag to Sanic.static (Cosmo Borsky) + - Fix: subprotocols parameter missing from add_websocket_route + (ciscorn) + - Fix (1242): Responses for CI header (yunstanford) + - Fix (1237): add version constraint for websockets (yunstanford) + - Fix (1231): memory leak - always release resource (Phillip Xu) + - Fix (1221): make request truthy if transport exists (Raphael + Deem) + - Fix failing tests for aiohttp\>=3.1.0 (Ashley Sommer) + - Fix try_everything examples (PyManiacGR, kot83) + - Fix (1158): default to auto_reload in debug mode (Raphael Deem) + - Fix (1136): ErrorHandler.response handler call too restrictive + (Julien Castiaux) + - Fix: raw requires bytes-like object (cloudship) + - Fix (1120): passing a list in to a route decorator\'s host arg + (Timothy Ebiuwhe) + - Fix: Bug in multipart/form-data parser (DirkGuijt) + - Fix: Exception for missing parameter when value is null + (NyanKiyoshi) + - Fix: Parameter check (Howie Hu) + - Fix (1089): Routing issue with named parameters and different + methods (yunstanford) + - Fix (1085): Signal handling in multi-worker mode (yunstanford) + - Fix: single quote in readme.rst (Cosven) + - Fix: method typos (Dmitry Dygalo) + - Fix: log_response correct output for ip and port (Wibowo + Arindrarto) + - Fix (1042): Exception Handling (Raphael Deem) + - Fix: Chinese URIs (Howie Hu) + - Fix (1079): timeout bug when self.transport is None (Raphael + Deem) + - Fix (1074): fix strict_slashes when route has slash (Raphael + Deem) + - Fix (1050): add samesite cookie to cookie keys (Raphael Deem) + - Fix (1065): allow add_task after server starts (Raphael Deem) + - Fix (1061): double quotes in unauthorized exception (Raphael + Deem) + - Fix (1062): inject the app in add_task method (Raphael Deem) + - Fix: update environment.yml for readthedocs (Eli Uriegas) + - Fix: Cancel request task when response timeout is triggered + (Jeong YunWon) + - Fix (1052): Method not allowed response for RFC7231 compliance + (Raphael Deem) + - Fix: IPv6 Address and Socket Data Format (Dan Palmer) + +Note: Changelog was unmaintained between 0.1 and 0.7 + +## Version 0.1 + +**0.1.7** + +- Reversed static url and directory arguments to meet spec + +**0.1.6** + +- Static files +- Lazy Cookie Loading + +**0.1.5** + +- Cookies +- Blueprint listeners and ordering +- Faster Router +- Fix: Incomplete file reads on medium+ sized post requests +- Breaking: after_start and before_stop now pass sanic as their + first argument + +**0.1.4** + +- Multiprocessing + +**0.1.3** + +- Blueprint support +- Faster Response processing + +**0.1.1 - 0.1.2** + +- Struggling to update pypi via CI + +**0.1.0** + +- Released to public diff --git a/guide/content/zh/built-with-sanic.md b/guide/content/zh/built-with-sanic.md new file mode 100644 index 0000000000..ed2b9b4a10 --- /dev/null +++ b/guide/content/zh/built-with-sanic.md @@ -0,0 +1,67 @@ +--- +title: 全速向前——我们如何通过 Sanic 构建这个站点 +layout: 主界面 +--- + +.. attrs:: +:class: title + +``` +全速向前: +``` + +.. attrs:: +:class: subtitle + +``` +我们如何通过 Sanic 构建这个站点 +``` + +欢迎来到这个互联网中我们可以自豪地说,「是的,我们用 Sanic 建造了这个!」的小角落。 这里不仅仅是一个网站,也是我们的游戏场、测试实验室、战场以及家里。 + +![](/assets/images/build-sanic.png) + +### 故事:「我们饮下自酿的香槟」 + +我们非常相信 Sanic,因此我们决定对它进行最终测试——运行我们自己的网站。 就像在自家的餐馆里的厨师,只有在食物中毒的风险很低很低的情况下才敢畅怀开吃。 + +那么为什么? 因为建立网站或网络应用程序是困难的。 这里有数不清的活动部件、大量的挑战,以及对速度和可靠性无时不在的需求。 我们只想向您展示你_可以_ 实现的很多方法之一。 + +在这个高风险的数字厨房中, Sanic 是我们的秘方。 通过 Sanic 部署我们自己的网站,我们不只是展示自己的能力,我们正让现实世界对 Sanic 进行压力测试。 这是我们大显身手的机会,证明 Sanic 不只是纸上谈兵,它还是一个强大稳健的高性能框架,可以处理所有场景,从最简单的博客到最繁忙的电子商务网站。 + +因此,我们在这里啜饮着自己酿造的香槟,自信满满,如果 Sanic 能够运行我们的网站,它也能为您的网站提供动力。 向可以按照灵感迸发的速度编码干杯! 🥂 + +### 安装:数字的汪洋大海,啊哈! + +我们启动了我们在 DigitalOcean App Platform 上的网站,因为我们喜爱高性能的云端航行。 这就好比在云上拥有一辆法拉利跑车——快速、美观,而且易于操作。 + +为何求简? 有简单的团队而没有开发运维专家,我们需要一种不大费周章、直截了当的解决办法。 DigitalOcean 为我们提供了顺利航行的平台既服务(PaaS)的经验。 它完美地满足我们的需要:轻松设置、自动部署以及让你可以安稳睡觉的可靠性。 + +我们的选择反映了我们的理念:专注于自己的优势,而让平台负责繁重的工作。 对我们来讲,这意味着通过 Sanic 创造惊人的网络经验,并由简单而强大的部署解决方案提供支持。 ⛵ + +### 代码:GitHub 就是它的位置 + +我们的所有代码都是开放的,并在 GitHub 上接受公众的监督。 为何遮掩魔法? 它在[我们的 GitHub 仓库](https://github.com/sanic-org/sanic/tree/main/guide)中。 来吧,看一看, fork 一 fork ,玩一玩,弄坏它(最好再好心地修好它)。 + +对我们来说,「开源」不仅仅是浮于表现的流行语,还是我们的理念与灵魂。 是为了我们,可以共同创建比我们还要大的东西。 我们的代码是协作创新的见证,是开发的乐园,也是 Sanic 在行动中的真实案例。 + +每一行代码每一次提交,都反映出我们与 Sanic 的旅程,展示我们如何利用其速度和可扩展性。 您的贡献,不管是修复 bug 、建议一个功能,或是改进文档,都是推动这个项目前进的因素。 + +所以,来吧,贡献你的想法你的解决方案,让我们继续通过 Sanic 创建网络发展的未来。 我们不仅仅是编写代码——我们正在创建一个由社区驱动的发电站。 🚀 + +### 邀请:写,码,破,立! + +- **记录者**: 喜欢把复杂的东西说得简单明了? 我们的文档是你的画布。 键盘当画笔的那种! 🎨 + +- **代码忍者**:找到了 bug ? 打得他们落花流水! 有想法? 码出来! 让拉取请求降雨! 🥷 + +- **Bug 猎人**:如果你发现了 bug ,不要光盯着。 告诉我们吧。 我们很喜欢好的 bug 猎人。 🐛 + +### 写在最后 + +我们用 Sanic 建造这个网站来展示给大家它可以做些什么。 它很快,很有趣,也是我们正在使用的。 嗯,如果真的加载得很快的话,那就呱唧呱唧。 如果不是这样,嗯... 我们就怪... 宇宙射线? + +加入我们,让 Sanic 不仅好,还好到让人叫妈妈! + +干杯🍻, +(偶尔也穿斗篷的) Sanic 团队敬上 diff --git a/guide/content/zh/guide/advanced/class-based-views.md b/guide/content/zh/guide/advanced/class-based-views.md new file mode 100644 index 0000000000..7c969975d0 --- /dev/null +++ b/guide/content/zh/guide/advanced/class-based-views.md @@ -0,0 +1,227 @@ +# 基于类的视图(Class Based Views) + +## 为什么要使用它? + +.. column:: + +``` +### 问题所在 + +设计API时常见的一个模式是在同一路由入口上根据HTTP方法实现多种功能。 + +虽然这两种方案都能正常工作,但它们并不是良好的设计实践,并且随着项目的增长,可能会随着时间推移变得难以维护。 +``` + +.. column:: + +```` +```python +@app.get("/foo") +async def foo_get(request): + ... + +@app.post("/foo") +async def foo_post(request): + ... + +@app.put("/foo") +async def foo_put(request): + ... + +@app.route("/bar", methods=["GET", "POST", "PATCH"]) +async def bar(request): + if request.method == "GET": + ... + + elif request.method == "POST": + ... + + elif request.method == "PATCH": + ... +``` +```` + +.. column:: + +``` +### 解决方案 + +基于类的视图本质上是实现响应请求行为的类。它们提供了一种方式,在同一路由入口上将不同类型的HTTP请求处理方式进行模块化管理。 +``` + +.. column:: + +```` +```python +from sanic.views import HTTPMethodView + +class FooBar(HTTPMethodView): + async def get(self, request): + ... + + async def post(self, request): + ... + + async def put(self, request): + ... + +app.add_route(FooBar.as_view(), "/foobar") +``` +```` + +## 定义视图 (Defining a view) + +一个基于类的视图应继承自 :class:`sanic.views.HTTPMethodView`. 然后,您可以实现与相应HTTP方法同名的类方法。 如果接收到的方法未定义的请求,将生成`405: Method not allowed`的响应。 + +.. column:: + +``` +在路由入口上注册基于类的视图时,通常使用 `app.add_route` 方法。第一个参数应当是已定义的类,并调用其 `as_view` 方法,第二个参数则是该视图要绑定的URL路由入口。 + +可用的方法包括: + +- get +- post +- put +- patch +- delete +- head +- options +``` + +.. column:: + +```` +```python +from sanic.views import HTTPMethodView +from sanic.response import text + +class SimpleView(HTTPMethodView): + + def get(self, request): + return text("I am get method") + + # You can also use async syntax + async 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") + +app.add_route(SimpleView.as_view(), "/") +``` +```` + +## 路径参数(Path parameters) + +.. column:: + +``` +您可以完全按照[路由部分](/zh/guide/basics/routing.md)中讨论的方式来使用路径参数。 +``` + +.. column:: + +```` +```python +class NameView(HTTPMethodView): + + def get(self, request, name): + return text("Hello {}".format(name)) + +app.add_route(NameView.as_view(), "/") +``` +```` + +## 装饰器(Decorators) + +正如在[装饰器](/zh/guide/best-practices/decorators.md)部分讨论的那样,您经常需要通过使用装饰器为路由入口添加功能。 对于基于类的视图(CBV),有两种选择: + +1. 应用于视图中的**所有**HTTP方法 +2. 分别应用于视图中的各个HTTP方法 + +让我们来看一下这两种选项的样子: + +.. column:: + +``` +### 应用于视图中的所有HTTP方法 + +若要向此类添加任何装饰器,您可以设置 `decorators` 类变量。当调用 `as_view` 时,这些装饰器将会应用于此类。 +``` + +.. column:: + +```` +```python +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") +``` +```` + +.. column:: + +``` +### 分别应用于视图中的各个HTTP方法 + +但是,如果您只想装饰某些方法而不是所有方法,则可以如以下所示操作。 +``` + +.. column:: + +```` +```python +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 do not have any decorators") + + @some_decorator_here + def patch(self, request, name): + return text("Hello I have a decorator") +``` +```` + +## 动态路由(Generating a URL) + +.. column:: + +``` +这就像[生成任何其他URL](/zh/guide/basics/routing.md#generating-a-url)一样工作,只不过类名是路由入口的一部分。 +``` + +.. column:: + +```` +```python +@app.route("/") +def index(request): + url = app.url_for("SpecialClassView") + return redirect(url) + +class SpecialClassView(HTTPMethodView): + def get(self, request): + return text("Hello from the Special Class View!") + +app.add_route(SpecialClassView.as_view(), "/special_class_view") +``` +```` diff --git a/guide/content/zh/guide/advanced/commands.md b/guide/content/zh/guide/advanced/commands.md new file mode 100644 index 0000000000..531fa428b6 --- /dev/null +++ b/guide/content/zh/guide/advanced/commands.md @@ -0,0 +1,73 @@ +# 自定义 CLI 命令 + +.. 新:v24.12 + +``` +This feature was added in version 24.12 +``` + +使用 [CLI]的Sanic 飞船(../running/running.html#running-via-command) 运行Sanic 服务器。 有时,您可能需要提高CLI来运行您自己的自定义命令。 命令使用以下基本模式: + +```sh +sanic path.to:app exec [--arg=value] +``` + +.. 列: + +``` +To enable this, you can use your `Sanic` app instance to wrap functions that can be callable from the CLI using the `@app.command` decorator. +``` + +.. 列: + +```` +```python +@app.command +async def hello(name="world"): + print(f"Hello, {name}.") +``` +```` + +.. 列: + +``` +Now, you can easily invoke this command using the `exec` action. +``` + +.. 列: + +```` +```sh +sanic path.to:app exec hello --name=Adam +``` +```` + +命令处理程序可以是同步或异步的。 处理程序可以接受任何一些关键词参数,这些参数将从CLI传递。 + +.. 列: + +``` +By default, the name of the function will be the command name. You can override this by passing the `name` argument to the decorator. +``` + +.. 列: + +```` +```python +@app.command(name="greet") +async def hello(name="world"): + print(f"Hello, {name}.") +``` + +```sh +sanic path.to:app exec greet --name=Adam +``` +```` + +.. 警告:: + +``` +This feature is still in **BETA** and may change in future versions. There is no type coercion or validation on the arguments passed in from the CLI, and the CLI will ignore any return values from the command handler. Future enhancements and changes are likely. +``` + +_添加于 v24.12_ diff --git a/guide/content/zh/guide/advanced/proxy-headers.md b/guide/content/zh/guide/advanced/proxy-headers.md new file mode 100644 index 0000000000..2203952d58 --- /dev/null +++ b/guide/content/zh/guide/advanced/proxy-headers.md @@ -0,0 +1,576 @@ +# 代理配置 (Proxy configuration) + +当您使用反向代理服务器(例如nginx)时,`request.ip` 的值将包含代理的IP地址,通常是 `127.0.0.1`。 几乎在所有情况下,这都不是您所期望得到的结果。 + +Sanic 可以配置为使用代理头信息来确定真实的客户端IP地址,该地址可通过 `request.remote_addr` 获取。 如果有提供的话,完整的外部URL也会从头字段中构建出来。 + +.. tip:: 注意一下 + +``` +未经适当防护措施,恶意客户端可能会利用代理头信息伪造自己的IP地址。为了避免这类问题,Sanic默认不使用任何代理头信息,除非明确启用此功能。 +``` + +.. column:: + +``` +部署在反向代理之后的服务必须配置以下[配置值](/zh/guide/deployment/configuration.md)之一或多者: + +- `FORWARDED_SECRET` +- `REAL_IP_HEADER` +- `PROXIES_COUNT` +``` + +.. column:: + +```` +```python +app.config.FORWARDED_SECRET = "super-duper-secret" +app.config.REAL_IP_HEADER = "CF-Connecting-IP" +app.config.PROXIES_COUNT = 2 +``` +```` + +## 转发头(Forwarded header) + +为了使用“Forwarded”头信息,您应该将 `app.config.FORWARDED_SECRET` 设置为可信代理服务器所知的一个值。 这个密钥用于安全地识别特定的代理服务器。 + +如果没有设置密钥,Sanic会忽略任何没有密钥的秘密元素,甚至不会解析该头信息。 + +一旦找到可信的转发元素,所有其他代理头信息都将被忽略,因为可信的转发元素已经携带了关于客户端的完整信息。 + +要了解更多关于Forwarded头信息的内容,请阅读相关的 [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded) 和[Nginx](https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/) 文章。 + +## 常见的代理请求头(Traditional proxy headers) + +### IP Headers + +当您的代理通过某个已知头信息传递IP地址时,您可以使用 `REAL_IP_HEADER` 配置值告诉Sanic这个头信息是什么。 + +### X-Forwarded-For + +此头信息通常包含经过每一层代理的IP地址链。 设置 PROXIES_COUNT 告诉Sanic应深入到哪一层以获取实际的客户端IP地址。 这个值应等于链中IP地址预期的数量。 + +### Other X-headers + +如果通过上述方法找到了客户端IP地址,Sanic会使用以下头信息来构建URL各部分: + +- x-forwarded-proto +- x-forwarded-host +- x-forwarded-port +- x-forwarded-path +- x-scheme + +## 示例 + +在以下示例中,所有请求都将以如下形式的路由入口为基础进行演示: + +```python +@app.route("/fwd") +async def forwarded(request): + return json( + { + "remote_addr": request.remote_addr, + "scheme": request.scheme, + "server_name": request.server_name, + "server_port": request.server_port, + "forwarded": request.forwarded, + } + ) +``` + +--- + +##### 例一(Example 1) + +若未配置`FORWARDED_SECRET`,应尊重x-headers + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=1.1.1.1, for=injected;host=", for="[::2]";proto=https;host=me.tld;path="/app/";secret=mySecret,for=broken;;secret=b0rked, for=127.0.0.3;scheme=http;port=1234' \ + -H "X-Real-IP: 127.0.0.2" \ + -H "X-Forwarded-For: 127.0.1.1" \ + -H "X-Scheme: ws" \ + -H "Host: local.site" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "127.0.0.2", + "scheme": "ws", + "server_name": "local.site", + "server_port": 80, + "forwarded": { + "for": "127.0.0.2", + "proto": "ws" + } +} +``` +```` + +--- + +##### 例二(Example 2) + +`FORWARDED_SECRET` 已配置 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=1.1.1.1, for=injected;host=", for="[::2]";proto=https;host=me.tld;path="/app/";secret=mySecret,for=broken;;secret=b0rked, for=127.0.0.3;scheme=http;port=1234' \ + -H "X-Real-IP: 127.0.0.2" \ + -H "X-Forwarded-For: 127.0.1.1" \ + -H "X-Scheme: ws" \ + -H "Host: local.site" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "[::2]", + "scheme": "https", + "server_name": "me.tld", + "server_port": 443, + "forwarded": { + "for": "[::2]", + "proto": "https", + "host": "me.tld", + "path": "/app/", + "secret": "mySecret" + } +} +``` +```` + +--- + +##### 例三(Example 3) + +空`Forwarded`头信息 -> 使用`X-headers` + +```sh +curl localhost:8000/fwd \ + -H "X-Real-IP: 127.0.0.2" \ + -H "X-Forwarded-For: 127.0.1.1" \ + -H "X-Scheme: ws" \ + -H "Host: local.site" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "127.0.0.2", + "scheme": "ws", + "server_name": "local.site", + "server_port": 80, + "forwarded": { + "for": "127.0.0.2", + "proto": "ws" + } +} +``` +```` + +--- + +##### 例四(Example 4) + +存在头信息但无法匹配任何内容 + +```sh +curl localhost:8000/fwd \ + -H "Forwarded: nomatch" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. 列: + +```` +```bash +# curl response +{ + "remote_addr": "", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": {} +} + +``` +```` + +--- + +##### 例五(Example 5) + +`Forwarded`头信息存在,但无匹配的密钥 -> 使用`X-headers` + +```sh +curl localhost:8000/fwd \ + -H "Forwarded: for=1.1.1.1;secret=x, for=127.0.0.1" \ + -H "X-Real-IP: 127.0.0.2" | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "127.0.0.2", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "for": "127.0.0.2" + } +} +``` +```` + +--- + +##### 例六(Example 6) + +不同格式化方式,同时触及头信息两端的情况 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: Secret="mySecret";For=127.0.0.4;Port=1234' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "127.0.0.4", + "scheme": "http", + "server_name": "localhost", + "server_port": 1234, + "forwarded": { + "secret": "mySecret", + "for": "127.0.0.4", + "port": 1234 + } +} +``` +```` + +--- + +##### 例七(Example 7) + +测试转义字符(如果发现有人实现引用对,请修改此处) + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=test;quoted="\,x=x;y=\";secret=mySecret' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "test", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "for": "test", + "quoted": "\\,x=x;y=\\", + "secret": "mySecret" + } +} +``` +```` + +--- + +##### 例八(Example 8) + +密钥信息因格式错误的字段而被隔绝 #1 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=test;secret=mySecret;b0rked;proto=wss;' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "test", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "for": "test", + "secret": "mySecret" + } +} +``` +```` + +--- + +##### 例九(Example 9) + +密钥信息因格式错误的字段而被隔绝 #2 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=test;b0rked;secret=mySecret;proto=wss' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "", + "scheme": "wss", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "secret": "mySecret", + "proto": "wss" + } +} +``` +```` + +--- + +##### 例十(Example 10) + +意外终止不应丢失现有的有效值 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: b0rked;secret=mySecret;proto=wss' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "", + "scheme": "wss", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "secret": "mySecret", + "proto": "wss" + } +} +``` +```` + +--- + +##### 例十一(Example 11) + +字段标准化 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: PROTO=WSS;BY="CAFE::8000";FOR=unknown;PORT=X;HOST="A:2";PATH="/With%20Spaces%22Quoted%22/sanicApp?key=val";SECRET=mySecret' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "mySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "", + "scheme": "wss", + "server_name": "a", + "server_port": 2, + "forwarded": { + "proto": "wss", + "by": "[cafe::8000]", + "host": "a:2", + "path": "/With Spaces\"Quoted\"/sanicApp?key=val", + "secret": "mySecret" + } +} +``` +```` + +--- + +##### 例十二(Example 12) + +使用 "by" 字段作为密钥 + +```sh +curl localhost:8000/fwd \ + -H 'Forwarded: for=1.2.3.4; by=_proxySecret' | jq +``` + +.. column:: + +```` +```python +# Sanic application config +app.config.PROXIES_COUNT = 1 +app.config.REAL_IP_HEADER = "x-real-ip" +app.config.FORWARDED_SECRET = "_proxySecret" +``` +```` + +.. column:: + +```` +```bash +# curl response +{ + "remote_addr": "1.2.3.4", + "scheme": "http", + "server_name": "localhost", + "server_port": 8000, + "forwarded": { + "for": "1.2.3.4", + "by": "_proxySecret" + } +} + +``` +```` diff --git a/guide/content/zh/guide/advanced/signals.md b/guide/content/zh/guide/advanced/signals.md new file mode 100644 index 0000000000..2057626226 --- /dev/null +++ b/guide/content/zh/guide/advanced/signals.md @@ -0,0 +1,400 @@ +# 信号(Signals) + +信号提供了一种方式,使得应用程序的一部分能够通知另一部分发生了某件事情。 + +```python +@app.signal("user.registration.created") +async def send_registration_email(**context): + await send_email(context["email"], template="registration") + +@app.post("/register") +async def handle_registration(request): + await do_registration(request) + await request.app.dispatch( + "user.registration.created", + context={"email": request.json.email} + }) +``` + +## 添加信号(Adding a signal) + +.. column:: + +``` +添加信号的 API 与添加路由非常相似。 +``` + +.. column:: + +```` +```python +async def my_signal_handler(): + print("something happened") + +app.add_signal(my_signal_handler, "something.happened.ohmy") +``` +```` + +.. column:: + +``` +但是,也许使用内置装饰器的方法更为便捷一些。 +``` + +.. column:: + +```` +```python +@app.signal("something.happened.ohmy") +async def my_signal_handler(): + print("something happened") +``` +```` + +.. column:: + +``` +如果信号需要满足某些条件,请确保在添加处理器时添加这些条件。 +``` + +.. column:: + +```` +```python +async def my_signal_handler1(): + print("something happened") + +app.add_signal( + my_signal_handler, + "something.happened.ohmy1", + conditions={"some_condition": "value"} +) + +@app.signal("something.happened.ohmy2", conditions={"some_condition": "value"}) +async def my_signal_handler2(): + print("something happened") +``` +```` + +.. column:: + +``` +信号也可以在蓝图上声明 +``` + +.. column:: + +```` +```python +bp = Blueprint("foo") + +@bp.signal("something.happened.ohmy") +async def my_signal_handler(): + print("something happened") +``` +```` + +## 内置信号(Built-in signals) + +除了创建新的信号外,Sanic 自身还分发了一些内置信号。 这些信号的存在是为了为开发者提供更多机会在请求和服务器生命周期中添加功能。 + +\*添加于 v21.9 \* + +.. column:: + +``` +您可以像对待其他任何信号一样,将它们附加到应用或蓝图实例上。 +``` + +.. column:: + +```` +```python +@app.signal("http.lifecycle.complete") +async def my_signal_handler(conn_info): + print("Connection has been closed") +``` +```` + +这些信号是可用的信号,包括处理器所需处理的参数以及(如果有)附带的条件。 + +| 事件名称(Event name) | 参数(Arguments) | 条件(Conditions) | +| -------------------------- | ------------------------------- | --------------------------------------------------------- | +| `http.routing.before` | request | | +| `http.routing.after` | request, route, kwargs, handler | | +| `http.handler.before` | request | | +| `http.handler.after` | request | | +| `http.lifecycle.begin` | conn_info | | +| `http.lifecycle.read_head` | head | | +| `http.lifecycle.request` | request | | +| `http.lifecycle.handle` | request | | +| `http.lifecycle.read_body` | body | | +| `http.lifecycle.exception` | request, exception | | +| `http.lifecycle.response` | request, response | | +| `http.lifecycle.send` | data | | +| `http.lifecycle.complete` | conn_info | | +| `http.middleware.before` | request, response | `{"attach_to": "request"}` or `{"attach_to": "response"}` | +| `http.middleware.after` | request, response | `{"attach_to": "request"}` or `{"attach_to": "response"}` | +| `server.exception.report` | app, exception | | +| `server.init.before` | app, loop | | +| `server.init.after` | app, loop | | +| `server.shutdown.before` | app, loop | | +| `server.shutdown.after` | app, loop | | + +22.9版本增加了 `http.handler.before` 和 `http.handler.after` 。 + +版本23.6增加了 `server.exception.report` 。 + +.. column:: + +``` +为了更方便地使用内置信号,这里有一个包含所有允许内置信号的 `Enum` 对象。在现代 IDE 中,这将有助于您无需记忆作为字符串形式的所有事件名称列表。 + +*从 v21.12 版本开始新增* +``` + +.. column:: + +```` +```python +from sanic.signals import Event + +@app.signal(Event.HTTP_LIFECYCLE_COMPLETE) +async def my_signal_handler(conn_info): + print("Connection has been closed") +``` +```` + +## 事件(Events) + +.. column:: + +``` +信号基于某个 _事件_ 。事件实际上就是一个遵循以下模式的字符串: +``` + +.. column:: + +```` +``` +namespace.reference.action +``` +```` + +.. tip:: 事件必须包含三个部分。 如果您不确定该如何使用,请尝试以下模式: + +``` +- `my_app.something.happened` +- `sanic.notice.hello` +``` + +### 事件参数(Event parameters) + +.. column:: + +``` +事件可以是“动态”的,并使用与[路径参数](../basics/routing.md#path-parameters)相同的语法进行声明。这样就可以基于任意值进行匹配。 +``` + +.. column:: + +```` +```python +@app.signal("foo.bar.") +async def signal_handler(thing): + print(f"[signal_handler] {thing=}") + +@app.get("/") +async def trigger(request): + await app.dispatch("foo.bar.baz") + return response.text("Done.") +``` +```` + +有关允许的类型定义的更多信息,请查阅[路径参数](../basics/routing.md#path-parameters)。 + +.. info:: 只有事件的第三部分(动作)可以是动态的: + +``` +- `foo.bar.` 🆗 +- `foo..baz` ❌ +``` + +### 等待(Waiting) + +.. column:: + +``` +除了执行信号处理器之外,您的应用程序还可以等待某个事件被触发。 +``` + +.. column:: + +```` +```python +await app.event("foo.bar.baz") +``` +```` + +.. column:: + +``` +**重要提示**:等待是一个阻塞函数。因此,您可能希望将其在一个[后台任务](../basics/tasks.md)中运行。 +``` + +.. column:: + +```` +```python +async def wait_for_event(app): + while True: + print("> waiting") + await app.event("foo.bar.baz") + print("> event found\n") + +@app.after_server_start +async def after_server_start(app, loop): + app.add_task(wait_for_event(app)) +``` +```` + +.. column:: + +``` +如果您的事件使用了动态路径定义,您可以使用 `*` 来捕获任何动作。 +``` + +.. column:: + +```` +```python +@app.signal("foo.bar.") + +... + +await app.event("foo.bar.*") +``` +```` + +## 触发/派发/分发(Dispatching) + +_在未来,Sanic 将自动分发一些事件以帮助开发者接入生命周期事件。_ + +.. column:: + +``` +触发一个事件将会执行两件事: + +1. 执行该事件上定义的所有信号处理器, +2. 处理所有正在“等待”该事件完成的任务。 +``` + +.. column:: + +```` +```python +@app.signal("foo.bar.") +async def foo_bar(thing): + print(f"{thing=}") + +await app.dispatch("foo.bar.baz") +``` +``` +thing=baz +``` +```` + +### 上下文(Context) + +.. column:: + +``` +有时您可能会发现有必要向信号处理器传递额外信息。在上面的第一个示例中,我们希望电子邮件注册过程能拥有用户的电子邮件地址。 +``` + +.. column:: + +```` +```python +@app.signal("user.registration.created") +async def send_registration_email(**context): + print(context) + +await app.dispatch( + "user.registration.created", + context={"hello": "world"} +) +``` +``` +{'hello': 'world'} +``` +```` + +.. tip:: 提示一下 + +``` +信号是在后台任务中分发的。 +``` + +### 蓝图(Blueprints) + +触发蓝图信号的概念类似于 [中间件](../basics/middleware.md). 从应用级别所做的任何操作都将传递到蓝图。 然而,在蓝图上触发信号时,只会执行该蓝图上定义的信号。 + +.. column:: + +``` +或许来个例子更容易解释: +``` + +.. column:: + +```` +```python +bp = Blueprint("bp") + +app_counter = 0 +bp_counter = 0 + +@app.signal("foo.bar.baz") +def app_signal(): + nonlocal app_counter + app_counter += 1 + +@bp.signal("foo.bar.baz") +def bp_signal(): + nonlocal bp_counter + bp_counter += 1 +``` +```` + +.. column:: + +``` +运行 `app.dispatch("foo.bar.baz")` 将会执行两个信号。 +``` + +.. column:: + +```` +```python +await app.dispatch("foo.bar.baz") +assert app_counter == 1 +assert bp_counter == 1 +``` +```` + +.. column:: + +``` +运行 `bp.dispatch("foo.bar.baz")` 将只执行蓝图上的信号。 +``` + +.. column:: + +```` +```python +await bp.dispatch("foo.bar.baz") +assert app_counter == 1 +assert bp_counter == 2 +``` +```` diff --git a/guide/content/zh/guide/advanced/streaming.md b/guide/content/zh/guide/advanced/streaming.md new file mode 100644 index 0000000000..b5f38ce7fb --- /dev/null +++ b/guide/content/zh/guide/advanced/streaming.md @@ -0,0 +1,169 @@ +# 流式传输(Streaming) + +## 请求流(Request streaming) + +Sanic 允许您流式处理客户端发送的数据,以便在字节到达时立即开始处理数据。 + +.. column:: + +``` +当在路由入口上启用流式处理时,您可以使用 `await request.stream.read()` 来流式读取请求体。 + +当请求体读取完毕时,该方法将返回 `None`。 +``` + +.. column:: + +```` +```python +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") + return text(result) +``` +```` + +.. column:: + +``` +也可以在装饰器中通过关键字参数启用该功能... +``` + +.. column:: + +```` +```python +@app.post("/stream", stream=True) +async def handler(request): + ... + body = await request.stream.read() + ... +``` +```` + +.. column:: + +``` +... 或者使用`add_route()` 方法 +``` + +.. column:: + +```` +```python +bp.add_route( + bp_handler, + "/bp_stream", + methods=["POST"], + stream=True, +) +``` +```` + +.. tip:: 提示一下 + +``` +只有post、put和patch这三个装饰器具有stream参数。 +``` + +## 响应流(Response streaming) + +.. column:: + +``` +Sanic 支持向客户端流式传输内容。 +``` + +.. column:: + +```` +```python +@app.route("/") +async def test(request): + response = await request.respond(content_type="text/csv") + await response.send("foo,") + await response.send("bar") + + # Optionally, you can explicitly end the stream by calling: + await response.eof() +``` +```` + +在需要将源自外部服务(例如数据库)的内容实时传输给客户端的情况下,这项功能非常有用。 举例来说,您可以利用`asyncpg`提供的异步游标将数据库记录逐条流式传输至客户端。 + +```python +@app.route("/") +async def index(request): + response = await request.respond() + conn = await asyncpg.connect(database='test') + async with conn.transaction(): + async for record in conn.cursor('SELECT generate_series(0, 10)'): + await response.send(record[0]) +``` + +您可以通过调用 `await response.eof()` 显式结束一个流。 这是一个便捷方法,替代了 `await response.send("", True)`。 在处理器确定已无任何内容需要发送回客户端后,应调用**一次**此方法。 管在Sanic服务器中使用它是可选的,但如果在ASGI模式下运行Sanic,则必须显式终止流。 + +_自v21.6版本起,调用`eof`变为可选_ + +## 文件流(File streaming) + +.. column:: + +``` +Sanic 提供了一个名为 `sanic.response.file_stream` 的函数,当您需要发送大文件时,这个函数非常有用。它会返回一个 `StreamingHTTPResponse` 对象,默认采用分块传输编码;因此,Sanic 不会在响应中添加 `Content-Length` HTTP 头部信息。 + +典型应用场景可能是流式传输视频文件。 +``` + +.. column:: + +```` +```python +@app.route("/mp4") +async def handler_file_stream(request): + return await response.file_stream( + "/path/to/sample.mp4", + chunk_size=1024, + mime_type="application/metalink4+xml", + headers={ + "Content-Disposition": 'Attachment; filename="nicer_name.meta4"', + "Content-Type": "application/metalink4+xml", + }, + ) +``` +```` + +.. column:: + +``` +如果您希望使用 `Content-Length` 头部信息,可以通过禁用分块传输编码并手动添加它来实现。只需简单地向响应中添加 `Content-Length` 头部即可。 +``` + +.. column:: + +```` +```python +from aiofiles import os as async_os +from sanic.response import file_stream + +@app.route("/") +async def index(request): + file_path = "/srv/www/whatever.png" + + file_stat = await async_os.stat(file_path) + headers = {"Content-Length": str(file_stat.st_size)} + + return await file_stream( + file_path, + headers=headers, + ) +``` +```` diff --git a/guide/content/zh/guide/advanced/versioning.md b/guide/content/zh/guide/advanced/versioning.md new file mode 100644 index 0000000000..5e21bfc75c --- /dev/null +++ b/guide/content/zh/guide/advanced/versioning.md @@ -0,0 +1,185 @@ +# 版本控制(Versioning) + +在API构建的标准实践中,向路由入口添加版本是一种常见做法。 这样,当您在未来以破坏性方式更改API时,可以轻松区分不兼容的路由入口。 + +添加版本号将会在您的路由入口前加上 `/v{version}` 形式的URL前缀。 + +版本可以是 `int`、`float`、`str`类型 下列值都为有效值: + +- `1`, `2`, `3` +- `1.1`, `2.25`, `3.0` +- `"1"`, `"v1"`, `"v1.1"` + +## 单个路由(Per route) + +.. column:: + +``` +您可以直接向路由传递版本号。 +``` + +.. column:: + +```` +```python +# /v1/text +@app.route("/text", version=1) +def handle_request(request): + return response.text("Hello world! Version 1") + +# /v2/text +@app.route("/text", version=2) +def handle_request(request): + return response.text("Hello world! Version 2") +``` +```` + +## 单个蓝图(Per Blueprint) + +.. column:: + +``` +您还可以向蓝图传递版本号,这样该版本号将应用于该蓝图内的所有路由。 +``` + +.. column:: + +```` +```python +bp = Blueprint("test", url_prefix="/foo", version=1) + +# /v1/foo/html +@bp.route("/html") +def handle_request(request): + return response.html("

Hello world!

") +``` +```` + +## 单个蓝图组(Per Blueprint Group) + +.. column:: + +``` +为了简化版本化蓝图的管理,您可以在蓝图组中提供一个版本号。如果蓝图在创建实例时未指定覆盖相同信息的值,那么同一版本号将自动应用于该蓝图组下的所有蓝图。 + +在使用蓝图组管理版本时,将按照以下顺序将Version前缀应用于正在注册的路由: + +1. 路由级别的配置 +2. 蓝图级别的配置 +3. 蓝图组级别的配置 + +如果我们发现更具体的版本化规范,我们将优先选择它,而不是蓝图或蓝图组中提供的更通用的版本化规范。 +``` + +.. column:: + +```` +```python +from sanic.blueprints import Blueprint +from sanic.response import json + +bp1 = Blueprint( + name="blueprint-1", + url_prefix="/bp1", + version=1.25, +) +bp2 = Blueprint( + name="blueprint-2", + url_prefix="/bp2", +) + +group = Blueprint.group( + [bp1, bp2], + url_prefix="/bp-group", + version="v2", +) + +# GET /v1.25/bp-group/bp1/endpoint-1 +@bp1.get("/endpoint-1") +async def handle_endpoint_1_bp1(request): + return json({"Source": "blueprint-1/endpoint-1"}) + +# GET /v2/bp-group/bp2/endpoint-2 +@bp2.get("/endpoint-1") +async def handle_endpoint_1_bp2(request): + return json({"Source": "blueprint-2/endpoint-1"}) + +# GET /v1/bp-group/bp2/endpoint-2 +@bp2.get("/endpoint-2", version=1) +async def handle_endpoint_2_bp2(request): + return json({"Source": "blueprint-2/endpoint-2"}) +``` +```` + +## 版本前缀(Version prefix) + +如上所述,应用于路由的版本始终是生成URI路径中的第一个部分。 因此,为了能够在版本之前添加路径部分,您在传入版本参数的所有位置都可以同时传入`version_prefix`参数,从而实现这一目的。 + +`version_prefix` 参数可以定义于: + +- `app.route` 和 `bp.route` 装饰器 (也包括所有便捷的装饰器) +- `Blueprint` 实例 +- `Blueprint.group` 构造函数 +- `BlueprintGroup` 实例 +- `使用 `app.blueprint\` 注册蓝图时 + +如果有多个位置定义,较具体的定义会覆盖较一般的定义。 本列表提供了这一层级关系。 + +版本控制前缀`version_prefix`的默认值是 `/v`。 + +.. column:: + +``` +一个常被要求的功能是在 `/api` 上挂载版本化的路由。这可以通过 `version_prefix` 轻松实现。 +``` + +.. column:: + +```` +```python +# /v1/my/path +app.route("/my/path", version=1, version_prefix="/api/v") +``` +```` + +.. column:: + +``` +或许一个更具说服力的用法是将所有 `/api` 路由加载到单个 `BlueprintGroup` 中。 +``` + +.. column:: + +```` +```python +# /v1/my/path +app = Sanic(__name__) +v2ip = Blueprint("v2ip", url_prefix="/ip", version=2) +api = Blueprint.group(v2ip, version_prefix="/api/version") + +# /api/version2/ip +@v2ip.get("/") +async def handler(request): + return text(request.ip) + +app.blueprint(api) +``` +```` + +因此我们可以得知,一个路由的 URI 是由下面基本构成的: + +``` +version_prefix + version + url_prefix + URI definition +``` + +.. tip:: 提示 + +```` +就像 `url_prefix` 一样,在 `version_prefix` 内部定义路径参数也是可能的。这样做完全合理。但请记住,每个路由都会将该参数注入到处理器中。 + +```python +version_prefix="//v" +``` +```` + +\*添加于 v21.6 \* diff --git a/guide/content/zh/guide/advanced/websockets.md b/guide/content/zh/guide/advanced/websockets.md new file mode 100644 index 0000000000..62bcbed8d2 --- /dev/null +++ b/guide/content/zh/guide/advanced/websockets.md @@ -0,0 +1,91 @@ +# Websockets + +Sanic 提供了一个基于 [websockets](https://websockets.readthedocs.io/en/stable/) 的易于使用的抽象层 + +## 路由(Routing) + +.. column:: + +``` +Websocket 处理程序可以像常规处理程序那样连接到路由器上。 +``` + +.. column:: + +```` +```python +from sanic import Request, Websocket + +async def feed(request: Request, ws: Websocket): + pass + +app.add_websocket_route(feed, "/feed") +``` +```python +from sanic import Request, Websocket + +@app.websocket("/feed") +async def feed(request: Request, ws: Websocket): + pass +``` +```` + +## 定义响应函数(Handler) + +.. column:: + +``` +通常情况下,websocket 处理程序会维持一个循环保持打开状态。 + +然后,可以在注入到处理程序的第二个对象上调用 `send()` 和 `recv()` 方法。 + +下面是一个简单的示例,该端点接收来自客户端的消息并将其回显给客户端。 +``` + +.. column:: + +```` +```python +from sanic import Request, Websocket + +@app.websocket("/feed") +async def feed(request: Request, ws: Websocket): + while True: + data = "hello!" + print("Sending: " + data) + await ws.send(data) + data = await ws.recv() + print("Received: " + data) +``` +```` + +.. column:: + +``` +您可以通过在一个for循环中迭代 `Websocket` 对象来简化您的循环。 + +*该特性在v22.9版本中添加* +``` + +.. column:: + +```` +```python +from sanic import Request, Websocket + +@app.websocket("/feed") +async def feed(request: Request, ws: Websocket): + async for msg in ws: + await ws.send(msg) +``` +```` + +## 配置(Configuration) + +更多详情请参阅[配置部分](/zh/guide/deployment/configuration.md),不过下面列出了默认值。 + +```python +app.config.WEBSOCKET_MAX_SIZE = 2 ** 20 +app.config.WEBSOCKET_PING_INTERVAL = 20 +app.config.WEBSOCKET_PING_TIMEOUT = 20 +``` diff --git a/guide/content/zh/guide/basics/README.md b/guide/content/zh/guide/basics/README.md new file mode 100644 index 0000000000..bf31ab2555 --- /dev/null +++ b/guide/content/zh/guide/basics/README.md @@ -0,0 +1 @@ +# 基础知识 diff --git a/guide/content/zh/guide/basics/app.md b/guide/content/zh/guide/basics/app.md new file mode 100644 index 0000000000..3c8ce82279 --- /dev/null +++ b/guide/content/zh/guide/basics/app.md @@ -0,0 +1,634 @@ +--- +title: Sanic 应用程序 +--- + +# Sanic 应用程序 + +请参阅API文档: [sanic.app](/api/sanic.app) + +## 实例(Instance) + +.. column:: + +``` +最基本的构建块是 :class: `sanic.app.Sanic` 实例。这不是必需的,但习惯是在名为 `server.py` 的文件中实例化它。 +``` + +.. column:: + +```` +```python +# /path/to/server.py + +from sanic import Sanic + +app = Sanic("MyHelloWorldApp") +``` +```` + +## 应用上下文(Application context) + +大多数应用程序都需要跨代码库的不同部分共享/重用数据或对象。 Sanic 帮助在应用程序实例上提供 `ctx` 对象。 它是开发者可以在应用程序整个生命周期中附加任何应存在的对象或数据的自由空间。 + +.. column:: + +``` +最常见的模式是将数据库实例附加到应用程序中。 +``` + +.. column:: + +```` +```python +app = Sanic("MyApp") +app.ctx.db = Database() +``` +```` + +.. column:: + +``` +虽然前面的示例可以工作并且具有说明性,但通常将对象在应用的开始或结束的生命周期里添加是比较合适的 +``` + +.. column:: + +```` +```python +app = Sanic("MyApp") + +@app.before_server_start +async def attach_db(app, loop): + app.ctx.db = Database() +``` +```` + +## App 注册(App Registry) + +.. column:: + +``` +当您实例化一个 Sanic 类时,它可以稍后从 `Sanic` 提供的方法`get_app()`中获取到这个实例。 例如,如果您需要从无法访问的地方访问您的 Sanic 实例,这可能是有用的。 +``` + +.. column:: + +```` +```python +# ./path/to/server.py +from sanic import Sanic + +app = Sanic("my_awesome_server") + +# ./path/to/somewhere_else.py +from sanic import Sanic + +app = Sanic.get_app("my_awesome_server") +``` +```` + +.. column:: + +``` +如果您调用 Sanic.get_app("non-existing") 尝试去获取一个不存在的实例时,默认情况下它将引发 :class:`sanic.exceptions.SanicException`。但是,您也可以通过指定`force_create=True`强制该方法返回具有该名称的 Sanic 的新实例。 +``` + +.. column:: + +```` +```python +app = Sanic.get_app( + "non-existing", + force_create=True, +) +``` +```` + +.. column:: + +``` +如果有 **只有** 一个Sanic 实例,那么不带参数,直接调用 `Sanic.get_app()` 将返回这个实例 +``` + +.. column:: + +```` +```python +Sanic("My only app") + +app = Sanic.get_app() +``` +```` + +## 配置(Configuration) + +.. 列: + +``` +Sanic 将所有的配置文件存储在`Sanic`实例的`config`属性当中,你可以通过`.`点的方式或者直接把`Sanic.config`当作一个数组去修改配置 +``` + +.. column:: + +```` +```python +app = Sanic('myapp') + +app.config.DB_NAME = 'appdb' +app.config['DB_USER'] = 'appuser' + +db_settings = { + 'DB_HOST': 'localhost', + 'DB_NAME': 'appdb', + 'DB_USER': 'appuser' +} +app.config.update(db_settings) +``` +```` + +.. note:: 注意一下 + +```` +通常来说,Config 的键应该是大写的,但是多数情况下,小写也能正常工作 +```python +app.config.GOOD = "yay!" +app.config.bad = "boo" +``` +```` + +这里有更多关于配置的使用方法[更详细的配置](../running/configuration.md)。 + +## 工厂模式(Factory pattern) + +文档中大多数案例都是在`server.py`的顶层(不是在一个函数中)实例化Sanic这个对象 :class:`sanic.app.Sanic` 这是非常简单的“hello world”风格应用程序的常见形式,但使用工厂模式往往具有更高的扩展性。 + +"工厂" 只是一个函数返回你想要使用的对象的实例。 这允许您抽象对象的实例化, 但也可能使它更容易隔离应用程序实例。 + +.. column:: + +``` +一个简单的出厂模式看起来像这样: +``` + +.. column:: + +```` +```python +# ./path/to/server.py +from sanic import Sanic +from .path.to.config import MyConfig +from .path.to.some.blueprint import bp + + +def create_app(config=MyConfig) -> Sanic: + app = Sanic("MyApp", config=config) + app.blueprint(bp) + return app +``` +```` + +.. column:: + +``` +当我们稍后开始运行 Sanic时,你会知道Sanic CLI 可以检测到这种模式并使用它来运行你的应用程序。 +``` + +.. column:: + +```` +```sh +sanic path.to.server:create_app +``` +```` + +## 自定义(Customization) + +在实例化阶段,可以根据您的应用程序需求以多种方式自定义 Sanic 应用程序实例。 + +详情请查看[API 文档](/api/sanic.app)。 + +### 自定义配置(Custom configuration) + +.. column:: + +``` +这种最简单的自定义配置形式是将您自己的对象直接传递到该 Sanic 应用程序实例中 + +如果您创建自定义配置对象,*强烈*建议您对 :class:`sanic.config.Config` 选项进行子类化以继承其行为。 您可以使用此选项添加属性或您自己的一组自定义逻辑。 + +*在 v21.6 中添加* +``` + +.. column:: + +```` +```python +from sanic.config import Config + +class MyConfig(Config): + FOO = "bar" + +app = Sanic(..., config=MyConfig()) +``` +```` + +.. column:: + +``` +此功能的一个有用例子是如,果您想使用一个格式不同于 [supported]的配置文件 (. /running/configuration.md#using-sanicupdateconfig)。 +``` + +.. column:: + +```` +```python +from sanic import Sanic, text +from sanic.config import Config + +class TomlConfig(Config): + def __init__(self, *args, path: str, **kwargs): + super().__init__(*args, **kwargs) + + with open(path, "r") as f: + self.apply(toml.load(f)) + + def apply(self, config): + self.update(self._to_uppercase(config)) + + def _to_uppercase(self, obj: Dict[str, Any]) -> Dict[str, Any]: + retval: Dict[str, Any] = {} + for key, value in obj.items(): + upper_key = key.upper() + if isinstance(value, list): + retval[upper_key] = [ + self._to_uppercase(item) for item in value + ] + elif isinstance(value, dict): + retval[upper_key] = self._to_uppercase(value) + else: + retval[upper_key] = value + return retval + +toml_config = TomlConfig(path="/path/to/config.toml") +app = Sanic(toml_config.APP_NAME, config=toml_config) +``` +```` + +### 自定义上下文 + +.. column:: + +``` +默认情况下,应用程序上下文是一个 [`SimpleNamespace()`](https://docs.python.org/3/library/types.html#types.SimpleNamespace),它允许您设置您想要的任何属性。 但是,您也可以选择传递任何对象。 + +*在 v21.6 中添加* +``` + +.. column:: + +```` +```python +app = Sanic(..., ctx=1) +``` + +```python +app = Sanic(..., ctx={}) +``` + +```python +class MyContext: + ... + +app = Sanic(..., ctx=MyContext()) +``` +```` + +### 自定义请求 + +.. column:: + +``` +有时拥有自己的“Request”类会很有帮助,并告诉 Sanic 使用它而不是默认的。 一个例子是,如果您想修改默认的“request.id”生成器。 + + + +.. note:: 重点 + +重要的是要记住,您传递的是 *class* 而不是该类的实例。 +``` + +.. column:: + +```` +```python +import time + +from sanic import Request, Sanic, text + +class NanoSecondRequest(Request): + @classmethod + def generate_id(*_): + return time.time_ns() + +app = Sanic(..., request_class=NanoSecondRequest) + +@app.get("/") +async def handler(request): + return text(str(request.id)) +``` +```` + +### 自定义错误响应函数(Custom error handler) + +.. column:: + +``` +查看[异常处理](../best practices/exceptions.md#custom-error-handling) 获取更多信息 +``` + +.. column:: + +```` +```python +from sanic.handlers import ErrorHandler + +class CustomErrorHandler(ErrorHandler): + def default(self, request, exception): + ''' handles errors that have no error handlers assigned ''' + # You custom error handling logic... + return super().default(request, exception) + +app = Sanic(..., error_handler=CustomErrorHandler()) +``` +```` + +### 自定义dumps函数 + +.. column:: + +``` +有时可能需要或需要提供一个自定义函数来序列一个 JSON 数据对象。 +``` + +.. column:: + +```` +```python +import ujson + +dumps = partial(ujson.dumps, escape_forward_slashes=False) +app = Sanic(__name__, dumps=dumps) +``` +```` + +.. column:: + +``` +或者,或许使用另一个库或创建您自己的库。 +``` + +.. column:: + +```` +```python +from orjson import dumps + +app = Sanic("MyApp", dumps=dumps) +``` +```` + +### 自定义loads函数 + +.. column:: + +``` +类似于“dumps”,您也可以为反序列化数据提供自定义函数。 + +*添加于v22.9* +``` + +.. column:: + +```` +```python +from orjson import loads + +app = Sanic("MyApp", loads=loads) +``` +```` + +### 自定义类型的应用程序 + +从 v23.6开始,默认Sanic 应用程序实例的正确类型注释是: + +```python +sanic.app.Sanic[sanic.config.Config, types.SimpleNamespace] +``` + +它指的是两种一般类型: + +1. 第一个是配置对象的类型。 它默认为 :class: `sanic.config.Config`,但可以是它的任何子类。 +2. 第二种是应用程序上下文的类型。 它默认了 [`SimpleNamespace()`](https://docs.python.org/3/library/types.html#types.SimpleNamespace),但上面显示的 **任何对象** 。 + +让我们看看如何改变类型的一些例子。 + +.. column:: + +``` +考虑这个示例,其中我们传递了 :class:`sanic.config.Config` 的自定义子类和自定义上下文对象。 +``` + +.. column:: + +```` +```python +from sanic import Sanic +from sanic.config import Config + +class CustomConfig(Config): + pass + +app = Sanic("test", config=CustomConfig()) +reveal_type(app) # N: Revealed type is "sanic.app.Sanic[main.CustomConfig, types.SimpleNamespace]" +``` +``` +sanic.app.Sanic[main.CustomConfig, types.SimpleNamespace] +``` +```` + +.. column:: + +``` +同样,当传递自定义上下文对象时,类型将会改变以反映这一点。 +``` + +.. column:: + +```` +```python +from sanic import Sanic + +class Foo: + pass + +app = Sanic("test", ctx=Foo()) +reveal_type(app) # N: Revealed type is "sanic.app.Sanic[sanic.config.Config, main.Foo]" +``` +``` +sanic.app.Sanic[sanic.config.Config, main.Foo] +``` +```` + +.. column:: + +``` +当然,您可以将配置和上下文设置为自定义类型。 +``` + +.. column:: + +```` +```python +from sanic import Sanic +from sanic.config import Config + +class CustomConfig(Config): + pass + +class Foo: + pass + +app = Sanic("test", config=CustomConfig(), ctx=Foo()) +reveal_type(app) # N: Revealed type is "sanic.app.Sanic[main.CustomConfig, main.Foo]" +``` +``` +sanic.app.Sanic[main.CustomConfig, main.Foo] +``` +```` + +如果为应用程序实例创建了自定义类型别名,则这种模式特别有用,因为您可以使用它来注解监听器和处理器。 + +```python +# ./path/to/types.py +from sanic.app import Sanic +from sanic.config import Config +from myapp.context import MyContext +from typing import TypeAlias + +MyApp = TypeAlias("MyApp", Sanic[Config, MyContext]) +``` + +```python +# ./path/to/listeners.py +from myapp.types import MyApp + +def add_listeners(app: MyApp): + @app.before_server_start + async def before_server_start(app: MyApp): + # do something with your fully typed app instance + await app.ctx.db.connect() +``` + +```python +# ./path/to/server.py +from myapp.types import MyApp +from myapp.context import MyContext +from myapp.config import MyConfig +from myapp.listeners import add_listeners + +app = Sanic("myapp", config=MyConfig(), ctx=MyContext()) +add_listeners(app) +``` + +_添加于 v23.6_ + +### 自定义request请求 + +Sanic还允许您自定义请求对象的类型。 如果您想向请求对象添加自定义属性,或者能够访问具有类型的应用程序实例的自定义属性,这将非常有用。 + +Sanic 请求实例的正确、默认类型是: + +```python +sanic.request.Request[ + sanic.app.Sanic[sanic.config.Config, types.SimpleNamespace], + types.SimpleNamespace +] +``` + +它指的是两种一般类型: + +1. 第一个是应用程序实例的类型。 默认为`sanic.app.Sanic[sanic.config.Config、types.SimpleNamespace]`,但可以是这个分类中的任何一个子类。 +2. 第二种是请求上下文的类型。 默认为`types.SimpleNamespace`,但可以是如上所示 [自定义请求](#custom-requests) 中的**任何对象**。 + +让我们看看如何改变类型的一些例子。 + +.. column:: + +``` +在上述包含自定义应用程序实例类型别名的完整示例基础上,我们还可以创建自定义请求类型,以便访问相同的类型注解。 + +当然,要实现这一功能并不需要类型别名。此处仅展示它们是为了减少显示的代码量。 +``` + +.. column:: + +```` +```python +from sanic import Request +from myapp.types import MyApp +from types import SimpleNamespace + +def add_routes(app: MyApp): + @app.get("/") + async def handler(request: Request[MyApp, SimpleNamespace]): + # do something with your fully typed app instance + results = await request.app.ctx.db.query("SELECT * FROM foo") +``` +```` + +.. column:: + +``` +也许您有一个生成自定义上下文对象的自定义请求对象。您可以像这里所示那样通过类型注解正确地使用 IDE 访问这些属性。 +``` + +.. 列: + +```` +```python +from sanic import Request, Sanic +from sanic.config import Config + +class CustomConfig(Config): + pass + +class Foo: + pass + +class RequestContext: + foo: Foo + +class CustomRequest(Request[Sanic[CustomConfig, Foo], RequestContext]): + @staticmethod + def make_context() -> RequestContext: + ctx = RequestContext() + ctx.foo = Foo() + return ctx + +app = Sanic( + "test", config=CustomConfig(), ctx=Foo(), request_class=CustomRequest +) + +@app.get("/") +async def handler(request: CustomRequest): + # Full access to typed: + # - custom application configuration object + # - custom application context object + # - custom request context object + pass +``` +```` + +在[自定义请求上下文](./request#custom-request-context)部分查看更多信息。 + +_添加于 v23.6_ diff --git a/guide/content/zh/guide/basics/cookies.md b/guide/content/zh/guide/basics/cookies.md new file mode 100644 index 0000000000..75fa3e2daa --- /dev/null +++ b/guide/content/zh/guide/basics/cookies.md @@ -0,0 +1,122 @@ +# Cookies + +## 读取(Reading) + +.. column:: + +``` +可以通过 `Request` 对象的 `cookies` 字典来访问 Cookies。 +``` + +.. column:: + +```` +```python +@app.route("/cookie") +async def test(request): + test_cookie = request.cookies.get("test") + return text(f"Test cookie: {test_cookie}") +``` +```` + +.. tip:: 提示一下 + +``` +💡 `request.cookies` 对象是一种具有列表值的字典类型之一。这是因为在 HTTP 中,允许使用单个键重复以发送多个值。 + +大部分情况下,您可能希望使用 `.get()` 方法获取第一个元素而不是一个 `list`。如果您确实需要所有项目组成的 `list`,可以使用 `.getlist()` 方法。 + +*该功能在 v23.3 版本中添加* +``` + +## 写入(Writing) + +.. column:: + +``` +在返回响应时,可以通过 `Response` 对象上的 `response.cookies` 设置 cookie。该对象是 `CookieJar` 类的一个实例,这是一种特殊类型的字典,它会自动为您写入响应头部信息。 +``` + +.. column:: + +```` +```python +@app.route("/cookie") +async def test(request): + response = text("There's a cookie up in this response") + response.add_cookie( + "test", + "It worked!", + domain=".yummy-yummy-cookie.com", + httponly=True + ) + return response +``` +```` + +响应中的 cookie 可以像设置字典值一样设置,并且具有以下可用参数: + +- `path: str` - 指定该 cookie 适用的 URL 子集, 默认为 `/`。 +- `domain: str` - 指定 cookie 有效的域名 。 显式指定的域名必须始终以点号开始。 +- `max_age: int` - 表示 cookie 应该存活的时间(秒)。 +- `expires: datetime` - 指定 cookie 在客户端浏览器上过期的时间。 通常最好使用 `max_age`。 +- `secure: bool` - 表示该 cookie 是否仅通过 HTTPS 发送, 默认为 `True`。 +- `httponly: bool` - 表示是否禁止 JavaScript 读取该 cookie 。 +- `samesite: str` - 可选值包括 Lax、Strict 和 None, 默认为\`Lax'。 +- `comment: str` - 用于提供 cookie 的注释(元数据)。 +- `host_prefix: bool` - 指定是否为 cookie 添加 __Host- 前缀。 +- `secure_prefix: bool` - 指定是否为 cookie 添加 __Secure- 前缀。 +- `partitioned: bool` - 表示是否标记该 cookie 为分区(partitioned)cookie。 + +为了更好地理解这些参数的作用和使用场景,阅读[MDN文档](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) 和 [关于设置cookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie)的相关文档可能会有所帮助。 + +.. tip:: 提示一下 + +``` +默认情况下,Sanic 会将 `secure` 标志设为 `True`,确保 cookie 只通过 HTTPS 安全传输,这是一个明智的默认设置。这对于本地开发来说一般不会有影响,因为在 HTTP 上安全的 cookie 仍然会被发送到 `localhost`。了解更多的信息,你可能需要阅读 [MDN 文档](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies) 和 [安全 cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#Secure). +``` + +## 删除(Deleting) + +.. column:: + +``` +Cookie 可以通过语义化方式或明确方式移除。 +``` + +.. column:: + +```` +```python +@app.route("/cookie") +async def test(request): + response = text("Time to eat some cookies muahaha") + + # This cookie will be set to expire in 0 seconds + response.delete_cookie("eat_me") + + # This cookie will self destruct in 5 seconds + response.add_cookie("fast_bake", "Be quick!", max_age=5) + + return response +``` + +*别忘了在必要时添加 `path` 或 `domain`!* +```` + +## 食用(Eating) + +.. column:: + +``` +Sanic 喜欢吃 cookies,给你也分一块! +``` + +.. column:: + +``` +.. attrs:: + :class: is-size-1 has-text-centered + + 🍪 +``` diff --git a/guide/content/zh/guide/basics/handlers.md b/guide/content/zh/guide/basics/handlers.md new file mode 100644 index 0000000000..19bacc42cb --- /dev/null +++ b/guide/content/zh/guide/basics/handlers.md @@ -0,0 +1,217 @@ +# 响应函数(Handlers) + +下一个重要的构件块是你的 _handlers_。 它们有时也被称为“视图”。 + +在 Sanic 中,响应函数可以是任何一个可调用程序,它至少以一个 :class:`sanic.request.Request` 实例作为参数,并返回一个 :class:`sanic.response.HTTPResponse` 实例或一个执行其他操作的协同程序作为响应。 + +.. column:: + +``` +嗯哼?😕 + +响应函数仅仅是一个 **函数**,可以是同步的,也可以是异步的。 + +响应函数的任务是响应一个请求入口并做一些数据操作。 这是您的大多数业务逻辑将要走的地方。 +``` + +.. column:: + +```` +```python +def i_am_a_handler(request): + return HTTPResponse() + +async def i_am_ALSO_a_handler(request): + return HTTPResponse() +``` +```` + +还有另外两个点需要注意一下: + +1. 你可以不用直接使用 :class:`sanic.response.HTTPresponse` 实例去返回数据 使用一些内置封装好的[便捷方法](./response#methods),将会变的更加简单。 + + - `from sanic import json` + - `from sanic import html` + - `from sanic import redirect` + - _etc_ +2. 我们会在[流媒体部分](../advanced/streaming#response-streaming)中看到的,您并不总是需要返回一个对象。 如果您使用这个底层的 API,您可以在处理程序内部控制响应的流程,并且不需要使用返回对象。 + +.. tip:: 提示 + +``` +如果你想了解更多关于封装逻辑的内容,可以查阅[基于类的视图](../advanced/class-based-views.md)。现在,我们将继续仅基于函数的视图讲解。 +``` + +### 一个简单的基于函数的处理器 + +创建路由处理程序的最常见方式是装饰函数。 它为路由定义创建了一个视觉上简洁的标识。 我们稍后将了解更多关于[路由](./routing.md) + +.. column:: + +``` +让我们来看一个实际的例子。 + +- 我们在应用实例上使用了一个便捷装饰器:`@app.get()` +- 以及一个用于生成响应对象的便捷方法:`text()` + +任务完成💪 +``` + +.. column:: + +```` +```python +from sanic import text + +@app.get("/foo") +async def foo_handler(request): + return text("I said foo!") +``` +```` + +--- + +## 关于 _async_... + +.. column:: + +``` +完全可以写入同步处理程序。 + +在此示例中,我们正在使用 _blocking_ `time.sleep()` 模拟100毫秒的处理时间。 也许这意味着从数据库或第三方网站获取数据。 + +使用四(4)个工序和一个共同的基准工具: + +- **956** 在 30.10s +- 或大约**31.76** 请求/秒 +``` + +.. column:: + +```` +```python +@app.get("/sync") +def sync_handler(request): + time.sleep(0.1) + return text("Done.") +``` +```` + +.. column:: + +``` +只需将 `time.sleep()` 更改为异步替代方案 `asyncio.sleep()`,我们就能看到性能有了显著提升。 🚀 + +同样使用四个(4)工作进程: + +- 在30.08秒内处理了 **115,590** 个请求 +- 或者说,大约每秒处理 **3,843.17** 个请求 + +.. attrs:: + :class: is-size-2 + + 🤯 +``` + +.. column:: + +```` +```python +@app.get("/async") +async def async_handler(request): + await asyncio.sleep(0.1) + return text("Done.") +``` +```` + +好的... 这是一个夸张得离谱的结果。 你们所看到的任何基准都本质上是非常偏颇的。 这个例子旨在极度展示`async/await`在web开发领域的优势。 结果肯定会有所不同。 像Sanic和其他异步Python库并非神奇的解决方案,能自动让程序运行更快。 它们使程序执行更加高效。 + +在我们的示例中,异步版本之所以表现优秀,是因为当一个请求在“睡眠”(等待)时,它可以开始处理另一个请求,然后再处理下一个、下一个、下一个……以此类推,实现并行处理多个请求,从而大大提高服务器的吞吐量。 + +但是,这就是关键所在! Sanic之所以快,是因为它充分利用可用资源并榨取其性能潜力。 它可以同时处理大量请求,这就意味着每秒能够处理更多的请求。 + +.. 提示:一个常见的误区! + +``` +当你你需要对一个网站进行ping操作。你会用什么工具?`pip install 你最爱的请求库 `,我劝你不要这样做🙈 + +而应该尝试使用支持`async/await`功能的客户端。你的服务器会因此说`谢谢你`,避免使用阻塞型工具,尽量选择能良好适应异步生态系统中的工具。如果你需要推荐,可以查看[Awesome Sanic](https://github.com/mekicha/awesome-sanic).。 + +Sanic在其测试包(sanic-testing)中使用了[httpx](https://www.python-httpx.org/) 😜。 +``` + +--- + +## 一个带完整注解的处理器 + +对于那些使用类型注解的人... + +```python +from sanic.response import HTTPResponse, text +from sanic.request import Request + +@app.get("/typed") +async def typed_handler(request: Request) -> HTTPResponse: + return text("Done.") +``` + +## 为您的处理器命名 + +所有处理器都会自动命名。 这对于调试和在模板中生成URL非常有用。 如果不特别指定,将使用的名称就是函数的名 + +.. column:: + +``` +例如,这个处理程序将被命名为“foo_handler”。 +``` + +.. column:: + +```` +```python +# Handler name will be "foo_handler" +@app.get("/foo") +async def foo_handler(request): + return text("I said foo!") +``` +```` + +.. column:: + +``` +同样,您可以通过向装饰器传递`name`参数来指定名称。 +``` + +.. column:: + +```` +```python +# Handler name will be "foo" +@app.get("/foo", name="foo") +async def foo_handler(request): + return text("I said foo!") +``` +```` + +.. column:: + +``` +事实上,正如你将会遇到的情况,有时您**必须**提供名称。例如,如果您在同一函数上使用两个装饰器,那么至少需要为其中一个提供名称。 + +如果不这样做,您将收到错误,并且您的应用将无法启动。在您的应用中,名称**必须**是唯一的。 +``` + +.. column:: + +```` +```python +# Two handlers, same function, +# different names: +# - "foo_arg" +# - "foo" +@app.get("/foo/", name="foo_arg") +@app.get("/foo") +async def foo(request, arg=None): + return text("I said foo!") +``` +```` diff --git a/guide/content/zh/guide/basics/headers.md b/guide/content/zh/guide/basics/headers.md new file mode 100644 index 0000000000..b49b3d869f --- /dev/null +++ b/guide/content/zh/guide/basics/headers.md @@ -0,0 +1,249 @@ +# 请求头(Headers) + +请求和响应头分别在 `Request` 和 `HTTPResponse` 对象中起作用。 它们利用了 [multidict 包](https://multidict.readthedocs.io/en/stable/multidict.html#cimultidict),该包允许单个键拥有多个值。 + +.. tip:: 提示一下 + +``` +解析时,头部键会被转换为**小写**形式。对于头部字段名称不考虑大小写。 +``` + +## 请求(Request) + +Sanic 在将请求头呈现给开发者之前,确实会尝试对它们进行一些规范化处理,并针对常见用例提取一些潜在有意义的信息。 + +.. column:: + +``` +#### 认证信息(Tokens) + +格式为 `Token ` 或 `Bearer ` 的授权令牌会被提取到请求对象中,即:`request.token`。 +``` + +.. column:: + +```` +```python +@app.route("/") +async def handler(request): + return text(request.token) +``` + +```sh +curl localhost:8000 \ + -H "Authorization: Token ABCDEF12345679" +ABCDEF12345679 +``` + +```sh +curl localhost:8000 \ + -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c +``` +```` + +### 代理头(Proxy headers) + +Sanic 对于代理头(proxy headers)信息有特别的处理方式。 更多详情请参阅[代理头](/zh/guide/advanced/proxy-headers.md)章节。 + +### 主机标头和动态URL的构建(Host header and dynamic URL construction) + +.. column:: + +``` +通过 request.host 可获取到有效主机名。有效主机名不一定与主机头相同,因为它优先采用代理转发的主机名,并且可以通过服务器名称设置强制指定。 + +Web 应用通常应使用此访问器,以便无论部署方式如何都能保持相同的功能。如有需要,实际主机头可以通过 request.headers 获取。 + +有效主机名还用于通过 request.url_for 动态构建 URL,该方法使用请求来确定处理器的外部地址。 + +.. tip:: 警惕恶意客户端 + + 这些 URL 可能通过发送误导性主机头信息被操纵。如果对此有所顾虑,建议改用 `app.url_for`。 +``` + +.. column:: + +```` +```python +app.config.SERVER_NAME = "https://example.com" + +@app.route("/hosts", name="foo") +async def handler(request): + return json( + { + "effective host": request.host, + "host header": request.headers.get("host"), + "forwarded host": request.forwarded.get("host"), + "you are here": request.url_for("foo"), + } + ) +``` + +```sh +curl localhost:8000/hosts +{ + "effective host": "example.com", + "host header": "localhost:8000", + "forwarded host": null, + "you are here": "https://example.com/hosts" +} +``` +```` + +### 其他请求头(Other headers) + +.. column:: + +``` +所有请求头都可以通过 `request.headers` 访问,可以以字典形式访问这些头信息。对于头信息的大小写不作考虑,因此可以使用大写或小写键来访问。 +``` + +.. column:: + +```` +```python +@app.route("/") +async def handler(request): + return json( + { + "foo_weakref": request.headers["foo"], + "foo_get": request.headers.get("Foo"), + "foo_getone": request.headers.getone("FOO"), + "foo_getall": request.headers.getall("fOo"), + "all": list(request.headers.items()), + } + ) +``` + +```sh +curl localhost:9999/headers -H "Foo: one" -H "FOO: two"|jq +{ + "foo_weakref": "one", + "foo_get": "one", + "foo_getone": "one", + "foo_getall": [ + "one", + "two" + ], + "all": [ + [ + "host", + "localhost:9999" + ], + [ + "user-agent", + "curl/7.76.1" + ], + [ + "accept", + "*/*" + ], + [ + "foo", + "one" + ], + [ + "foo", + "two" + ] + ] +} +``` +```` + +.. tip:: 提示一下 + +``` +💡 `request.headers` 对象是一种特殊类型的字典,其中每个值都是一个列表。这是因为 HTTP 协议允许同一键被复用以发送多个值。 + +大多数时候,您可能想要通过 .get() 或 .getone() 方法获取第一个元素而非整个列表。如果您确实需要获取所有项目的列表,可以使用 .getall() 方法。 +``` + +### 请求ID(Request ID) + +.. column:: + +``` +通常,通过 `X-Request-ID` 头部追踪请求是很方便或必要的。您可以轻松地通过以下方式访问该请求ID:`request.id`。 +``` + +.. column:: + +```` +```python +@app.route("/") +async def handler(request): + return text(request.id) +``` + +```sh +curl localhost:8000 \ + -H "X-Request-ID: ABCDEF12345679" +ABCDEF12345679 +``` +```` + +## 响应(Response) + +Sanic 会自动为您设置以下响应头部(在适当的情况下): + +- `content-length` +- `content-type` +- `connection` +- `transfer-encoding` + +在大多数情况下,您无需担心设置这些响应头的信息。 + +.. column:: + +``` +如果您想设置任何其他头部,可以在路由处理器中或响应中间件中完成。 +``` + +.. column:: + +```` +```python +@app.route("/") +async def handler(request): + return text("Done.", headers={"content-language": "en-US"}) + +@app.middleware("response") +async def add_csp(request, response): + response.headers["content-security-policy"] = "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';base-uri 'self';form-action 'self'" +``` +```` + +.. column:: + +``` +一个常见的[中间件](middleware.md)应用场景是向每个响应添加一个 `X-Request-ID` 头部。如上所述:`request.id` 将提供来自传入请求的 ID。但是,即使请求头中没有提供 ID,也会自动为您提供一个 ID。 + +[有关更多详细信息,请查阅 API 文档](https://sanic.readthedocs.io/en/latest/sanic/api_reference.html#sanic.request.Request.id) +``` + +.. column:: + +```` +```python +@app.route("/") +async def handler(request): + return text(str(request.id)) + +@app.on_response +async def add_request_id_header(request, response): + response.headers["X-Request-ID"] = request.id +``` + +```sh +curl localhost:8000 -i +HTTP/1.1 200 OK +X-Request-ID: 805a958e-9906-4e7a-8fe0-cbe83590431b +content-length: 36 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +805a958e-9906-4e7a-8fe0-cbe83590431b +``` +```` diff --git a/guide/content/zh/guide/basics/listeners.md b/guide/content/zh/guide/basics/listeners.md new file mode 100644 index 0000000000..9f9655eec9 --- /dev/null +++ b/guide/content/zh/guide/basics/listeners.md @@ -0,0 +1,326 @@ +# 监听器(Listeners) + +Sanic 提供了八个(8)机会让您在应用程序服务器生命周期的不同阶段注入操作。 这还不包括[信号](../advanced/signals.md),后者允许进一步自定义注入。 + +有两个(2)**仅**在主 Sanic 进程中运行(即每次调用 `sanic server.app` 时运行一次)。 + +- `main_process_start` +- `main_process_stop` + +另外有两个(2)是在启用自动重载功能时**仅**在重载进程中运行的。 + +- `reload_process_start` +- `reload_process_stop` + +\*在 v22.3中添加 `reload_process_start` 和 `reload_process_stop` \* + +还有四个(4)允许您在服务器启动(startup)或关闭(teardown )时执行启动/清理代码。 + +- `before_server_start` +- `after_server_start` +- `before_server_stop` +- `after_server_stop` + +工作者进程的生命周期如下所示: + +.. mermaid:: + +``` +sequenceDiagram +autonumber +participant Process +participant Worker +participant Listener +participant Handler +Note over Process: sanic server.app +loop + Process->>Listener: @app.main_process_start + Listener->>Handler: Invoke event handler +end +Process->>Worker: Run workers +loop Start each worker + loop + Worker->>Listener: @app.before_server_start + Listener->>Handler: Invoke event handler + end + Note over Worker: Server status: started + loop + Worker->>Listener: @app.after_server_start + Listener->>Handler: Invoke event handler + end + Note over Worker: Server status: ready +end +Process->>Worker: Graceful shutdown +loop Stop each worker + loop + Worker->>Listener: @app.before_server_stop + Listener->>Handler: Invoke event handler + end + Note over Worker: Server status: stopped + loop + Worker->>Listener: @app.after_server_stop + Listener->>Handler: Invoke event handler + end + Note over Worker: Server status: closed +end +loop + Process->>Listener: @app.main_process_stop + Listener->>Handler: Invoke event handler +end +Note over Process: exit +``` + +重新加载进程位于负责启动和停止 Sanic 进程的外部进程内,独立于上述工作者进程之外运行。 请看下面的例子: + +```python +@app.reload_process_start +async def reload_start(*_): + print(">>>>>> reload_start <<<<<<") + +@app.main_process_start +async def main_start(*_): + print(">>>>>> main_start <<<<<<") + +@app.before_server_start +async def before_start(*_): + print(">>>>>> before_start <<<<<<") +``` + +如果该应用程序在开启自动重载的情况下运行,当重新加载进程启动时,会调用一次 `reload_start` 函数。 当主进程启动时,`main_start` 函数也会被调用一次。 **然而**,`before_start` 函数会在每个启动的工作者进程中被调用一次,并且每当文件保存导致工作者进程重启时,也会再次调用。 + +## 注册监听器(Attaching a listener) + +.. column:: + +``` +将函数设置为监听器的过程类似于声明路由。 + +当前正在运行的 `Sanic()` 实例会被注入到监听器中。 +``` + +.. column:: + +```` +```python +async def setup_db(app): + app.ctx.db = await db_setup() + +app.register_listener(setup_db, "before_server_start") +``` +```` + +.. column:: + +``` +`Sanic` 应用实例还提供了一个便利的装饰器。 +``` + +.. column:: + +```` +```python +@app.listener("before_server_start") +async def setup_db(app): + app.ctx.db = await db_setup() +``` +```` + +.. column:: + +``` +在 v22.3 版本之前,应用程序实例和当前事件循环都会被注入到函数中。然而,默认情况下只会注入应用程序实例。如果您的函数签名同时接受这两个参数,那么应用程序实例和循环将会像下面所示那样都被注入。 +``` + +.. column:: + +```` +```python +@app.listener("before_server_start") +async def setup_db(app, loop): + app.ctx.db = await db_setup() +``` +```` + +.. column:: + +``` +您甚至可以进一步缩短装饰器。这对于具有自动补全功能的 IDE 尤其有用。 +``` + +.. column:: + +```` +```python +@app.before_server_start +async def setup_db(app): + app.ctx.db = await db_setup() +``` +```` + +## 执行顺序(Order of execution) + +在启动期间,监听器按照声明的顺序执行,在关闭期间则按照声明的反向顺序执行。 + +| | 执行阶段 | 执行顺序 | +| --------------------- | --------------- | ------------- | +| `main_process_start` | main startup | regular 🙂 ⬇️ | +| `before_server_start` | worker startup | regular 🙂 ⬇️ | +| `after_server_start` | worker startup | regular 🙂 ⬇️ | +| `before_server_stop` | worker shutdown | 🙃 ⬆️ reverse | +| `after_server_stop` | worker shutdown | 🙃 ⬆️ reverse | +| `main_process_stop` | main shutdown | 🙃 ⬆️ reverse | + +鉴于以下设置,如果我们运行两个工作者进程,我们预期会在控制台看到以下输出: + +.. column:: + +```` +```python +@app.listener("before_server_start") +async def listener_1(app, loop): + print("listener_1") + +@app.before_server_start +async def listener_2(app, loop): + print("listener_2") + +@app.listener("after_server_start") +async def listener_3(app, loop): + print("listener_3") + +@app.after_server_start +async def listener_4(app, loop): + print("listener_4") + +@app.listener("before_server_stop") +async def listener_5(app, loop): + print("listener_5") + +@app.before_server_stop +async def listener_6(app, loop): + print("listener_6") + +@app.listener("after_server_stop") +async def listener_7(app, loop): + print("listener_7") + +@app.after_server_stop +async def listener_8(app, loop): + print("listener_8") +``` +```` + +.. column:: + +```` +```bash +[pid: 1000000] [INFO] Goin' Fast @ http://127.0.0.1:9999 +[pid: 1000000] [INFO] listener_0 +[pid: 1111111] [INFO] listener_1 +[pid: 1111111] [INFO] listener_2 +[pid: 1111111] [INFO] listener_3 +[pid: 1111111] [INFO] listener_4 +[pid: 1111111] [INFO] Starting worker [1111111] +[pid: 1222222] [INFO] listener_1 +[pid: 1222222] [INFO] listener_2 +[pid: 1222222] [INFO] listener_3 +[pid: 1222222] [INFO] listener_4 +[pid: 1222222] [INFO] Starting worker [1222222] +[pid: 1111111] [INFO] Stopping worker [1111111] +[pid: 1222222] [INFO] Stopping worker [1222222] +[pid: 1222222] [INFO] listener_6 +[pid: 1222222] [INFO] listener_5 +[pid: 1222222] [INFO] listener_8 +[pid: 1222222] [INFO] listener_7 +[pid: 1111111] [INFO] listener_6 +[pid: 1111111] [INFO] listener_5 +[pid: 1111111] [INFO] listener_8 +[pid: 1111111] [INFO] listener_7 +[pid: 1000000] [INFO] listener_9 +[pid: 1000000] [INFO] Server Stopped +``` +在上面的例子中,请注意存在三个运行中的进程: + +- `pid: 1000000` - The *main* process +- `pid: 1111111` - Worker 1 +- `pid: 1222222` - Worker 2 + +*尽管我们的示例先展示了所有属于一个工作者(worker)的输出,然后展示了另一个工作者(worker)的所有输出,但在实际情况中,由于这些进程是在不同的进程中运行的,不同进程之间的执行顺序并不保证一致。但是,您完全可以确定的是,单一工作者(worker)进程 **总是** 会保持其内部执行顺序不变。* +```` + +.. tip:: 提示一下 + +``` +这种情况的实际结果是,如果在 `before_server_start` 处理器中的第一个监听器设置了数据库连接,那么在此之后注册的监听器可以依赖于在它们启动和停止时该连接都处于活跃状态。 +``` + +### 优先级(Priority) + +在v23.12中,将`priority`关键词参数添加到听众中。 这使得可以微调听器的执行顺序。 默认优先级是 `0'。 优先级较高的监听器将先执行。 具有相同优先级的侦听器将按照他们注册的顺序执行。 此外,连接到 `app`实例的侦听器将在连接到`Blueprint\` 实例的侦听器之前执行。 + +决定执行顺序的整体规则如下: + +1. 先级降序排列 +2. 应用程序级别的监听器优先于蓝图级别的监听器执行 +3. 按照注册顺序执行 + +.. 列: + +```` +作为示例,考虑以下内容,它将打印: + +```bash +third +bp_third +second +bp_second +first +fourth +bp_first +``` +```` + +.. column:: + +```` +```python +@app.before_server_start +async def first(app): + print("first") + +@app.listener("before_server_start", priority=2) +async def second(app): + print("second") + +@app.before_server_start(priority=3) +async def third(app): + print("third") + +@bp.before_server_start +async def bp_first(app): + print("bp_first") + +@bp.listener("before_server_start", priority=2) +async def bp_second(app): + print("bp_second") + +@bp.before_server_start(priority=3) +async def bp_third(app): + print("bp_third") + +@app.before_server_start +async def fourth(app): + print("fourth") + +app.blueprint(bp) +``` +```` + +## ASGI 模式 (ASGI Mode) + +如果你使用ASGI服务器运行应用程序,请注意以下变化: + +- `reload_process_start` 和 `reload_process_stop` 将被**忽略** +- `main_process_start` 和 `main_process_stop` 将被**忽略** +- `before_server_start` 尽可能早地运行,并且将在 `after_server_start ` 之前执行,但从技术上讲,在此时服务器已经启动了 +- `after_server_stop` 尽可能晚地运行,并且将在 `before_server_stop` 之后执行,但从技术上讲,在此时服务器仍在运行中 diff --git a/guide/content/zh/guide/basics/middleware.md b/guide/content/zh/guide/basics/middleware.md new file mode 100644 index 0000000000..a6c0671e2e --- /dev/null +++ b/guide/content/zh/guide/basics/middleware.md @@ -0,0 +1,278 @@ +# 中间件(Middleware) + +监听器(listeners )允许您将功能附加到工作进程的生命周期中,中间件(middleware )则允许您将功能附加到HTTP流的生命周期中。 + +```python +@app.on_request +async def example(request): + print("I execute before the handler.") +``` + +您可以选择在处理器执行前或执行后执行中间件。 + +```python +@app.on_response +async def example(request, response): + print("I execute after the handler.") +``` + +.. mermaid:: + +``` +sequenceDiagram +autonumber +participant Worker +participant Middleware +participant MiddlewareHandler +participant RouteHandler +Note over Worker: Incoming HTTP request +loop + Worker->>Middleware: @app.on_request + Middleware->>MiddlewareHandler: Invoke middleware handler + MiddlewareHandler-->>Worker: Return response (optional) +end +rect rgba(255, 13, 104, .1) +Worker->>RouteHandler: Invoke route handler +RouteHandler->>Worker: Return response +end +loop + Worker->>Middleware: @app.on_response + Middleware->>MiddlewareHandler: Invoke middleware handler + MiddlewareHandler-->>Worker: Return response (optional) +end +Note over Worker: Deliver response +``` + +## 注册中间件(Attaching middleware) + +.. column:: + +``` +到现在为止,这个概念应该已经很熟悉了。您需要做的只是声明何时希望中间件执行:是在请求(`request`)阶段还是响应(`response`)阶段。 +``` + +.. column:: + +```` +```python +async def extract_user(request): + request.ctx.user = await extract_user_from_request(request) + +app.register_middleware(extract_user, "request") +``` +```` + +.. column:: + +``` +同样,`Sanic` 应用实例也提供了一个便捷的装饰器。 +``` + +.. column:: + +```` +```python +@app.middleware("request") +async def extract_user(request): + request.ctx.user = await extract_user_from_request(request) +``` +```` + +.. column:: + +``` +响应中间件同时接收 `request` 和 `response` 参数。 +``` + +.. column:: + +```` +```python +@app.middleware('response') +async def prevent_xss(request, response): + response.headers["x-xss-protection"] = "1; mode=block" +``` +```` + +.. column:: + +``` +您甚至可以进一步简化装饰器。如果您使用的IDE支持自动补全,这将非常有帮助。 + +这是首选用法,也是我们后续将会采用的方式。 +``` + +.. column:: + +```` +```python +@app.on_request +async def extract_user(request): + ... + +@app.on_response +async def prevent_xss(request, response): + ... +``` +```` + +## 修改(Modification) + +只要中间件不返回请求或响应参数,它可以修改接收到的请求或响应参数。 + +.. column:: + +``` +#### 执行顺序 + +1. Request middleware: `add_key` +2. Route handler: `index` +3. Response middleware: `prevent_xss` +4. Response middleware: `custom_banner` +``` + +.. column:: + +```` +```python +@app.on_request +async def add_key(request): + # Arbitrary data may be stored in request context: + request.ctx.foo = "bar" + +@app.on_response +async def custom_banner(request, response): + response.headers["Server"] = "Fake-Server" + +@app.on_response +async def prevent_xss(request, response): + response.headers["x-xss-protection"] = "1; mode=block" + +@app.get("/") +async def index(request): + return text(request.ctx.foo) + +``` +```` + +.. column:: + +``` +您可以修改 `request.match_info`。例如,在中间件中,可以利用这一有用特性将 `a-slug` 转换为 `a_slug`。 +``` + +.. column:: + +```` +```python +@app.on_request +def convert_slug_to_underscore(request: Request): + request.match_info["slug"] = request.match_info["slug"].replace("-", "_") + +@app.get("/") +async def handler(request, slug): + return text(slug) +``` +``` +$ curl localhost:9999/foo-bar-baz +foo_bar_baz +``` +```` + +## 提前响应(Resonding early) + +.. column:: + +``` +如果中间件返回一个 `HTTPResponse` 对象,则请求处理将停止,并返回该响应。如果在到达路由处理器之前发生这种情况,则不会调用处理器。返回响应也将阻止任何其他中间件继续执行。 +``` + +.. tip:: 提示 + +``` +您可以在中间件处理器中返回 `None` 值来停止执行,以允许请求正常进行处理。当使用早期返回机制避免在此中间件处理器内部处理请求时,这一做法十分有用。 +``` + +.. column:: + +```` +```python +@app.on_request +async def halt_request(request): + return text("I halted the request") + +@app.on_response +async def halt_response(request, response): + return text("I halted the response") +``` +```` + +## 执行顺序(Order of execution) + +请求中间件按照声明的顺序执行。 响应中间件按**相反顺序**执行。 + +按照下面的代码,我们应该期望在控制台看到这样的输出结果。 + +.. column:: + +```` +```python +@app.on_request +async def middleware_1(request): + print("middleware_1") + +@app.on_request +async def middleware_2(request): + print("middleware_2") + +@app.on_response +async def middleware_3(request, response): + print("middleware_3") + +@app.on_response +async def middleware_4(request, response): + print("middleware_4") + +@app.get("/handler") +async def handler(request): + print("~ handler ~") + return text("Done.") +``` +```` + +.. column:: + +```` +```bash +middleware_1 +middleware_2 +~ handler ~ +middleware_4 +middleware_3 +[INFO][127.0.0.1:44788]: GET http://localhost:8000/handler 200 5 +``` +```` + +### 中间件优先级(Middleware priority) + +.. column:: + +``` +您可以通过分配更高的优先级来修改中间件执行的顺序。这一操作在定义中间件时进行。优先级值越高,相对于其他中间件,其执行就越早。默认情况下,中间件的优先级为 `0`。 +``` + +.. column:: + +```` +```python +@app.on_request +async def low_priority(request): + ... + +@app.on_request(priority=99) +async def high_priority(request): + ... +``` +```` + +\*添加于 v22.9 \* diff --git a/guide/content/zh/guide/basics/request.md b/guide/content/zh/guide/basics/request.md new file mode 100644 index 0000000000..f387bdb28f --- /dev/null +++ b/guide/content/zh/guide/basics/request.md @@ -0,0 +1,476 @@ +# 请求(Request) + +查看 API 文档: [sanic.request](/api/sanic.request) + +:class: `sanic.request.Request` 实例的参数中包含大量有用的信息。 详情请参阅[API 文档](https://sanic.readthedocs.io/)。 + +正如我们在 [响应函数(Handlers)](./handlers) 部分中所看到的,路由处理器的第一个参数通常是 :class:`sanic.request.Request` 对象。 因为Sanic是一个异步框架,处理器将在一个 [`asyncio.Task`](https://docs.python.org/3/library/asyncio-task.html#asyncio.Task) 内运行,并由事件循环(event loop)调度。 这意味着处理器将在一个隔离的上下文中执行,并且请求对象(Rquest)将是该处理器任务所独有的。 + +.. column:: + +``` +按照惯例,参数被命名为`request`,但您可以根据需要随意命名。参数的名称并不重要。以下两个处理器的写法都是有效的。 +``` + +.. column:: + +```` +```python +@app.get("/foo") +async def typical_use_case(request): + return text("I said foo!") +``` + +```python +@app.get("/foo") +async def atypical_use_case(req): + return text("I said foo!") +``` +```` + +.. column:: + +``` +为请求的对象添加一个注解变是超级简单的 +``` + +.. column:: + +```` +```python +from sanic.request import Request +from sanic.response import text + +@app.get("/typed") +async def typed_handler(request: Request): + return text("Done.") +``` +```` + +.. tip:: 提示 + +``` +为了方便起见,假设您正在使用现代IDE,您应当利用类型注解来辅助完成代码提示和文档编写。这对于使用request对象时尤其有帮助,因为它具有**许多**属性和方法。 + +若要查看所有可用属性和方法的完整列表,请参阅 [API 文档](/api/sanic.request). +``` + +## 请求体(Body) + +`Request`对象允许您用以下几种不同的方式访问请求体的内容。 + +### JSON(json数据) + +.. column:: + +``` +**参数**: `request.json` +**描述**: 已解析的 JSON 对象 +``` + +.. column:: + +```` +```bash +$ curl localhost:8000 -d '{"foo": "bar"}' +``` + +```python +>>> print(request.json) +{'foo': 'bar'} +``` +```` + +### Raw(原始数据) + +.. column:: + +``` +**参数**: `request.body` +**描述**: 请求正文中的原始字节 +``` + +.. column:: + +```` +```bash +$ curl localhost:8000 -d '{"foo": "bar"}' +``` + +```python +>>> print(request.body) +b'{"foo": "bar"}' +``` +```` + +### Form(表单数据) + +.. column:: + +``` +**参数**: `request.form` +**描述**: 表单数据 + +.. tip:: 额外补充 + +`request.form`对象是几种类型之一,它是一个字典,其中每个值都是一个列表。这是因为HTTP协议允许单个键被重复用来发送多个值。 + +大多数情况下,您可能希望使用`.get()`方法访问第一个元素而不是一个列表。如果您确实需要所有项的列表,您可以使用`.getlist()`方法。 +``` + +.. column:: + +```` +```bash +$ curl localhost:8000 -d 'foo=bar' +``` + +```python +>>> print(request.body) +b'foo=bar' + +>>> print(request.form) +{'foo': ['bar']} + +>>> print(request.form.get("foo")) +bar + +>>> print(request.form.getlist("foo")) +['bar'] +``` +```` + +### Uploaded(上传文件) + +.. column:: + +``` +**参数**: `request.files` +**描述**: 上传给服务器的文件数据 + +.. tip:: 额外提示 + +`request.files`对象是一种字典类型的实例,其中每个值都是一个列表。这是由于HTTP协议允许单个键被重复用来发送多个文件。 + +大多数时候,您可能希望通过`.get()`方法获取并访问第一个文件对象而非整个列表。如果您确实需要获取所有文件项的列表,您可以使用`.getlist()`方法。 +``` + +.. column:: + +```` +```bash +$ curl -F 'my_file=@/path/to/TEST' http://localhost:8000 +``` + +```python +>>> print(request.body) +b'--------------------------cb566ad845ad02d3\r\nContent-Disposition: form-data; name="my_file"; filename="TEST"\r\nContent-Type: application/octet-stream\r\n\r\nhello\n\r\n--------------------------cb566ad845ad02d3--\r\n' + +>>> print(request.files) +{'my_file': [File(type='application/octet-stream', body=b'hello\n', name='TEST')]} + +>>> print(request.files.get("my_file")) +File(type='application/octet-stream', body=b'hello\n', name='TEST') + +>>> print(request.files.getlist("my_file")) +[File(type='application/octet-stream', body=b'hello\n', name='TEST')] +``` +```` + +## 上下文(Context) + +### 请求上下文内容 + +request.ctx 对象是存储请求相关信息的地方。 它仅在请求的生命周期内存在,并且是针对该请求独一无二的。 + +这与app.ctx对象形成了对比,后者是在所有请求间共享的。 务必注意不要混淆它们! + +默认情况下,request.ctx对象是一个SimpleNamespace对象,允许您在其上设置任意属性。 Sanic不会对此对象做任何用途,因此您可以自由地按照需要使用它,无需担心名称冲突问题。 + +### 典型应用场景 + +这常常用来存储像认证用户详细信息这样的项目。 稍后我们会更多地进入 [middleware](./medileware.md),但这是一个简单的例子。 + +```python +@app.on_request +async def run_before_handler(request): + request.ctx.user = await fetch_user_by_token(request.token) + +@app.route('/hi') +async def hi_my_name_is(request): + if not request.ctx.user: + return text("Hmm... I don't know you) + return text(f"Hi, my name is {request.ctx.user.name}") +``` + +如您所见,`request.ctx`对象是一个很好的位置,用于存储您需要在多个处理器中访问的信息,从而使您的代码更加遵循DRY原则(Don't Repeat Yourself),也更易于维护。 但是,正如我们在中间件章节中将要学习的那样,您还可以使用它来存储在一个中间件中产生的信息,这些信息将在另一个中间件中使用。 + +### 连接上下文(Connection context) + +.. column:: + +``` + +很多时候,您的API需要为同一客户端并发(或连续)处理多个请求。这种情况在需要查询多个端点以获取数据的渐进式Web应用程序中经常发生。 + +HTTP协议要求通过使用 [保持连接头](../deployment/configuration.md#keep-alive-timeout).来减轻由连接引起的开销时间。 + +当多个请求共享单个连接时,Sanic提供了一个上下文对象,允许这些请求共享状态。 +``` + +.. column:: + +```` +```python +@app.on_request +async def increment_foo(request): + if not hasattr(request.conn_info.ctx, "foo"): + request.conn_info.ctx.foo = 0 + request.conn_info.ctx.foo += 1 + +@app.get("/") +async def count_foo(request): + return text(f"request.conn_info.ctx.foo={request.conn_info.ctx.foo}") +``` + +```bash +$ curl localhost:8000 localhost:8000 localhost:8000 +request.conn_info.ctx.foo=1 +request.conn_info.ctx.foo=2 +request.conn_info.ctx.foo=3 +``` +```` + +.. warning:: + +``` +虽然对于通过单一HTTP连接在请求之间存储信息而言,这是一个便利的位置,但不要假定单个连接上的所有请求都来自同一个最终用户。这是因为HTTP代理和负载均衡器可能会将多个连接复用到单个到服务器的连接中。 + +**切勿** 使用此机制来存储关于单个用户的信息。为此目的应使用`request.ctx`对象。 +``` + +### 自定义请求对象(Custom Request Objects) + +正如在[自定义app](./app.md#custom-requests)部分讨论的那样,您可以创建:class:`sanic.request.Request`的一个子类,以向请求对象添加更多功能。 这对于添加专属于您的应用程序的额外属性或方法非常有用。 + +.. column:: + +``` +例如,设想您的应用程序发送了一个包含用户ID的自定义头部。您可以创建一个自定义请求对象,它将解析该头部并将用户ID为您存储起来。 +``` + +.. column:: + +```` +```python +from sanic import Sanic, Request + +class CustomRequest(Request): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.user_id = self.headers.get("X-User-ID") + +app = Sanic("Example", request_class=CustomRequest) +``` +```` + +.. 列: + +``` +现在,您可以在处理器中访问 `user_id` 属性。 +``` + +.. column:: + +```` +```python +@app.route("/") +async def handler(request: CustomRequest): + return text(f"User ID: {request.user_id}") +``` +```` + +### 自定义请求上下文(Custom Request Context) + +默认情况下,请求上下文(`request.ctx`) 是一个[`Simpenamespace`](https://docs.python.org/3/library/types.html#types.SimpleNamespace) 对象,允许您在它上设置任意属性。 尽管在整个应用程序中重用这一逻辑非常有助于提高效率,但在开发过程中可能会遇到困难,因为IDE无法知道有哪些可用的属性。 + +为了解决这个问题,您可以创建一个自定义请求上下文对象,以替代默认的`SimpleNamespace`。 这样您可以在上下文对象中添加类型提示,并使其在您的IDE中可用。 + +.. column:: + +``` +首先,通过继承 :class:`sanic.request.Request` 类来创建自定义请求类型。接着,您需要添加一个 `make_context()` 方法,该方法返回您的自定义上下文对象实例。*注意:`make_context` 方法应为静态方法。* +``` + +.. column:: + +```` +```python +from sanic import Sanic, Request +from types import SimpleNamespace + +class CustomRequest(Request): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.ctx.user_id = self.headers.get("X-User-ID") + + @staticmethod + def make_context() -> CustomContext: + return CustomContext() + +@dataclass +class CustomContext: + user_id: str = None +``` +```` + +.. note:: 注意 + +``` +这是一个Sanic高级用户的特性,使得在大型代码库中拥有类型化的请求上下文对象变得极为方便。当然这不是必需的,但会非常有帮助。 +``` + +_添加于 v23.6_ + +## 路径参数(Parameters) + +.. column:: + +``` +从路径参数中提取的值会被注入到处理器作为参数,更具体地说,是以关键字参数的形式。关于这一点,在[路由章节](./routing.md)中有更多详细信息。 +``` + +.. column:: + +```` +```python +@app.route('/tag/') +async def tag_handler(request, tag): + return text("Tag - {}".format(tag)) + +# or, explicitly as keyword arguments +@app.route('/tag/') +async def tag_handler(request, *, tag): + return text("Tag - {}".format(tag)) +``` +```` + +## 查询参数(Arguments) + +在 "request" 实例上有两个属性来获取查询参数: + +- `request.args` +- `requery_args` + +这些让您能够从请求路径中访问查询参数(URL中`?`号后面的部分)。 + +### 典型的使用情况 + +在大多数情况下,您将想使用 `request.args` 对象来访问查询参数。 它是解析后的查询字符串,存储为一个字典。 + +迄今为止,这是最常见的模式。 + +.. column:: + +``` +考虑这样一个示例:我们有一个带有`q`参数的`/search`路由入口,我们希望使用这个参数来进行搜索。 +``` + +.. column:: + +```` +```python +@app.get("/search") +async def search(request): + query = request.args.get("q") + if not query: + return text("No query string provided") + return text(f"Searching for: {query}") +``` +```` + +### 解析查询字符串 + +然而,在某些情况下,您可能希望以原始字符串形式或元组列表形式访问查询字符串。 为此,您可以使用`request.query_string` 和 `request.query_args` 属性。 + +此外还应注意,HTTP协议允许单个键拥有多个值。 虽然`request.args`看起来像一个常规字典,但实际上它是一种特殊类型,允许单个键对应多个值。 您可以通过`request.args.getlist()`方法来访问这些多个值。 + +- `request.query_string` - 原始查询字符串 +- `request.query_args` - 解析成元组的查询字符串 +- `request.args` - 解析成字典的查询字符串 + - `request.args.get()` - 获取查询参数中对应key的第一个值 (像普通字典一样) + - `request.args.getlist()` - 取查询参数中对应key的所有值(返回一个数组) + +```sh +curl "http://localhost:8000?key1=val1&key2=val2&key1=val3" +``` + +```python +>>> print(request.args) +{'key1': ['val1', 'val3'], 'key2': ['val2']} + +>>> print(request.args.get("key1")) +val1 + +>>> print(request.args.getlist("key1")) +['val1', 'val3'] + +>>> print(request.query_args) +[('key1', 'val1'), ('key2', 'val2'), ('key1', 'val3')] + +>>> print(request.query_string) +key1=val1&key2=val2&key1=val3 + +``` + +.. tip:: 额外提示 + +``` +`request.args`对象是一种字典类型的实例,其中每个值都是一个列表。这是由于HTTP协议允许单个参数名多次出现以传输多个值。 + +大多数情况下,您可能希望使用`.get()`方法来获取并访问第一个元素而非整个列表。但如果确实需要获取所有项目组成的列表,您可以使用`.getlist()`方法。 +``` + +## 获取当前请求对象 + +有时您可能会发现在应用程序中的某个位置无法直接访问当前请求, 比如在日志格式化时。 此时,您可以使用Request.get_current()来获取当前请求(如果需要的话)。 + +请记住,请求对象只限于运行处理器的那个单独的 [`asyncio.Task`](https://docs.python.org/3/library/asyncio-task.html#asyncio.Task) 内。 如果您不在那个任务中,就不会存在请求对象。 + +```python +import logging + +from sanic import Request, Sanic, json +from sanic.exceptions import SanicException +from sanic.log import LOGGING_CONFIG_DEFAULTS + +LOGGING_FORMAT = ( + "%(asctime)s - (%(name)s)[%(levelname)s][%(host)s]: " + "%(request_id)s %(request)s %(message)s %(status)d %(byte)d" +) + +old_factory = logging.getLogRecordFactory() + +def record_factory(*args, **kwargs): + record = old_factory(*args, **kwargs) + record.request_id = "" + + try: + request = Request.get_current() + except SanicException: + ... + else: + record.request_id = str(request.id) + + return record + +logging.setLogRecordFactory(record_factory) + + +LOGGING_CONFIG_DEFAULTS["formatters"]["access"]["format"] = LOGGING_FORMAT +app = Sanic("Example", log_config=LOGGING_CONFIG_DEFAULTS) +``` + +在此示例中,我们正在将`request.id`添加到每个访问日志消息中。 + +_添加于 v22.6_ diff --git a/guide/content/zh/guide/basics/response.md b/guide/content/zh/guide/basics/response.md new file mode 100644 index 0000000000..5a64a3f32b --- /dev/null +++ b/guide/content/zh/guide/basics/response.md @@ -0,0 +1,298 @@ +# 响应(Response) + +所有 [handlers](./handlers.md) _通常_返回一个响应对象, [middleware](./midd) 可以选择是否返回一个响应对象。 + +解释一下 + +- 除非处理器是一个流式响应,即发送字节到客户端模式的流式路由,否则返回值必须是:class:`sanic.response.HTTPResponse`类的实例(要了解更多关于这一例外情况,请参[阅流式响应](../advanced/streaming.md#response-streaming)) 否则,您需要返回一个响应。 +- 如果中间件确实返回了一个响应对象,则会使用该响应对象代替处理器原本的行为(更多细节请参阅 [中间件](./middleware.md) 部分)。 + +一个最基本的处理器可能如下所示。 使用 :class:`sanic.response.HTTPResponse` 类对象,您可以设置要返回给客户端的状态码、主体内容以及头部信息。 + +```python +from sanic import HTTPResponse, Sanic + +app = Sanic("TestApp") + +@app.route("") +def handler(_): + return HTTPResponse() +``` + +然而,通常使用下文讨论的一些便捷方法更为简单。 + +## 响应方式(Methods) + +生成响应对象最简便的方式是使用以下便捷函数。 + +### Text(文本) + +.. column:: + +``` +**默认Content-Type**: `text/plain; charset=utf-8` +**描述**: 返回纯文本 +``` + +.. column:: + +```` +```python +from sanic import text + +@app.route("/") +async def handler(request): + return text("Hi 😎") +``` +```` + +### HTML(HTML) + +.. column:: + +``` +**默认Content-Type**: `text/html; charset=utf-8` +**描述**: 返回一个 HTML 文档 +``` + +.. column:: + +```` +```python +from sanic import html + +@app.route("/") +async def handler(request): + return html('
Hi 😎
') +``` +```` + +### JSON(JSON) + +.. column:: + +``` +**默认 Content-Type**: `application/json` +**描述**: 返回 JSON 数据 +``` + +.. column:: + +```` +```python +from sanic import json + +@app.route("/") +async def handler(request): + return json({"foo": "bar"}) +``` +```` + +默认情况下,Sanic 使用 [`ujson`](https://github.com/ultrajson/ultrajson) 作为其首选的 JSON 解析器。 如果`ujson`模块没有被安装,程序将会退回到使用标准库中的`json`模块。 + +想要改变默认的json解析器,非常简单 + +```python +from sanic import json +from orjson import dumps + +json({"foo": "bar"}, dumps=dumps) +``` + +您还可以在初始化时全局声明在整个应用程序中使用哪个json解析器 + +```python +from orjson import dumps + +app = Sanic(..., dumps=dumps) +``` + +### File(文件) + +.. column:: + +``` +**默认Content-Type**:N/A +**描述**:返回一个文件 +``` + +.. column:: + +```` +```python +from sanic import file + +@app.route("/") +async def handler(request): + return await file("/path/to/whatever.png") +``` +```` + +Sanic 将会检查该文件,并尝试猜测其 MIME 类型,然后为内容类型使用合适的值。 如果您愿意,也可以明确指定: + +```python +file("/path/to/whatever.png", mime_type="image/png") +``` + +您也可以选择覆盖文件名称: + +```python +file("/path/to/whatever.png", filename="super-awesome-incredible.png") +``` + +### File Streaming(文件流) + +.. column:: + +``` +**默认Content-Type**: N/A +**描述**: 流一个文件到一个客户端, 当像视频一样流出大文件时有用。 +``` + +.. column:: + +```` +```python +from sanic.response import file_stream + +@app.route("/") +async def handler(request): + return await file_stream("/path/to/whatever.mp4") +``` +```` + +与 `file()` 方法类似,`file_stream()` 也会尝试确定文件的 MIME 类型。 + +### Raw(原始数据) + +.. column:: + +``` +**默认Content-Type**: `application/octet-stream` +**描述**: 发送原始字节而不对正文进行编码 +``` + +.. column:: + +```` +```python +from sanic import raw + +@app.route("/") +async def handler(request): + return raw(b"raw bytes") +``` +```` + +### Redirect(重定向) + +.. column:: + +``` +**默认Content-Type**: `text/html; charset=utf-8` +**描述**: 发送一个 `302` 响应来将客户重定向到另一个URL +``` + +.. column:: + +```` +```python +from sanic import redirect + +@app.route("/") +async def handler(request): + return redirect("/login") +``` +```` + +### Empty(空返回) + +.. column:: + +``` +**默认 Content-Type**: N/A +**描述**: 对于按照 [RFC 2616](https://tools.ietf.org/search/rfc2616#section-7.2.1) 规定响应空消息 +``` + +.. column:: + +```` +```python +from sanic import empty + +@app.route("/") +async def handler(request): + return empty() +``` + +默认的状态码是 `204` +```` + +## Default Status(默认状态码) + +响应默认的 HTTP 状态代码是 `200'。 如果您需要更改它,它可以通过在响应函数中传入指定的`status\`。 + +```python +@app.post("/") +async def create_new(request): + new_thing = await do_create(request) + return json({"created": True, "id": new_thing.thing_id}, status=201) +``` + +## Returning JSON data(返回json数据) + +从 v22.12 版本开始,当您使用 `sanic.json` 的便捷方法时,它将返回一个名为 :class:`sanic.response.types.JSONResponse` 的 `HTTPResponse` 子类。 此对象将提供多个便捷方法来修改常见的 JSON 正文。 + +```python +from sanic import json + +resp = json(...) +``` + +- `resp.set_body()` - 将 JSON 对象的正文设置为传递的值 +- `resp.append()` - 向正文追加一个值,如同 `list.append`(仅当根 JSON 是数组时有效) +- `resp.extend()` - 将一个值扩展到正文中,如同 `list.extend`(仅当根 JSON 是数组时有效) +- `resp.update()` -使用类似 `dict.update` 的方式更新正文(仅当根 JSON 是对象时有效) +- `resp.pop()` - 移除并返回一个值,如同 `list.pop` 或 `dict.pop`(仅当根 JSON 是数组或对象时有效) + +.. warning:: 警告⚠ + +``` +原始 Python 对象作为 `raw_body` 存储在 `JSONResponse` 对象上。虽然您可以安全地用新值覆盖这个值,但您应该**不要**尝试对其进行修改。相反,您应该使用上面列出的方法进行操作。 +``` + +```python +resp = json({"foo": "bar"}) + +# This is OKAY +resp.raw_body = {"foo": "bar", "something": "else"} + +# This is better +resp.set_body({"foo": "bar", "something": "else"}) + +# This is also works well +resp.update({"something": "else"}) + +# This is NOT OKAY +resp.raw_body.update({"something": "else"}) +``` + +```python +# Or, even treat it like a list +resp = json(["foo", "bar"]) + +# This is OKAY +resp.raw_body = ["foo", "bar", "something", "else"] + +# This is better +resp.extend(["something", "else"]) + +# This is also works well +resp.append("something") +resp.append("else") + +# This is NOT OKAY +resp.raw_body.append("something") +``` + +_添加于 v22.9_ diff --git a/guide/content/zh/guide/basics/routing.md b/guide/content/zh/guide/basics/routing.md new file mode 100644 index 0000000000..670095b73c --- /dev/null +++ b/guide/content/zh/guide/basics/routing.md @@ -0,0 +1,927 @@ +# 路由(Routing) + +.. column:: + +``` +至今为止,我们已经看到了这个装饰器的不同形式。 + +但它究竟是什么?以及我们应该如何使用它呢? +``` + +.. column:: + +```` +```python +@app.route("/stairway") + ... + +@app.get("/to") + ... + +@app.post("/heaven") + ... +``` +```` + +## 添加路由 + +.. column:: + +``` +将处理函数连接到路由入口的最基本方法是使用 `app.add_route()`。 + +详情请参考 [API 文档](https://sanic.readthedocs.io/en/stable/sanic/api_reference.html#sanic.app.Sanic.url_for) +``` + +.. column:: + +```` +```python +async def handler(request): + return text("OK") + +app.add_route(handler, "/test") +``` +```` + +.. column:: + +``` +默认情况下,路由可通过 HTTP `GET` 请求访问。您可以更改处理函数,使其响应一种或多种 HTTP 方法。 +``` + +.. column:: + +```` +```python +app.add_route( + handler, + '/test', + methods=["POST", "PUT"], +) +``` +```` + +.. column:: + +``` +使用装饰器语法,前面的例子等同于下面这样。 +``` + +.. column:: + +```` +```python +@app.route('/test', methods=["POST", "PUT"]) +async def handler(request): + return text('OK') +``` +```` + +## HTTP 方法(HTTP methods) + +每种标准 HTTP 方法都有一个便捷的装饰器。 + +### GET + +```python +@app.get('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET) + +### POST + +```python +@app.post('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) + +### PUT + +```python +@app.put('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) + +### PATCH + +```python +@app.patch('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) + +### DELETE + +```python +@app.delete('/test') +async def handler(request): + return text('OK') +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE) + +### HEAD + +```python +@app.head('/test') +async def handler(request): + return empty() +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) + +### OPTIONS + +```python +@app.options('/test') +async def handler(request): + return empty() +``` + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS) + +.. warning:: 警告⚠ + +```` +默认情况下,Sanic 只会在非安全 HTTP 方法(`POST`、`PUT`、`PATCH`、`DELETE`)上接受传入的请求正文。如果您想在任何其他方法上接收 HTTP 请求中的数据,您需要采取以下两种选项之一: + +**选项 #1 - 使用 `ignore_body` 告诉 Sanic 去接受请求体** +```python +@app.request("/path", ignore_body=False) +async def handler(_): + ... +``` + +**选项 #2 - 在处理函数中手动使用 `receive_body` 接受请求体** +```python +@app.get("/path") +async def handler(request: Request): + await request.receive_body() +``` +```` + +## 路径参数(Path parameters) + +.. column:: + +``` +Sanic 支持模式匹配,可以从 URL 路径中提取参数,并将这些参数作为关键字参数注入到路由处理函数中。 +``` + +.. column:: + +```` +```python +@app.get("/tag/") +async def tag_handler(request, tag): + return text("Tag - {}".format(tag)) +``` +```` + +.. column:: + +``` +您可以为参数声明一个类型。在匹配时,该类型将被强制执行,并且还会对该变量进行类型转换。 +``` + +.. 列: + +```` +```python +@app.get("/foo/") +async def uuid_handler(request, foo_id: UUID): + return text("UUID - {}".format(foo_id)) +``` +```` + +.. column:: + +``` +对于一些标准类型,如 `str`、`int` 和 `UUID`,Sanic 可以从函数签名中推断路径参数的类型。这意味着在路径参数定义中不一定总是需要包含类型。 +``` + +.. column:: + +```` +```python +@app.get("/foo/") # Notice there is no :uuid in the path parameter +async def uuid_handler(request, foo_id: UUID): + return text("UUID - {}".format(foo_id)) +``` +```` + +### 支持的类型 + +### `str` + +.. column:: + +``` +**正则表达式**: `r"[^/]+"` +**转换类型**: `str` +**匹配案例**: + +- `/path/to/Bob` +- `/path/to/Python%203` + +从 v22.3 版本开始,`str` 将不会匹配空字符串。对于这种行为,请参见 `strorempty`。 +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +### `strorempty` + +.. column:: + +``` +**正则表达式**: `r"[^/]*"` +**转换类型**: `str` +**匹配案例**: + +- `/path/to/Bob` +- `/path/to/Python%203` +- `/path/to/` + +与 `str` 路径参数类型不同,`strorempty` 也可以匹配空字符串路径段。 + +*添加于 v22.3* +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +### `int` + +.. column:: + +``` +**正则表达式**: `r"-?\d+"` +**转换类型**: `int` +**匹配案例**: + +- `/path/to/10` +- `/path/to/-10` + +_不匹配浮点数(float)、十六进制(hex)、八进制(octal)等_ +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: int): + ... +``` +```` + +### `float` + +.. column:: + +``` +**正则表达式**: `r"-?(?:\d+(?:\.\d*)?|\.\d+)"` +**转换类型**: `float` +**匹配案例**: + +- `/path/to/10` +- `/path/to/-10` +- `/path/to/1.5` +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: float): + ... +``` +```` + +### `alpha` + +.. column:: + +``` +**正则表达式**: `r"[A-Za-z]+"` +**转换类型**: `str` +**匹配实例**: + +- `/path/to/Bob` +- `/path/to/Python` + +_不匹配数字(digit)、空格(space )或其他特殊字符(special character)_ +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +### `slug` + +.. column:: + +``` +**正则表达式**: `r"[a-z0-9]+(?:-[a-z0-9]+)*"` +**转换类型**: `str` +**匹配案例**: + +- `/path/to/some-news-story` +- `/path/to/or-has-digits-123` + +*添加于v21.6* +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, article: str): + ... +``` +```` + +### `path` + +.. column:: + +``` +**正则表达式**: `r"[^/].*?"` +**转换类型**: `str` +**匹配案例**: +- `/path/to/hello` +- `/path/to/hello.txt` +- `/path/to/hello/world.txt` +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +.. warning:: 警告 + +``` +由于 `path` 类型会匹配 `/` 符号,您应在使用 `path` 类型时务必小心,并彻底测试您的模式,以免捕获到原本打算发往其他端点的流量。另外,根据您如何使用这种类型,可能会在您的应用程序中引入路径遍历漏洞。防止此类漏洞是您的责任,但如有需要,请随时在我们的社区频道寻求帮助:) +``` + +### `ymd` + +.. column:: + +``` +**正则表达式**: `r"^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))"` +**转换类型**: `datetime.date` +**匹配案例**: + +- `/path/to/2021-03-28` +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: datetime.date): + ... +``` +```` + +### `uuid` + +.. column:: + +``` +**正则表达式**: `r"[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}"` +**转换类型**: `UUID` +**匹配案例**: + +- `/path/to/123a123a-a12a-1a1a-a1a1-1a12a1a12345` +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: UUID): + ... +``` +```` + +### ext + +.. column:: + +``` +**正则表达式**: n/a +**转换类型**: *varies* +**匹配案例**: +``` + +.. column:: + +```` +```python +@app.route("/path/to/") +async def handler(request, foo: str, ext: str): + ... +``` +```` + +| 定义 | 示例 | 文件名 | 扩展 | +| ------------------------------------------------------------------------------------ | ----------------------------------------------------------- | -------- | --------------------------- | +| \ | page.txt | `"page"` | `"txt"` | +| \ | cat.jpg | `"cat"` | \`"jpg"" | +| \ | cat.jpg | `"cat"` | \`"jpg"" | +| | 123.txt | `123` | `"txt"` | +| | 123.svg | `123` | `"svg"` | +| | 3.14.tar.gz | `3.14` | \`"tar.gz"" | + +可以通过特殊的 ext 参数类型来匹配文件扩展名。 它采用一种特殊格式,允许您指定其他类型的参数作为文件名,并如上文示例表格所示,指定一个或多个特定扩展名。 + +它**不支持** `path` 参数类型。 + +_添加于 v22.3_ + +### 正则表达式 + +.. column:: + +``` +**正则表达式**: _whatever you insert_ +**转换类型**: `str` +**匹配案例**: + +- `/path/to/2021-01-01` + +这样您就可以自由地为您的应用场景定义特定的匹配模式。 + +在所示示例中,我们正在查找符合 `YYYY-MM-DD` 格式的日期。 +``` + +.. column:: + +```` +```python +@app.route(r"/path/to/") +async def handler(request, foo: str): + ... +``` +```` + +### 正则表达式匹配 + +相比于复杂的路由,上述例子往往过于简单,我们采用了完全不同的路由匹配模式,因此这里将详细解释正则表达式匹配的高级用法。 + +有时,您可能只想匹配路由的一部分: + +```text +/image/123456789.jpg +``` + +如果你想要匹配包含文件模式,并仅捕获其中的数字部分,那么确实需要运用一些正则表达式的技巧 😄: + +```python +app.route(r"/image/\d+)\.jpg>") +``` + +此外,所有以下这些匹配项也都是可以的: + +```python +@app.get(r"/") # matching on the full pattern +@app.get(r"/") # defining a single matching group +@app.get(r"/[a-z]{3}).txt>") # defining a single named matching group +@app.get(r"/[a-z]{3}).(?:txt)>") # defining a single named matching group, with one or more non-matching groups +``` + +另外,如果使用命名匹配组,其名称必须与段标签相同。 + +```python +@app.get(r"/\d+).jpg>") # OK +@app.get(r"/\d+).jpg>") # NOT OK +``` + +有关更多常规正则表达式用法,请参考 [正则表达式操作](https://docs.python.org/3/library/re.html) 。 + +## 动态访问(Generating a URL) + +.. column:: + +``` +Sanic 提供了一种基于处理程序方法名称生成 URL 的方法:`app.url_for()`。当您希望避免在应用中硬编码 URL 路径时,这非常有用;您可以仅引用处理程序名称即可。 +``` + +.. column:: + +```` +```python +@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/') +async def post_handler(request, post_id): + ... +``` +```` + +.. column:: + +``` +您可以传递任意数量的关键字参数。任何不是请求参数的项都将作为查询字符串的一部分实现。 +``` + +.. column:: + +```` +```python +assert app.url_for( + "post_handler", + post_id=5, + arg_one="one", + arg_two="two", +) == "/posts/5?arg_one=one&arg_two=two" +``` +```` + +.. column:: + +``` +同样支持对单一查询键传入多个值。 +``` + +.. column:: + +```` +```python +assert app.url_for( + "post_handler", + post_id=5, + arg_one=["one", "two"], +) == "/posts/5?arg_one=one&arg_one=two" +``` +```` + +### 特殊关键字参数 + +See API 文档 for more details. + +```python +app.url_for("post_handler", post_id=5, arg_one="one", _anchor="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) +# '//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) +# '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") +# 'http://another_server:8888/posts/5?arg_one=one&arg_one=two&arg_two=2#anchor' +``` + +### 自定义路由名称(Customizing a route name) + +.. column:: + +``` +可以通过在注册路由时传递 `name` 参数来自定义路由名称。 +``` + +.. column:: + +```` +```python +@app.get("/get", name="get_handler") +def handler(request): + return text("OK") +``` +```` + +.. column:: + +``` +现在,可以使用这个自定义名称来检索 URL。 +``` + +.. column:: + +```` +```python +assert app.url_for("get_handler", foo="bar") == "/get?foo=bar" +``` +```` + +## Websockets 路由(Websockets routes) + +.. column:: + +``` +WebSocket 路由的工作方式与 HTTP 方法类似。 +``` + +.. column:: + +```` +```python +async def handler(request, ws): + message = "Start" + while True: + await ws.send(message) + message = await ws.recv() + +app.add_websocket_route(handler, "/test") +``` +```` + +.. column:: + +``` +它还提供了一个便捷装饰器。 +``` + +.. column:: + +```` +```python +@app.websocket("/test") +async def handler(request, ws): + message = "Start" + while True: + await ws.send(message) + message = await ws.recv() +``` +```` + +请阅读[WebSocket 部分](/zh/guide/advanced/websockets.md),了解更多关于它们如何工作的内容。 + +## 严格匹配分隔符(Strict slashes) + +.. column:: + +``` +Sanic 路由可以根据 URL 是否包含尾部斜杠(/)进行严格的匹配配置。这一配置可以在以下几个层级进行,并遵循以下优先级顺序: + +1. 路由(Route) +2. 蓝图(Blueprint) +3. 蓝图组(BlueprintGroup) +4. 应用程序(Application) +``` + +.. column:: + +```` +```python +# provide default strict_slashes value for all routes +app = Sanic(__file__, strict_slashes=True) +``` + +```python +# overwrite strict_slashes value for specific route +@app.get("/get", strict_slashes=False) +def handler(request): + return text("OK") +``` + +```python +# it also works for blueprints +bp = Blueprint(__file__, strict_slashes=True) + +@bp.get("/bp/get", strict_slashes=False) +def handler(request): + return text("OK") +``` + +```python +bp1 = Blueprint(name="bp1", url_prefix="/bp1") +bp2 = Blueprint( + name="bp2", + url_prefix="/bp2", + strict_slashes=False, +) + +# This will enforce strict slashes check on the routes +# under bp1 but ignore bp2 as that has an explicitly +# set the strict slashes check to false +group = Blueprint.group([bp1, bp2], strict_slashes=True) +``` +```` + +## 静态文件(Static files) + +.. column:: + +``` +为了在 Sanic 中提供静态文件服务,请使用 `app.static()` 方法。 + +参数的顺序很重要: + +1. 文件将被服务的路由地址 +2. 服务器上的文件实际路径 + +欲了解更多信息,请参阅 [API 文档](https://sanic.readthedocs.io/en/stable/sanic/api/app.html#sanic.app.Sanic.static)。 +``` + +.. column:: + +```` +```python +app.static("/static/", "/path/to/directory/") +``` +```` + +.. tip:: + +``` +通常最好以尾部斜杠(/)结束您的目录路径(如 `/this/is/a/directory/`)。这样做能够更明确地消除歧义。 +``` + +.. column:: + +``` +您也可以单独提供单个文件的服务。 +``` + +.. column:: + +```` +```python +app.static("/", "/path/to/index.html") +``` +```` + +.. column:: + +``` +有时为你指定入口提供一个名称也会有所帮助。 +``` + +.. column:: + +```` +```python +app.static( + "/user/uploads/", + "/path/to/uploads/", + name="uploads", +) +``` +```` + +.. column:: + +``` +获取 URL 的工作方式与处理程序类似。但是,当我们需要获取目录中的特定文件时,还可以添加 `filename` 参数。 +``` + +.. column:: + +```` +```python +assert app.url_for( + "static", + name="static", + filename="file.txt", +) == "/static/file.txt" +``` +```python +assert app.url_for( + "static", + name="uploads", + filename="image.png", +) == "/user/uploads/image.png" + +``` +```` + +.. tip:: + +```` +如果您打算设置多个 `static()` 路由,强烈建议您手动为它们命名。这样做几乎可以肯定能缓解潜在的难以发现的错误问题。 + +```python +app.static("/user/uploads/", "/path/to/uploads/", name="uploads") +app.static("/user/profile/", "/path/to/profile/", name="profile_pics") +``` +```` + +#### 自动索引服务(Auto index serving) + +.. column:: + +``` +如果您有一目录静态文件应通过索引页面提供服务,您可以提供该索引页面的文件名。这样一来,当访问该目录 URL 时,系统将会自动提供索引页面服务。 +``` + +.. column:: + +```` +```python +app.static("/foo/", "/path/to/foo/", index="index.html") +``` +```` + +_添加于 v23.3_ + +#### 文件浏览器(File browser) + +.. column:: + +``` +当从静态处理器提供目录服务时,可以配置 Sanic 使用 `directory_view=True` 来显示一个基本的文件浏览器。 +``` + +.. column:: + +```` +```python +app.static("/uploads/", "/path/to/dir", directory_view=True) +``` +```` + +现在您可以在 Web 浏览器中浏览该目录了: + +![image](/assets/images/directory-view.png) + +_添加于 v23.3_ + +## 路由上下文(Route context) + +.. column:: + +``` +在定义路由时,您可以添加任意数量以 `ctx_` 前缀的关键字参数。这些值将被注入到路由的 `ctx` 对象中。 +``` + +.. column:: + +```` +```python +@app.get("/1", ctx_label="something") +async def handler1(request): + ... + +@app.get("/2", ctx_label="something") +async def handler2(request): + ... + +@app.get("/99") +async def handler99(request): + ... + +@app.on_request +async def do_something(request): + if request.route.ctx.label == "something": + ... +``` +```` + +_添加于 v21.12_ diff --git a/guide/content/zh/guide/basics/tasks.md b/guide/content/zh/guide/basics/tasks.md new file mode 100644 index 0000000000..dcdabddfe0 --- /dev/null +++ b/guide/content/zh/guide/basics/tasks.md @@ -0,0 +1,166 @@ +--- +title: 背景任务 +--- + +# 后台任务(Background tasks) + +## 创建任务(Creating Tasks) + +在异步 Python 中,常常希望能够方便地使用任务[tasks](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task)。 Sanic 提供了一种便捷的方法,可以将任务添加到当前**正在运行**的循环中, 类似于 `asyncio.create_task`。 若要在“App”循环启动前添加任务,请参阅下一节内容。 + +```python +async def notify_server_started_after_five_seconds(): + await asyncio.sleep(5) + print('Server successfully started!') + +app.add_task(notify_server_started_after_five_seconds()) +``` + +.. column:: + +``` +Sanic 会尝试自动注入应用实例,并将其作为参数传递给任务。 +``` + +.. column:: + +```` +```python +async def auto_inject(app): + await asyncio.sleep(5) + print(app.name) + +app.add_task(auto_inject) +``` +```` + +.. column:: + +``` +或者,您可以显式地传递 `app` 参数。 +``` + +.. column:: + +```` +```python +async def explicit_inject(app): + await asyncio.sleep(5) + print(app.name) + +app.add_task(explicit_inject(app)) +``` +```` + +## 在 `app.run` 之前添加任务 + +在应用运行之前(即在调用 app.run 之前),可以添加后台任务。 建议在这种情况下不要传递协程对象(即通过调用异步可调用函数创建的对象),而是仅传递可调用函数,Sanic 会在**每个工作进程**中自行创建协程对象。 请注意,这样添加的任务会在每个工作进程内以 `before_server_start `任务的形式运行,而不是在主进程中运行。 这一点具有特定的影响,请参阅该问题下的[这条评论](https://github.com/sanic-org/sanic/issues/2139#issuecomment-868993668) 在 [issue](https://github.com/sanic-org/sanic/issues/2139) 以获取更多细节。 + +若要在主进程中添加任务,请考虑使用装饰器 [`@app.main_process_start`](./listeners.md) 来添加任务。 请注意,直到这些任务完成,工作进程才开始启动。 + +.. column:: + +``` +在 `app.run` 之前添加任务的示例代码 +``` + +.. column:: + +```` +```python +async def slow_work(): + ... + +async def even_slower(num): + ... + +app = Sanic(...) +app.add_task(slow_work) # Note: we are passing the callable and not coroutine object ... +app.add_task(even_slower(10)) # ... or we can call the function and pass the coroutine. +app.run(...) +``` +```` + +## 命名任务(Named tasks) + +.. column:: + +``` +创建任务时,通过提供一个`name`,你可以让Sanic帮你跟踪这个任务。 +``` + +.. column:: + +```` +```python +app.add_task(troub_work, name="low_task") +``` +```` + +.. column:: + +``` +现在,你可以在应用程序的任何地方使用`get_task`来检索该任务实例。 +``` + +.. column:: + +```` +```python +task = app.get_task("slow_task") +``` +```` + +.. column:: + +``` +如果需要取消该任务,你可以使用`cancel_task`来完成。确保对它进行`await`调用。 +``` + +.. column:: + +```` +```python +await app.cancel_task("slow_task") +``` +```` + +.. column:: + +``` +所有已注册的任务都可以在`app.tasks`属性中找到。为了避免被取消的任务占用空间,你可能想要运行`app.purge_tasks`,它会清除所有已完成或被取消的任务。 +``` + +.. column:: + +```` +```python +app.purge_tasks() +``` +```` + +这种模式在处理`websockets`时尤其有用: + +```python +async def receiver(ws): + while True: + message = await ws.recv() + if not message: + break + print(f"Received: {message}") + +@app.websocket("/feed") +async def feed(request, ws): + task_name = f"receiver:{request.id}" + request.app.add_task(receiver(ws), name=task_name) + try: + while True: + await request.app.event("my.custom.event") + await ws.send("A message") + finally: + # When the websocket closes, let's cleanup the task + await request.app.cancel_task(task_name) + request.app.purge_tasks() +``` + +\*添加于 v21.12 \* diff --git a/guide/content/zh/guide/best-practices/blueprints.md b/guide/content/zh/guide/best-practices/blueprints.md new file mode 100644 index 0000000000..ea974807c8 --- /dev/null +++ b/guide/content/zh/guide/best-practices/blueprints.md @@ -0,0 +1,521 @@ +# 蓝图 + +## 概览 + +蓝图是可以在应用程序内用于子路由的对象。 蓝图不是将路线添加到应用程序实例中,而是定义了类似的添加路线的方法, 然后以灵活和可搭配的方式在申请中登记。 + +蓝图对于更大的应用程序特别有用,您的应用程序逻辑可以分成几组或几个责任领域。 + +## 创建和注册 + +.. 列: + +``` +首先,您必须创建一个蓝图。它有一个非常相似的 API 与 `Sanic()` 应用实例与许多相同的装饰师相同。 +``` + +.. 列: + +```` +```python +# ./my_bluprint.py +from sanic.response import json +from sanic import Blueprint + +bp = Blueprint(“my_bluprint") + +@bp. oute("/") +async def bp_root(request): + return json({"my": "blueprint"}) +``` +```` + +.. 列: + +``` +接下来,你在应用实例中注册它。 +``` + +.. 列: + +```` +```python +from sanic import Sanic +from my_blueprint import bp + +app = Sanic(__name__) +app.blueprint(bp) +``` +```` + +蓝图也有相同的`websocket()`装饰器和`add_websocket_route`方法实现websocket。 + +.. 列: + +``` +从 v21.12开始,蓝图可以在添加对象之前或之后注册。 以前,只有注册时附于蓝图的对象才会被加载到应用程序实例。 +``` + +.. 列: + +```` +```python +app.bluprint(bp) + +@bp.route("/") +async def bp_root(request): + ... +``` +```` + +## 正在复制 + +.. 列: + +``` +蓝图以及附加到它们的所有内容都可以使用 `copy()` 方法复制到新的实例。 唯一需要的参数是通过一个新的 `name` 。 然而,您也可以使用这个来覆盖旧蓝图中的任何值。 +``` + +.. 列: + +```` +```python +v1 = Blueprint("Version1", version=1) + +@v1.route("/something") +def something(request): + passe + +v2 = v1.copy("Version2", version=2) + +app. lueprint(v1) +app.bluprint(v2) +``` + +``` +可用路径: +/v1/some +/v2/some + +``` +```` + +\*添加于 v21.9 \* + +## 蓝图组 + +蓝图也可以作为列表或管道的一部分注册 如果登记员将通过任何次级蓝图序列递归循环,并相应地进行登记。 提供了蓝图群组方法来简化这个过程,允许“模拟”后端目录结构模仿从前端看到的东西。 请考虑这个(相当构思) 示例: + +```text +api/ +├──content/ +│ ├──authors.py +│ ├──static.py +│ └──__init__.py +├──info.py +└──__init__.py +app.py +``` + +.. 列: + +``` +#### 第一张蓝图 +``` + +.. 列: + +```` +```python +# api/content/authors.py +from sanic import Blueprint + +作者 = Bluprint("content_authors", url_prefix="/authors") +``` +```` + +.. 列: + +``` +#### 第二张蓝图 +``` + +.. 列: + +```` +```python +# api/content/static.py +from sanic import Blueprint + +static = Bluprint("content_static", url_prefix="/static") +``` +```` + +.. 列: + +``` +#### 蓝图组 +``` + +.. 列: + +```` +```python +# api/content/__init__.py +from sanic import Blueprint +from .static import static +from .authors import authors + +content = Blueprint.group(static, authors, url_prefix="/content") +``` +```` + +.. 列: + +``` +#### 第三张蓝图 +``` + +.. 列: + +```` +```python +# api/info.py +from sanic importer + +info = Bluprint("info", url_prefix="/info") +``` +```` + +.. 列: + +``` +#### 另一个蓝图组 +``` + +.. 列: + +```` +```python +# api/__init__.py +from sanic import Blueprint +from .content import content +from .info import info + +api = Blueprint.group(content, info, url_prefix="/api") +``` +```` + +.. 列: + +``` +#### 主要服务器 + +所有蓝图已注册 +``` + +.. 列: + +```` +```python +# app.py +from sanic import Sanic +from .api import api + +app = Sanic(__name__) +app.blueprint(api) +``` +```` + +### 蓝图组前缀和可编辑性 + +如上面的代码所示, 当你创建一个蓝图组时,你可以通过将 `url_prefix` 参数传递到 \`Blueprint,将该组中所有蓝图的 URL 前缀扩展到该组。 路由方法 这有助于为您的 API 创建模拟目录结构。 + +此外,还有一个“name_prefix”参数可用来使蓝图可重新使用和复制。 在对多个群组应用单一蓝图时是特别必要的。 通过这样做,蓝图将被注册为每个组的唯一名称。 它允许蓝图多次注册,并且每个路径都有一个唯一的标识符。 + +.. 列: + +``` +考虑这个示例。已生成的路由将被命名如下: +- `TestAppp.group-a_bp1.route1` +- `TestAppp.group-a_bp2.route2` +- `TestAppp.group-b_bp1.route1` +- `TestApp.group-b_bp2.route2` +``` + +.. 列: + +```` +```python +bp1 = Blueprint("bp1", url_prefix="/bp1") +bp2 = Blueprint("bp2", url_prefix="/bp2") + +bp1。 dd_route(lambda _ : ..., "/", name="route1") +bp2.add_route(lambda _: ..., "/", name="route2") + +group_a = Bluprint。 roup( + bp1, bp2, url_prefix="/group-a", name_prefix="group-a" +) +group_b = 蓝图。 roup( + bp1, bp2, url_prefix="/group-b", name_prefix="group-b" +) + +app = Sanic("TestApp") +app.bluprint(troup_a) +app.bluprint(group_b) +``` +```` + +_在 v23.6_ 中添加的名称前缀 + +## 中间件 + +.. 列: + +``` +蓝图也可以有只为其端点注册的中间件. +``` + +.. 列: + +```` +```python +@bp.middleware +async def print_on_request(request): + print("我是一个间谍") + +@bp. iddleware("request") +async def halt_request(request): + return text("我停止请求") + +@bp. iddleware("响应") +async def halt_response(request, response): + return text("我已停止响应") +``` +```` + +.. 列: + +``` +同样,使用蓝图组可以将中间件应用到整个一组嵌套蓝图。 +``` + +.. 列: + +```` +```python +bp1 = Blueprint("bp1", url_prefix="/bp1") +bp2 = Blueprint("bp2", url_prefix="/bp2") + +@bp1。 iddleware("request") +async def bp1_only_middleware(request): + print("applied on 蓝图: bp1 only") + +@bp1。 oute("/") +async def bp1_route(request): + return text("bp1") + +@bp2。 oute("/") +async def bp2_route(request, param): + return text(param) + +group = Bluprint.group(bp1, bp2) + +@group。 iddleware("request") +async def group_middleware(request): + print("common middleware applied for bp1 and bp2") + +# 注册应用程序 +app之下的蓝图组。 lueprint(group) +``` +```` + +## 例外 + +.. 列: + +``` +就像其他[异常处理](./exceptions.md)一样,您可以定义蓝图特定处理器。 +``` + +.. 列: + +```` +```python +@bp.exception(NotFound) +def ignorre_404s(请求, 异常): + return text("Yep, 我完全发现页面: {}".format (crequest.url)) +``` +```` + +## 静态文件 + +.. 列: + +``` +蓝图也可以有自己的静态处理程序 +``` + +.. 列: + +```` +```python +bp = Blueprint("bp", url_prefix="/bp") +bp.static("/web/path", "/folder/to/serve") +bp.static("/web/path", "/folder/to/server", name="上传") +``` +```` + +.. 列: + +``` +然后可以通过 `url_for()`检索。更多信息请见 [routing](/guide/basics/routing.md)。 +``` + +.. 列: + +```` +```python +>> > print(app.url_for("static", name="bp.uploads", filename="file.txt")) +'/bp/web/path/file.txt' +``` +```` + +## 监听器 + +.. 列: + +``` +蓝图也可以实现 [listeners](/guide/basics/listeners.md). +``` + +.. 列: + +```` +```python +@bp.listener("before_server_start") +async def before_server_start(app, loop): + + +@bp.listener("after_server_stop") +async def after _server_stop(app, loop): + ... +``` +```` + +## Versioning + +正如[版本部分](/guide/advanced/versioning.md)所讨论的,蓝图可以用于实现不同版本的Web API。 + +.. 列: + +``` +`version`将作为`/v1`或`/v2`等添加到路线上。 +``` + +.. 列: + +```` +```python +auth1 = Blueprint("auth", url_prefix="/auth", version=1) +auth2 = Blueprint("auth", url_prefix="/auth", version=2) +``` +```` + +.. 列: + +``` +当我们在应用上注册我们的蓝图时,路由`/v1/auth`和`/v2/auth`将指向个别的蓝图, 允许为每个API版本创建子站点。 +``` + +.. 列: + +```` +```python +from auth_blueprints import auth1, auth2 + +app = Sanic(__name__) +app.blueprint(auth1) +app.blueprint(auth2) +``` +```` + +.. 列: + +``` +同时也可以将蓝图归类到 '蓝图组' 实体,并且同时将它们的多个版本一起使用 +。 +``` + +.. 列: + +```` +```python +auth = Blueprint("auth", url_prefix="/auth") +metrics = Blueprint("metrics", url_prefix="/metrics") + +group = Blueprint. roup(aut, meters, version="v1") + +# 这将为API提供以下URL路径 +# /v1/auth/ 和 /v1/metrics +``` +```` + +## 可合成的 + +“蓝图”可以注册到多个组,每个“蓝图组”本身都可以注册和嵌套。 这就创造了无限的 `Blueprint` 组成。 + +\*添加于 v21.6 \* + +.. 列: + +``` +看看这个示例,并看看这两个处理程序如何实际装载为五(5)条不同的路径。 +``` + +.. 列: + +```` +```python +app = Sanic(__name__) +blueprint_1 = Blueprint("blueprint_1", url_prefix="/bp1") +blueprint_2 = Blueprint("blueprint_2", url_prefix="/bp2") +group = Blueprint.group( + blueprint_1, + blueprint_2, + version=1, + version_prefix="/api/v", + url_prefix="/grouped", + strict_slashes=True, +) +primary = Blueprint.group(group, url_prefix="/primary") + +@blueprint_1.route("/") +def blueprint_1_default_route(request): + return text("BP1_OK") + +@blueprint_2.route("/") +def blueprint_2_default_route(request): + return text("BP2_OK") + +app.blueprint(group) +app.blueprint(primary) +app.blueprint(blueprint_1) + +# The mounted paths: +# /api/v1/grouped/bp1/ +# /api/v1/grouped/bp2/ +# /api/v1/primary/grouped/bp1 +# /api/v1/primary/grouped/bp2 +# /bp1 + +``` +```` + +## 正在生成 URL + +当生成一个 url_for()\`的URL时,端点名称将是表单的: + +```text +{blueprint_name}.{handler_name} +``` diff --git a/guide/content/zh/guide/best-practices/decorators.md b/guide/content/zh/guide/best-practices/decorators.md new file mode 100644 index 0000000000..af8bbdfa05 --- /dev/null +++ b/guide/content/zh/guide/best-practices/decorators.md @@ -0,0 +1,195 @@ +# 装饰符 + +创建一致的 DRY 网页API 的最佳方法之一是利用装饰器从处理器中移除功能。 并使之可以在你的意见中重复出现。 + +.. 列: + +``` +因此,非常常见的情况是看到一个带有几个装饰师的Sanic视图处理器。 +``` + +.. 列: + +```` +```python +@app.get("/orders") +@authorized("view_order") +@validate_list_params() +@inject_user() +async def get_order_details(request, params, user): + ... +``` +```` + +## 示例 + +这是一个帮助您创建装饰程序的启动模板。 + +在这个示例中,让我们说你想要检查用户是否被授权访问某个特定端点。 您可以创建一个装饰器,包装处理函数。 检查客户是否有权访问资源的请求,并发送适当的响应。 + +```python +from functools import wraps +from sanic.response import json + +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 = await 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"}) +``` + +## 模板 + +装饰器是 **基础** ,用于构建带萨尼语的应用程序。 它们提高了您的代码的可携带性和可维护性。 + +在解析Python的Zen时:“[decorators] 是一个很好的主意——让我们做更多的事!” + +为了使它们更容易实现,在这里是三个可复制/可粘贴代码的例子来让您开始操作。 + +.. 列: + +``` +不要忘记添加这些导入语句。 虽然它是*不是*必需的,但使用 @wraws` 有助于保持您函数的某些元数据完整。[见文档](https://docs)。 ython.org/3/library/functools.html#functools.wrawals。另外,我们在这里使用 `isawaitable` 模式,允许通过常规或异步函数处理路由处理程序。 +``` + +.. 列: + +```` +```python +from inspect import isawaitable +from functools import wraps +``` +```` + +### 带参数: + +.. 列: + +```` +你常常想要一个装饰器,它将*总是需要参数。因此,当它实现时,你总是会调用它。 + +```python +@app.get("/") +@foobar(1, 2) +async def 处理器(请求: 请求): + return text("hi") +``` +```` + +.. 列: + +```` +```python +def foobar(g1, arg2: + def 装饰物(f): + @wraws(f) + async def 装饰函数(请求) *args, **kwargs): + + response = f(request, *args, **kwargs) + if isawaitable (response): + response = requires + + return response + + return decorated_function + + return Decorator +``` +```` + +### 没有参数 + +.. 列: + +```` +有时你想要一个不需要参数的装饰器。 在这种情况下,最好不要调用 + +``python +@app。 et("/") +@foobar +async def 处理器(请求: 请求): + return text("hi") +``` +```` + +.. 列: + +```` +```python +def foobar(function): + def decorator(f): + @wraws(f) + async def decorated_function_request. *args, **kwargs): + + response = f(request, *args, **kwargs) + if isawaitable (response): + response = 等待回应 + + return response + + return decorated_function + + return decorator(ffunc) +``` +```` + +### 使用或不使用参数 + +.. 列: + +```` +If you want a decorator with the ability to be called or not, you can follow this pattern. Using keyword only arguments is not necessary, but might make implementation simpler. + +```python +@app.get("/") +@foobar(arg1=1, arg2=2) +async def handler(request: Request): + return text("hi") +``` + +```python +@app.get("/") +@foobar +async def handler(request: Request): + return text("hi") +``` +```` + +.. 列: + +```` +```python +def foobar(maybe_func=None, *, arg1=None, arg2=None): + def decorator(f): + @wraps(f) + async def decorated_function(request, *args, **kwargs): + + response = f(request, *args, **kwargs) + if isawaitable(response): + response = await response + + return response + + return decorated_function + + return decorator(maybe_func) if maybe_func else decorator +``` +```` diff --git a/guide/content/zh/guide/best-practices/exceptions.md b/guide/content/zh/guide/best-practices/exceptions.md new file mode 100644 index 0000000000..d0c80c9c31 --- /dev/null +++ b/guide/content/zh/guide/best-practices/exceptions.md @@ -0,0 +1,636 @@ +# 例外 + +## 使用 Sanic 异常 + +有时,你只需要告诉Sanic停止执行处理程序并发送状态代码回复。 你可以为此举出一个 'SanicException' ,萨尼克将为你做其他事情。 + +您可以通过一个可选的 `status_code` 参数。 默认情况下,一个 SanicException 将返回一个内部服务器错误500。 + +```python +从 sanic.exception 导入SanicException + +@app.route("/youshallnotpass") +async def no_no(request): + raising SanicException("出了错", status_code=501) +``` + +Sanic提供了一些标准例外情况。 他们将自动在您的响应中提取适当的 HTTP 状态代码。 [请参阅API参考](https://sanic.readthedocs.io/enage/sanic/api_reference.html#module-sanic.exceptions) 了解更多详情。 + +.. 列: + +``` +The more common exceptions you _should_ implement yourself include: + +- `BadRequest` (400) +- `Unauthorized` (401) +- `Forbidden` (403) +- `NotFound` (404) +- `ServerError` (500) +``` + +.. 列: + +```` +```python +from sanic import exceptions + +@app.route("/login") +async def login(request): + user = await some_login_func(request) + if not user: + raise exceptions.NotFound( + f"Could not find user with username={request.json.username}" + ) +``` +```` + +## 异常属性 + +所有萨尼克例外情况都源于“SanicException”。 该类具有几个属性,可以帮助开发者始终如一地报告他们在应用程序中的异常情况。 + +- `message` +- `status_code` +- 安静\` +- `headers` +- `context` +- `extra` + +所有这些属性都可以在创建时传递到异常。 但前三个也可以用作我们所看到的类变量。 + +.. 列: + +``` +### `message` + +`message` 属性显然控制了将会像Python中任何其他异常一样显示的消息。 尤其有用的是,您可以在类定义中设置`message`属性,以便于所有应用程序的语言标准化 +``` + +.. 列: + +```` +```python +class CustomError(SanicException): + message = "出了错误" + +raising Custom错误 +# 或 +raw customError("用其他东西覆盖默认消息") +``` +```` + +.. 列: + +``` +### `status_code` + +这个属性用于设置异常时的响应代码。 这在创建习惯400系列异常时尤其有用,这些异常通常是为了对客户端提供的坏信息作出反应。 +``` + +.. 列: + +```` +```python +class TeapotError(SanicException): + status_code = 418 + message = “对不起,” 我不能酿造咖啡” + +提高Teapot错误 +# 或 +提高TeapotError(status_code=400) +``` +```` + +.. 列: + +``` +### `quiet` + +默认情况下,例外情况将由 Sanic 输出到 `error_logger` 。 有时这可能是不可取的,特别是如果你在异常处理程序中使用异常来触发事件(见以下章节) 。 异常.md#handling)。您可以使用 "quiot=True" 来抑制日志输出。 +``` + +.. 列: + +```` +```python +class SilentError(SanicException): + message = "发生了什么, 但未显示在日志中。 + 安静=True + +引起静音错误 +# 或 +引起无效使用("blah blah", 安静=True) +``` +```` + +.. 列: + +``` +有时候在调试时,你可能想要全局忽略`quiet=True`属性。 您可以使用 `NOISY_EXCEPIONS` + +强制从 v21.12中添加的 Sanic 注销所有异常,无论这个属性如何。 +``` + +.. 列: + +```` +```python +app.config.NOISY_EXCEPTIONS = True +``` +```` + +.. 列: + +``` +### `headers` + +使用 `SanicException` 作为创建响应的工具是超强大的。 这部分是因为你不仅可以控制 `status_code`,而且你也可以直接从异常中控制回复头部。 +``` + +.. 列: + +```` +```python +class MyException(SanicException): + headers = + "X-Foo": "bar" + } + +raising MyException +# 或 +raising InvalidUsage("blah blah", headers=leaders= + "X-Foo": "bar" +}) +``` +```` + +.. 列: + +``` +### `extran` + +查看[contextual explitions](./exceptions.md#contextual-exceptions) + +*添加于v21.12* +``` + +.. 列: + +```` +```python +raising SanicException(..., extrade={"name": "Adam"}) +``` +```` + +.. 列: + +``` +### `context` + +见 [contextual explositions](./exceptions.md#contextual-异常) + +*添加于v21.12* +``` + +.. 列: + +```` +```python +raising SanicException(..., context={"foo": "bar"}) +``` +```` + +## 处理 + +通过渲染错误页自动处理异常,所以在许多情况下您不需要自己处理。 然而,如果你想要更多地控制在提出异常时做什么,你可以自己实现一个处理程序。 + +Sanic为此提供了一个装饰器,它不仅适用于Sanic标准例外情况,而且**任何**你的应用程序可能丢弃的例外情况。 + +.. 列: + +``` +添加处理程序的最简单方法是使用 `@app.expition()` 并传递一个或多个异常。 +``` + +.. 列: + +```` +```python +from sanic.exceptions import NotFound + +@app.exception(NotFound, Some CustomException) +async def ignore_404s(request, exception): + return text("Yep, 我完全发现页面: {}".format (crequest.url)) +``` +```` + +.. 列: + +``` +你也可以通过捕捉"异常"来创建一个捕获器处理器。 +``` + +.. 列: + +```` +```python +@app.exception(Exception) +async def catch_any thing(request, exception): + ... +``` +```` + +.. 列: + +``` +您也可以使用 `app.error_handler.add()` 添加错误处理器。 +``` + +.. 列: + +```` +```python +async def server_error_handler(请求异常): + return text("Ops, server error", status=500) + +app.error_handler.add(Exception, server_error_handler) +``` +```` + +## 内置错误处理 + +有三种异常模式的神秘船只:HTML、JSON和文本。 你可以在下面的[回退处理程序](#回退处理程序)部分看到他们的例子。 + +.. 列: + +``` +You can control _per route_ which format to use with the `error_format` keyword argument. + +*Added in v21.9* +``` + +.. 列: + +```` +```python +@app.request("/", error_form="text") +async def handler(request): + ... +``` +```` + +## 自定义错误处理 + +在某些情况下,您可能想要将一些更多的错误处理功能添加到默认提供的功能。 在这种情况下,你可以将亚类Sanic的默认错误处理程序作为这样: + +```python +from sanic.handlers import ErrorHandler + +class CustomErrorHandler(ErrorHandler): + def default(self, request: Request, exception: Exception) -> HTTPResponse: + ''' handles errors that have no error handlers assigned ''' + # You custom error handling logic... + status_code = getattr(exception, "status_code", 500) + return json({ + "error": str(exception), + "foo": "bar" + }, status=status_code) + +app.error_handler = CustomErrorHandler() +``` + +## Fallback handler + +Sanic带有三个回退异常处理器: + +1. HTML +2. 文本 +3. JSON + +这些处理程序视应用程序是否处于[调试模式](/guide/deplement/development.md)而提供不同的详细程度。 + +默认情况下,Sanic将处于“自动”模式, 这意味着它将使用传入的请求和潜在的匹配处理程序来选择适当的响应格式。 例如,在浏览器中,它应该始终提供一个 HTML 错误页面。 当使用curl时,您可能会看到JSON或纯文本。 + +### HTML + +```python +app.config.FALLBACK_ERROR_FORMAT = "html" +``` + +.. 列: + +```` +```python +app.config.DEBUG = True +`` + +![Error](/assets/images/error-display-html-debug.png) +```` + +.. 列: + +```` +```python +app.config.DEBUG = False +`` + +![Error](/assets/images/error-disply-html-prod.png) +```` + +### 文本 + +```python +app.config.FALLBACK_ERROR_FORMAT = “文本” +``` + +.. 列: + +```` +```python +app.config.DEBUG = True +`` + +```sh +curl localhost:8000/exc -i +HTTP/1。 500 服务器内部错误 +内容长度:620 +连接:保持存活 +内容类型:text/plain; charset=utf-8 + +:警告:500-内部服务器错误 +================================= +那段时间当那个事情打破了那个其他事情吗? 发生了这种情况。 + +服务器错误:那个时候那件事情打破了那个东西吗?那就发生了。 处理路径/exc +追踪TestApp (最近一次通话时间): + + 服务器错误: 那个时候那个东西打破了那个其他东西? 发生了这种情况。 + 文件 /path/to/sanic/app y, line 979, in handle_request + response = requiring response + + file /path/to/server. y, line 16, in handler + do_something(cause_error=True) + + files/path/to/something。 y, line 9, in do_some + raising ServerError( +``` +```` + +.. 列: + +```` +```python +app.config.DEBUG = False +``` + +```sh +curl localhost:8000/exc -i +HTTP/1.1 500 Internal Server Error +content-length: 134 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +⚠️ 500 — Internal Server Error +============================== +That time when that thing broke that other thing? That happened. +``` +```` + +### JSON + +```python +app.config.FALLBACK_ERROR_FORMAT = "json" +``` + +.. 列: + +```` +```python +app.config.DEBUG = True +``` + +```sh +curl localhost:8000/exc -i +HTTP/1.1 500 Internal Server Error +content-length: 572 +connection: keep-alive +content-type: application/jso + +{ + "description": "Internal Server Error", + "status": 500, + "message": "That time when that thing broke that other thing? That happened.", + "path": "/exc", + "args": {}, + "exceptions": [ + { + "type": "ServerError", + "exception": "That time when that thing broke that other thing? That happened.", + "frames": [ + { + "file": "/path/to/sanic/app.py", + "line": 979, + "name": "handle_request", + "src": "response = await response" + }, + { + "file": "/path/to/server.py", + "line": 16, + "name": "handler", + "src": "do_something(cause_error=True)" + }, + { + "file": "/path/to/something.py", + "line": 9, + "name": "do_something", + "src": "raise ServerError(" + } + ] + } + ] +} +``` +```` + +.. 列: + +```` +```python +app.config.DEBUG = False +``` + +```sh +curl localhost:8000/exc -i +HTTP/1.1 500 Internal Server Error +content-length: 129 +connection: keep-alive +content-type: application/json + +{ + "description": "Internal Server Error", + "status": 500, + "message": "That time when that thing broke that other thing? That happened." +} + +``` +```` + +### 自动操作 + +Sanic还提供了一种猜测的选项,这种猜测可以使用后退选项。 + +```python +app.config.FALLBACK_ERROR_FORMAT = "自动" +``` + +## 上下文异常 + +默认异常消息简化了在整个应用程序中始终如一地提出异常的能力。 + +```python +class TeapotError(SanicException): + status_code = 418 + message = “对不起,我不能酿造咖啡” + +rappot错误 +``` + +但这缺少两件事: + +1. 动态和可预测的信息格式 +2. 添加附加上下文到错误消息的能力 (暂时更多) + +_添加于 v21.12_ + +使用 Sanic的一个异常,您有两个选项在运行时提供额外细节: + +```python +升起TeapotError(extrade={"foo": "bar"}, context={"foo": "bar"}) +``` + +两者之间有什么区别,何时决定使用? + +- `extran` - 对象本身将**永远不**发送到生产客户端。 它仅供内部使用。 它可以用于什么? + - 正在生成一个动态错误消息 (我们将在一分钟后看到) + - 为记录器提供运行时间详情 + - 调试信息(在开发模式中,它会提供) +- `context` - 此对象总是\*\*总是发送给生产客户。 一般用来提供关于所发生情况的更多细节。 它可以用于什么? + - 在 `BadRequest` 验证问题上提供替代值 + - 回答您的客户有帮助的详细信息来打开支持工单 + - 显示当前登录用户信息等状态信息 + +### 使用 "额外" 的动态和可预测消息 + +可使用 `extran`关键字参数来提起无声异常,为一个引起的异常实例提供额外信息。 + +```python +class TeapotError(SanicException): + status_code = 418 + + @property + def message(self): + return f"对不起{self.extr['name']}, 我不能让你咖啡” + +raising TeapotError(extranti={"name": "Adam"}) +``` + +新功能允许将 `extran`meta 传递给异常实例。 这可能与上面的例子一样对将动态数据传递到电文文本中特别有用。 这个“额外”信息对象 **将在 `PRODUCTION` 模式下被抑制** ,但将以`DevelopMENT` 模式显示。 + +.. 列: + +``` +**DEVELOPMENT** + +![image](~@assets/images/error-extra-debug.png) +``` + +.. 列: + +``` +**PRODUCTION** + +![image](~@assets/images/error-extra-prod.png) +``` + +### 附加“context”到错误消息 + +还可以用`context`的参数来提出无声异常,将预定的信息传递给用户关于所发生事件的信息。 这在创建微型服务或旨在以 JSON 格式传递错误消息的 API 时特别有用。 在这种情况下,我们想要围绕例外的某些上下文,而不仅仅是一个可解析的错误信息来返回客户端。 + +```python +raised TeapotError(context={"foot": "bar"}) +``` + +这是**我们想要**总是错误传递的信息(当它可用时)。 以下是它应该看起来的样子: + +.. 列: + +```` +**PRODUCTION** + +```json +{ + "description": "I'm a teapot", + "status": 418, + "message": "Sorry Adam, I cannot make you coffee", + "context": { + "foo": "bar" + } +} +``` +```` + +.. 列: + +```` +**DEVELOPMENT** + +```json +{ + "description": "I'm a teapot", + "status": 418, + "message": "Sorry Adam, I cannot make you coffee", + "context": { + "foo": "bar" + }, + "path": "/", + "args": {}, + "exceptions": [ + { + "type": "TeapotError", + "exception": "Sorry Adam, I cannot make you coffee", + "frames": [ + { + "file": "handle_request", + "line": 83, + "name": "handle_request", + "src": "" + }, + { + "file": "/tmp/p.py", + "line": 17, + "name": "handler", + "src": "raise TeapotError(" + } + ] + } + ] +} +``` +```` + +## 错误报告 + +Sanic 有一个 [signal](../advanced/signals.md#内置信号),允许您绑定到异常报告过程。 如果您想要向 Sentry 或 Rollbar等第三方服务发送异常信息,这是有用的。 这可以通过附加错误报告处理程序来方便地完成,如下所示: + +```python +@app.report_exception +async def catch_any_exception(app: Sanic, exception): +print("捕捉异常:", 异常) +``` + +.. 注: + +``` +此处理程序将被发送到一个后台任务中,**IS NOT** 将被用于操纵任何响应数据。 它仅用于伐木或报告目的。 并且不应影响您的应用程序返回错误响应客户端的能力。 +``` + +_添加于 v23.6_ diff --git a/guide/content/zh/guide/best-practices/logging.md b/guide/content/zh/guide/best-practices/logging.md new file mode 100644 index 0000000000..7bab5b6990 --- /dev/null +++ b/guide/content/zh/guide/best-practices/logging.md @@ -0,0 +1,218 @@ +# 日志记录 + +Sanic 允许您根据[Python log, 错误日志](https://docs.python.org/3/howto/logging.html)对请求进行不同类型的日志(访问日志, 错误日志)。 如果您想要创建一个新的配置,您应该在 Python 日志记录中获得一些基本知识。 + +但不要担心的是,沙尼克船在箱子里有一些合理的日志缺失。 在方框中,它使用一个 `AutoFormatter` 来格式化日志,取决于您是否处于调试模式。 我们将告诉你如何稍后强制这个操作。 + +## 快速开始 + +让我们首先看看本地开发中日志可能看起来像是什么。 为此,我们将使用 Sanic 提供的默认日志配置,并确保在开发模式中运行 Sanic。 + +.. 列: + +``` +一个使用默认设置的简单示例就是这样: +``` + +.. 列: + +```` +```python +from sanic import Sanic +from sanic.log import logger +from sanic.response import text + +app = Sanic('logging_example') + +@app.route('/') +async def test(request): + logger.info('Here is your log') + return text('Hello World!') +``` +```` + +.. 列: + +``` +因为我们正在特别试图查看发展记录,因此我们将确保在发展模式中运行萨尼克。 +``` + +.. 列: + +```` +```sh +sanic path.to.server:app --dev +``` +```` + +在服务器运行后,你应该看到像这样的日志。 + +![Sanic Logging Star](/assets/images/logging-debug-start.png) + +您可以向服务器发送请求,它将打印日志消息。 + +![Sanic 日志Access](/assets/images/logging-debug-access.png) + +需要注意的一些重要要点: + +- **production** 模式下的默认日志级别是 `INFO` 。 +- **debug** 模式下的默认日志级别是 `DEBUG` 。 +- 在 **debug** 模式中,日志消息将没有时间戳(访问日志除外)。 +- 如果终端支持它,Sanic将尝试对日志进行颜色。 如果你正在Docker中使用docker-compose,你可能需要在你的`docker-compose.yml`文件中设置`tty:true`来查看颜色。 + +## Sanic伐木者 + +在箱子里,有五艘伐木船的萨尼克船: + +| **Logger Name** | **使用大小写** | +| ------------------ | --------------- | +| `sanic.root` | 用于记录内部消息。 | +| `sanic.error` | 用于记录错误日志。 | +| `sanic.access` | 用于日志访问日志。 | +| `sanic.server` | 用于记录服务器日志。 | +| `sanic.websockets` | 用于记录 Web 套接字日志。 | + +.. 列: + +``` +如果你想要自己使用这些记录器,你可以从 `sanic.log`中导入它们。 +``` + +.. 列: + +```` +```python +来自sanic.log logger, error_logger, access_logger, server_logger, websockets_logger + +logger.info('这是一个root logger message') +``` +```` + +.. 警告:: + +``` +请随时使用您自己的Root日志和错误日志。 但您可能不想直接使用访问日志、服务器记录器或Websockets记录器。 这些是由 Sanic 内部使用的,并被配置为以特定方式登录。 如果你想要更改这些日志记录的方式,你应该更改日志的配置。 +``` + +## 默认日志配置 + +当您不提供自己的时候,默认日志配置为 Sanic 飞船。 此配置存储在 `sanic.log.LOGGING_CONFIG_DEFAULTS` 中。 + +```python +{ + 'version': 1, + 'disable_existing_loggers': False, + 'loggers': { + 'sanic.root': {'level': 'INFO', 'handlers': ['console']}, + 'sanic.error': { + 'level': 'INFO', + 'handlers': ['error_console'], + 'propagate': True, + 'qualname': 'sanic.error' + }, + 'sanic.access': { + 'level': 'INFO', + 'handlers': ['access_console'], + 'propagate': True, + 'qualname': 'sanic.access' + }, + 'sanic.server': { + 'level': 'INFO', + 'handlers': ['console'], + 'propagate': True, + 'qualname': 'sanic.server' + }, + 'sanic.websockets': { + 'level': 'INFO', + 'handlers': ['console'], + 'propagate': True, + 'qualname': 'sanic.websockets' + } + }, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'formatter': 'generic', + 'stream': sys.stdout + }, + 'error_console': { + 'class': 'logging.StreamHandler', + 'formatter': 'generic', + 'stream': sys.stderr + }, + 'access_console': { + 'class': 'logging.StreamHandler', + 'formatter': 'access', + 'stream': sys.stdout + } + }, + 'formatters': { + 'generic': {'class': 'sanic.logging.formatter.AutoFormatter'}, + 'access': {'class': 'sanic.logging.formatter.AutoAccessFormatter'} + } +} +``` + +## 正在更改 Sanic logger + +.. 列: + +``` +要使用您自己的日志配置,只需使用 `logging.config.dictConfig`,或通过 `log_config` 来初始化Sanic 应用程序。 +``` + +.. 列: + +```` +```python +app = Sanic('logging_example', log_config=LOGGING_CONFIG) + +if __name__ == "__main__": + app.run(access_log=False) +``` +```` + +.. 列: + +``` +但是,如果你不想完全控制日志记录, 那么如何改变格式化程序? 在这里,我们将导入默认的日志配置,并且只修改我们想要随时使用 "ProdFormatter" 的部件。 +``` + +.. 列: + +```` +```python +from sanic.log import LOGING_CONFIGG_DEFAULTS + +LOGING_CONFIG_DEFAULTS ['formations']['generic']['class'] = 'sanic.logging.formter.ProdFormatter' +LOGING_CONFIG_DEFAULTS['格式']['access']['class'] = 'sanic.logging.form.ter.ProdAccessFormatter' + +app = Sanic('logging_example', log_config=LOGING_CONFIG_FAULTS) +``` +```` + +.. tip:: FYI + +``` +在 Python 中登录是一个相对便宜的操作。 然而,如果你正在满足大量的请求,执行情况令人关切。 所有这些时间都增加了访问日志,费用都很高。 + +这是一个很好的机会,可以将 Sanic 放置在代理背后(如nginx),并在那里进行访问日志。 您将通过禁用 `access_log` 来看到整体性能的大幅提高。 + +为了最佳生产性能,建议运行 Sanic,禁用了 `debug` 和 `access_log` :`app.run(debug=False, access_log=False)` +``` + +## 访问记录器附加参数 + +Sanic 为访问日志提供额外参数。 + +| 日志上下文参数 | 参数值 | Datatype | +| --------- | ----------------------------------- | -------- | +| `host` | `request.ip` | `str` | +| `request` | `request.methods + " + request.url` | `str` | +| `status` | `response` | `int` | +| `byte` | `len(response.body)` | `int` | +| `持续时间` | | `float` | + +## 旧日志记录 + +Sanic 24.3引入了许多伐木变化。 主要的变动与伐木格式有关。 如果你喜欢旧版日志格式,你可以使用 `sanic.logging.formter.LegacyFormatter` 和 `sanic.logging.formter.LegacyAccessFormatter` 格式。 diff --git a/guide/content/zh/guide/best-practices/testing.md b/guide/content/zh/guide/best-practices/testing.md new file mode 100644 index 0000000000..40ceda6142 --- /dev/null +++ b/guide/content/zh/guide/best-practices/testing.md @@ -0,0 +1,3 @@ +# 测试 + +见 [sanic-testing](../../plugins/sanic-testing/getting-started.md) diff --git a/guide/content/zh/guide/deployment/caddy.md b/guide/content/zh/guide/deployment/caddy.md new file mode 100644 index 0000000000..86deefb227 --- /dev/null +++ b/guide/content/zh/guide/deployment/caddy.md @@ -0,0 +1,74 @@ +# Caddy 部署 + +## 一. 导言 + +Caddy 是一个最先进的网络服务器和代理服务器,最多支持 HTTP/3。 它的简单性在于它的最小化配置以及从我们加密中自动获取您的域名的 TLS 证书的内置能力。 在这个设置中,我们将配置Sanic应用程序在 127.0.0.0 本地服务。 :8001, 与 Caddy 在域example.com中扮演公共对口服务器的角色。 + +您可以在 Windows、Linux 和 Mac 上从您最喜欢的软件包菜单中安装Caddy。 包名为`caddy`。 + +## Proxied Sanic 应用 + +```python +from sanic import Sanic +from sanic.response import text + +app = Sanic("proxied_example") + +@app.get("/") +def index(request): + # This should display external (public) addresses: + return text( + f"{request.remote_addr} connected to {request.url_for('index')}\n" + f"Forwarded: {request.forwarded}\n" + ) +``` + +要运行此应用程序,请另存为 `proxied_example.py`,并使用符合条件的命令行接口如下: + +```bash +SANIC_PROXIES_COUNT=1 sanic proxied_example --porter 8001 +``` + +设置SANIC_PROXIES_COUNT 环境变量指示Sanic信任Caddy发送的 X-Forwarded-\* 头部。 允许它正确识别客户端的 IP 地址和其他信息。 + +## Caddy 是简单的 + +如果你没有其他网络服务器运行,你可以简单地运行 Caddy CLI (Linux需要`sudo`): + +```bash +caddy reverse-proxy --from example.com --to :8001 +``` + +这是一个完整的服务器,包括您的域名、 httpto https 重定向、 代理头、 流媒体和 WebSockets的证书。 您的 Sanic 应用程序现在应该可以在 HTTP 版本 1, 2 和 3 指定的域上使用。 记住要在您的防火墙上打开 UDP/443 以启用 H3 通信。 + +完成了所有任务吗? + +很快,您将需要多个服务器,或者更多的细节控制,这是配置文件的位置。 上述命令相当于此 `Caddyfile` ,作为您安装的一个良好起点: + +``` +example.com { + reverse_proxy localhost:8001 +} +``` + +一些Linux 发行版安装Caddy,它会读取`/etc/caddy/Caddyfile`的配置,它会为您正在运行的每个网站导入/etc/caddy/conf.d/\*。 如果没有,你需要手动运行 "caddy run" 作为系统服务,指向正确的配置文件。 或者,使用 caddy 运行 --resume` 来进行持续性配置更改的 Caddy API 模式。 请注意,任何Caddyfile 加载都将替换所有先前的配置,因此`caddy-api\`无法按照这种传统方式进行配置。 + +## 高级配置 + +有时,您可能需要在站点根目录混合静态文件和处理程序以获取更干净的 URL。 在 Sanic, 您使用 `app.static("/", "static", index="index.html")` 来实现这一点。 然而,为了提高性能,您可以卸载静态文件到 Cadd: + +``` +app.example.com { + # Look for static files first, proxy to Sanic if not found + route { + file_server { + root /srv/sanicexample/static + precompress br # brotli your large scripts and styles + pass_thru + } + reverse_proxy unix//tmp/sanic.socket # sanic --unix /tmp/sanic.socket + } +} +``` + +更多选项请参阅[Caddy documentation](https://caddyserver.com/docs/)。 diff --git a/guide/content/zh/guide/deployment/docker.md b/guide/content/zh/guide/deployment/docker.md new file mode 100644 index 0000000000..0e1a637ec6 --- /dev/null +++ b/guide/content/zh/guide/deployment/docker.md @@ -0,0 +1,199 @@ +# Docker 部署 + +## 一. 导言 + +长期以来,环境一直是部署的一个困难问题。 如果您的项目中存在相互冲突的配置,您必须花费大量时间解析它们。 幸运的是,虚拟化为我们提供了一个好的解决办法。 码头就是其中之一。 如果你不知道Docker,你可以访问 [Docker官方网站](https://www.docker.com/) 了解更多信息。 + +## 构建图像 + +让我们从一个简单的项目开始。 我们将以Sanic项目为例。 假设项目路径是 `/path/to/SanicDocker` 。 + +.. 列: + +``` +目录结构看起来像这样: +``` + +.. 列: + +```` +```text +# /path/to/SanicDocker +SanicDocker +├── requirements.txt +├── dockerfile +└── server.py +``` +```` + +.. 列: + +``` +`server.py`代码看起来像这样: +``` + +.. 列: + +```` +```python +app = Sanic("MySanicApp") + +@app.get('/') +async def hello(request): + return text("OK!") + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=8000) +``` +```` + +.. 注: + +``` +请注意主机不能为127.0.0.1 。在Docker容器中,127.0.0。 是容器的默认网络接口,只有容器可以与其他容器通信。 更多信息请访问[Docker network](https://docs.docker.com/engine/reference/commandline/network/) +``` + +代码已经准备好,让我们写入`Dockerfile`: + +```Dockerfile + +FROM sanicframework/sanic:3.8-最新 + +WORKDIR /sanic + +COPY . + +RUN pip install -r requirements.txt + +EXPOSE 8000 + +CMD ["python", "server.py"] +``` + +运行以下命令来构建图像: + +```shell +停靠构建-t my-sanic-image +``` + +## 启动容器 + +.. 列: + +``` +在图像生成后,我们可以启动容器使用 "my-sanic-image" : +``` + +.. 列: + +```` +```shell +docker run --name mysanic -p 8000:8000 -d my-sanic-image +``` +```` + +.. 列: + +``` +现在我们可以访问 `http://localhost:8000` 来查看结果: +``` + +.. 列: + +```` +```text +OK! +``` +```` + +## 使用 docker-compose + +如果您的项目包含多项服务,您可以使用 [docker-compose](https://docs.docker.com/compose/) 来管理它们。 + +例如,我们将部署`my-sanic-image`和`nginx`,通过 nginx 访问智能服务器来实现。 + +.. 列: + +``` +首先,我们需要准备 nginx 配置文件,创建一个名为 `mysanic.conf` 的文件: +``` + +.. 列: + +```` +```nginx +server { + listen 80; + listen [::]:80; + location / { + proxy_pass http://mysanic:8000/; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection upgrade; + proxy_set_header Accept-Encoding gzip; + } +} +``` +```` + +.. 列: + +``` +然后,我们需要准备`docker-compose.yml`文件。 +``` + +.. 列: + +```` +```yaml +版本: "3" + +services: + mysanic: + image: my-sanic-image + ports: + - "8000:8000" + 重启 + + mynginx: + image: nginx:1.13. -alpine + 端口: + - "80:80" + 依赖于: + - mysanic + 卷: + - . 神秘。 onf/etc/nginx/conf.d/mysanic.conf + 重启:总是 + +networks: + default: + driver: bridge +``` +```` + +.. 列: + +``` +然后,我们可以开始: +``` + +.. 列: + +```` +```shell +docker-compose up -d +``` +```` + +.. 列: + +``` +现在,我们可以访问 `http://localhost:80` 查看结果: +``` + +.. 列: + +```` +```text +OK! +``` +```` diff --git a/guide/content/zh/guide/deployment/kubernetes.md b/guide/content/zh/guide/deployment/kubernetes.md new file mode 100644 index 0000000000..8d6340de1c --- /dev/null +++ b/guide/content/zh/guide/deployment/kubernetes.md @@ -0,0 +1 @@ +# Kubernetes diff --git a/guide/content/zh/guide/deployment/nginx.md b/guide/content/zh/guide/deployment/nginx.md new file mode 100644 index 0000000000..c7c2f8928c --- /dev/null +++ b/guide/content/zh/guide/deployment/nginx.md @@ -0,0 +1,172 @@ +# Nginx 部署 + +## 一. 导言 + +虽然Sanic可以直接在互联网上运行,但在互联网前使用代理 +服务器可能是有用的,例如Nginx。 这对于运行同一IP上的 +多个虚拟主机特别有用, 服务于NodeJS或除了 +单个Sanic应用之外的其他服务,并且它还允许有效地服务于静态文件。 +TLS和HTTP-2也很容易在这种代理上执行。 + +我们正在设置 Sanic 应用程序仅在本地服务为127.0.0。 :8001, +Nginx 安装负责为域示例.com上的公共互联网 +提供服务。 静态文件将由Nginx提供最大 +性能。 + +## Proxied Sanic 应用 + +```python +from sanic import Sanic +from sanic.response import text + +app = Sanic("proxied_example") + +@app.get("/") +def index(request): + # This should display external (public) addresses: + return text( + f"{request.remote_addr} connected to {request.url_for('index')}\n" + f"Forwarded: {request.forwarded}\n" + ) +``` + +由于这是一个系统服务,将您的代码保存到 +`/srv/sanicservice/proxied_example.py`。 + +为测试,使用你保存文件的文件夹中的 `sanic` CLI 在终端中运行你的应用程序。 + +```bash +SANIC_FORWARDED_SECRET=_hostname sanic proxied_example --端口 8001 +``` + +We provide Sanic config `FORWARDED_SECRET` to identify which proxy it gets +the remote addresses from. 请注意本地主机名前面的`_` 。 +这提供了基本的保护,免受那些假冒头头并传真到 +他们的IP地址等用户的伤害。 + +## SSL 证书 + +安装 Certbot 并获得您所有域的节拍。 这将会在80端口上增加它自己的web服务器,以验证您控制给定的域名。 + +```bash +certbot -d example.com -d www.example.com +``` + +## Nginx 配置 + +需要很多配置才能快速透明的代理, 但 +大部分情况下不需要修改这些内容,所以与我休戚与共。 + +.. 提示:备注 + +``` +HTTP keep-live需要分隔上游部分,而不是像大多数教程中那样只是在"proxy_pass" +之后添加IP。 我们还启用了串流, +WebSockets 和 Nginx 服务于静态文件。 +``` + +以下配置在 `nginx 的 `http` 部分内。 开启或如果您的 +系统使用多个配置文件,`/etc/nginx/sites-available/default` 或 +您自己的文件(肯定要将它们与`sites-enabled\`链接): + +```nginx +# Files managed by Certbot +ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; +ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; + +# Sanic service +upstream example.com { + keepalive 100; + server 127.0.0.1:8001; + #server unix:/tmp//sanic.sock; +} + +server { + server_name example.com; + listen 443 ssl http2 default_server; + listen [::]:443 ssl http2 default_server; + # Serve static files if found, otherwise proxy to Sanic + location / { + root /srv/sanicexample/static; + try_files $uri @sanic; + } + location @sanic { + proxy_pass http://$server_name; + # Allow fast streaming HTTP/1.1 pipes (keep-alive, unbuffered) + proxy_http_version 1.1; + proxy_request_buffering off; + proxy_buffering off; + proxy_set_header forwarded 'by=\"_$hostname\";$for_addr;proto=$scheme;host=\"$http_host\"'; + # Allow websockets and keep-alive (avoid connection: close) + proxy_set_header connection "upgrade"; + proxy_set_header upgrade $http_upgrade; + } +} + +# Redirect WWW to no-WWW +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name ~^www\.(.*)$; + return 308 $scheme://$1$request_uri; +} + +# Redirect all HTTP to HTTPS with no-WWW +server { + listen 80 default_server; + listen [::]:80 default_server; + server_name ~^(?:www\.)?(.*)$; + return 308 https://$1$request_uri; +} + +# Forwarded for= client IP address formatting +map $remote_addr $for_addr { + ~^[0-9.]+$ "for=$remote_addr"; # IPv4 client address + ~^[0-9A-Fa-f:.]+$ "for=\"[$remote_addr]\""; # IPv6 bracketed and quoted + default "for=unknown"; # Unix socket +} +``` + +启动或重启 Nginx 以使更改生效。 例如: + +```bash +systemctl 重启 nginx +``` + +您应该能够在 `https://example.com` 上连接您的应用程序。 任何 404 +错误将通过 Sanic's 错误页面处理, 并且当静态 +文件存在于给定路径时,它将由Nginx提供服务。 + +## 作为服务运行 + +本部分是基于 `systemd` 的 Linux 发行版。 创建一个单位文件 +`/etc/systemd/system/sanicexample.service` + +``` +[Unit] +描述=Sanic 示例 + +[Service] +动态用户=是 +WorkingDirectory=/srv/sanicservice +Environment=SANIC_PROXY_SECRET=_hostname +ExecStart=sanic proxied_example --porter 8001 --fast +Restart=始终 + +[Install] +WantedBy=multi-user.target。 +``` + +然后重新加载服务文件,启动您的服务并在启动时启用它: + +```bash +systemctl daemon-reload +systemctl start sanicexample +systemctl 启用sanicexample +``` + +.. 提示:备注 + +``` +为简洁起见,我们跳过了设置一个单独的用户帐户和 Python 虚拟环境或将您的应用程序安装为 Python 模块。 其他地方也有很好的关于这些题目的教程,很容易应用于萨尼克。 动态用户设置创建了一个强大的沙盒,基本上意味着您的应用程序不能将其数据存储在文件中, 所以,如果您需要,您可以考虑设置 `User=sanicexplle` 。 +``` diff --git a/guide/content/zh/guide/getting-started.md b/guide/content/zh/guide/getting-started.md new file mode 100644 index 0000000000..25d7f02d17 --- /dev/null +++ b/guide/content/zh/guide/getting-started.md @@ -0,0 +1,105 @@ +# 快速上手 + +在我们开始之前,请确保您正在运行 Python 3.9 或更多。 目前,Sanic 正在使用 Python 版本 3.9 - 3.13。 + +## 安装(Install) + +```sh +pip install sanic +``` + +## Helloworld案例 + +.. column:: + +``` +如果你之前有使用过基于装饰器的web应用,那么Sanic的语法可能对你来说会很亲切。 + + +.. 注意:: + + 如果您来自 Flask 或其他框架,有一些重要的事情需要指出。 请记住,Sanic 的目标是性能(performance)、灵活性(flexibility) 和易用性(ease of use)。 这些指导原则对 API 及其工作方式产生了切实的影响。 +``` + +.. column:: + +```` +```python +from sanic import Sanic +from sanic.response import text + +app = Sanic("MyHelloWorldApp") + +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` +```` + +### 重要提示 + +- 每一个请求响应函数都可以使用同步方式(`def hello_world`)和异步方式(`async def hello_world`)进行声明。 除非您有一个明确的需求和完善的使用方法,否则的话,请尽量使用 async 来声明响应函数。 +- `request` 对象始终是响应函数的第一个参数。 其他框架在需要导入的上下文变量中进行传递。 在 `async`的世界里,如果使用隐式传递,那么它将无法完美的运行,更何况还要兼顾简洁且高效的表现形式。 +- 您 必须 使用 Response 或继承自 Response 的类作为响应类型。 在许多其他框架中,它们允许您使用诸如 `return "Hello World"` 或者 `return {"foo":"bar"}` 的方式来进行返回, 但是为了执行这类隐式调用,需要在响应流程中的某个位置花费大量的时间来确定您到底想要表达什么意思。 因此,在Sanic的返回语句中,需要明确的指定返回的数据类型(eg `return json({"foo":"bar"})` 或 `return text("Hello world")`) + +### 运行(Running) + +.. column:: + +``` +让我们把上面的文件保存为 `server.py`。然后启动它。 +``` + +.. column:: + +```` +```sh +sanic server +``` +```` + +.. note:: + +``` +这**另一个**重要的区别。其它框架包含在开发服务器中,并明确表示它仅用于开发用途。 Sanic的情况正好相反。 + +**Sanic内置的web服务器是生产就绪的。** +``` + +## Sanic 拓展 + +Sanic 有意打造一个干净且不带偏见的功能列表。 该项目不想要求您以某种方式构建应用程序,并试图避免指定特定的开发模式。 有许多由社区构建和维护的第三方插件,用于添加不符合核心库要求的附加功能。 + +但是,为了**帮助API开发者**,Sanic 组织维护了一个名为[Sanic Extensions](../plugins/sanic-ext/getting-started.md)的官方插件,提供各种各样的常见解决方案,其中包括: + +- 使用 **Redoc** 和/或 **Swagger** 编写 OpenAPI 文档 +- **CORS** 保护 +- 将其他对象通过 **Dependency injection** (依赖注入)到路由处理程序中 +- 请求查询参数和正文输入的**验证器**(validation) +- **自动创建** HEAD、OPTIONS 和 TRACE 入口(auto create) +- - 预先定义好的**序列化函数**(eg `json` `text`)、作用于不同的路由入口(serializers) + +最好的安装方式就是在安装 Sanic 的同时一并安装 Sanic 拓展,当然,您也可以独立安装: + +.. column:: + +```` +```sh +pip install sanic[ext] +``` +```` + +.. column:: + +```` +```sh +pip install sanic sanic-ext +``` +```` + +从 v21.12 开始,如果处于同一环境中,Sanic 将自动安装 Sanic 扩展。 您可以通过以下的两个属性来进行访问拓展功能: + +- `app.extend()` - 用于配置 Sanic 扩展 +- `app.ext` - 注入到应用程序的扩展实例 + +请参阅[插件文档](../plugins/sanic-ext/getting-started.md) 了解如何使用和使用插件的更多信息 diff --git a/guide/content/zh/guide/how-to/README.md b/guide/content/zh/guide/how-to/README.md new file mode 100644 index 0000000000..cde79793eb --- /dev/null +++ b/guide/content/zh/guide/how-to/README.md @@ -0,0 +1 @@ +# 如何... diff --git a/guide/content/zh/guide/how-to/authentication.md b/guide/content/zh/guide/how-to/authentication.md new file mode 100644 index 0000000000..f718528ebe --- /dev/null +++ b/guide/content/zh/guide/how-to/authentication.md @@ -0,0 +1,116 @@ +# 认证 + +> 如何控制认证和授权? + +这是一个非常复杂的 _extremy_ ,要把它变成几个代码片段。 但是,这应该为你们提供一个解决这一问题的方法的想法。 此示例使用 [JWTs](https://jwt.io/),但概念应该同样适用于会话或其他方案。 + +## `server.py` + +```python +从 sanic import Sanic, text + +from auth import protected +from login import login + +app = Sanic("AuthApp") +app.config.SECRET = “KEEP_IT_SECRET_KEEP_IT_SAFE” +app. lueprint(login) + +@app.get("/secret") +@protected +async def secret(request): + return text("to go fast, you must be fas.") +``` + +## `login.py` + +```python +从 Sanic 导入蓝图导入jt +文本 + +login = Blueprint("login", url_prefix="/login") + +@login。 ost("/") +async def do_login(请求): + token = jwt.encode({}, request.app.config.SECRET) + return text(token) +``` + +## `auth.py` + +```python +from functools import wraps + +import jwt +from sanic import text + +def check_token(request): + if not request.token: + return False + + try: + jwt.decode( + request.token, request.app.config.SECRET, algorithms=["HS256"] + ) + except jwt.exceptions.InvalidTokenError: + return False + else: + return True + +def protected(wrapped): + def decorator(f): + @wraps(f) + async def decorated_function(request, *args, **kwargs): + is_authenticated = check_token(request) + + if is_authenticated: + response = await f(request, *args, **kwargs) + return response + else: + return text("You are unauthorized.", 401) + + return decorated_function + + return decorator(wrapped) +``` + +这种装饰模式取自[装饰品页面](/en/guide/best practices/decorators.md)。 + +--- + +```bash +$ curl localhost:99999/secret -i +HTTP/1.1 401 未经授权的 +content-length: 21 +connection: keep-alive +content-type: text/pla; charset=utf-8 + +您未被授权。 + + +$curl localhost:9999/log-X POST +eyJ0eXAiOiJKV1QiLCJhbGciOiJIUZI1NiJ9。 30.rjxS7ztIGt5tpirWS8BGLUqjQFca4QOetHcZTi061DE + + +$ curl localhost:99999/secret -i -H "Authorization: Bearer eyJ0eXAioJKV1QiLCJhbGciOiJIUZI1NiJ9.e30.rjxS7ztIGt5tpirWS8BGLUqjQFca4QOetZTi061DE" +HTTP/1。 200 OK +内容长度:29 +连接:保持存活 +内容类型:text/pla;charset=utf-8 + +要快速,您必须快速。 + + +$ curl localhost:9999/secret -i -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUZI1NiJ9. 30.BAD" +HTTP/1。 401 未经授权的 +内容长度:21 +连接:保持存活 +内容类型:text/plain;charset=utf-8 + +您未经授权。 +``` + +此外,结算社区的一些资源: + +- 非常棒的 Sanic - [Authorization](https://github.com/mekicha/awesome-sanic/blob/master/README.md#认证) & [Session](https://github.com/mekicha/awesome-sanic/blob/master/README.md#session) +- [EuroPython 2020 - overcoming access control in web API](https://www.youtube.com/watch?v=Uqgoj43ky6A) diff --git a/guide/content/zh/guide/how-to/autodiscovery.md b/guide/content/zh/guide/how-to/autodiscovery.md new file mode 100644 index 0000000000..238555db14 --- /dev/null +++ b/guide/content/zh/guide/how-to/autodiscovery.md @@ -0,0 +1,197 @@ +--- +title: 自动发现 +--- + +# 自动发现蓝图、中程和侦听器 + +> 我如何自动发现我正在使用的组件来构建我的应用程序? + +某人在构建应用程序时面临的第一个问题是_如何_构建项目。 Sanic大量使用装饰器注册路由处理器、中间人和听众。 在创建蓝图后,它们需要安装到应用程序上。 + +可能的解决方案是一个 **每件事** 导入并应用到 Sanic 实例的单一文件。 另一个正在环绕着Sanic实例作为一个全球变量。 这两种解决办法都有其缺点。 + +另一种办法是自动发现。 您将您的应用程序指向模块 (已导入,或字符串),并让它连接一切。 + +## `server.py` + +```python +from sanic import Sanic +from sanic.response import empty + +import blueprints +from utility import autodiscover + +app = Sanic("auto", register=True) +autodiscover( + app, + blueprints, + "parent.child", + "listeners.something", + recursive=True, +) + +app.route("/")(lambda _: empty()) +``` + +```bash +[2021-03-02 21:37:02 +0200] [880451] [INFO] Goin' Fast @ http://127.0.0.1:9999 +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something @ nested +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something @ level1 +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something @ level3 +[2021-03-02 21:37:02 +0200] [880451] [DEBUG] something inside __init__.py +[2021-03-02 21:37:02 +0200] [880451] [INFO] Starting worker [880451] +``` + +## `utility.py` + +```python + +from glob import glob +from importlib import import_module, util +from inspect import getmembers +from pathlib import Path +from types import ModuleType +from typing import Union + +from sanic.blueprints import Blueprint + +def autodiscover( + app, *module_names: Union[str, ModuleType], recursive: bool = False +): + mod = app.__module__ + blueprints = set() + _imported = set() + + def _find_bps(module): + nonlocal blueprints + + for _, member in getmembers(module): + if isinstance(member, Blueprint): + blueprints.add(member) + + for module in module_names: + if isinstance(module, str): + module = import_module(module, mod) + _imported.add(module.__file__) + _find_bps(module) + + if recursive: + base = Path(module.__file__).parent + for path in glob(f"{base}/**/*.py", recursive=True): + if path not in _imported: + name = "module" + if "__init__" in path: + *_, name, __ = path.split("/") + spec = util.spec_from_file_location(name, path) + specmod = util.module_from_spec(spec) + _imported.add(path) + spec.loader.exec_module(specmod) + _find_bps(specmod) + + for bp in blueprints: + app.blueprint(bp) +``` + +## `蓝图/level1.py` + +```python +from sanic import Blueprint +from sanic.log import logger + +level1 = Blueprint("level1") + +@level1.after_server_start +def print_something(app, loop): + logger.debug("something @ level1") +``` + +## “蓝图1e/2/level3.py” + +```python +from sanic import Blueprint +from sanic.log import logger + +level3 = Blueprint("level3") + +@level3.after_server_start +def print_something(app, loop): + logger.debug("something @ level3") +``` + +## `监听器/something.py` + +```python +from sanic import Sanic +from sanic.log import logger + +app = Sanic.get_app("auto") + +@app.after_server_start +def print_something(app, loop): + logger.debug("something") +``` + +## `parent/child/__init__.py` + +```python +from sanic import Blueprint +from sanic.log import logger + +bp = Blueprint("__init__") + +@bp.after_server_start +def print_something(app, loop): + logger.debug("something inside __init__.py") +``` + +## `parent/child/nested.py` + +```python +从sanic.log 导入蓝图 +从sanic.log 导入记录器 + +嵌套= Blueprint("嵌套") + +@nested.after _server_start +def print_something(app, loop): + logger.debug("some @ 嵌套") +``` + +--- + +```text +here is the dir tree +generate with 'find . -type d -name "__pycache__" -exec rm -rf {} +; tree' + +. # run 'sanic sever -d' here +├── blueprints +│ ├── __init__.py # you need add this file, just empty +│ ├── level1.py +│ └── one +│ └── two +│ └── level3.py +├── listeners +│ └── something.py +├── parent +│ └── child +│ ├── __init__.py +│ └── nested.py +├── server.py +└── utility.py +``` + +```sh +源 ./.venv/bin/激活 # 激活安装在 +sanic sever -d # 中的 python venv 在包含服务器的目录中运行它。 +``` + +```text +you will see "something ***" like this: + +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something inside __init__.py +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something @ level3 +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something @ level1 +[2023-07-12 11:23:36 +0000] [113704] [DEBUG] something @ nested +``` diff --git a/guide/content/zh/guide/how-to/cors.md b/guide/content/zh/guide/how-to/cors.md new file mode 100644 index 0000000000..b4a87058d1 --- /dev/null +++ b/guide/content/zh/guide/how-to/cors.md @@ -0,0 +1,138 @@ +--- +title: CORS +--- + +# 跨来源资源共享 + +> 如何配置我的 CORS 应用程序? + +.. 注: + +``` +🏆 最好的解决方案是使用 [Sanic Extensions](../../plugins/sanic-ext/http/cors.md). + +然而,如果你想要建立自己的版本,你可以使用这个有限的例子作为起点。 +``` + +### `server.py` + +```python +from sanic import Sanic, text + +from cors import add_cors_headers +from options import setup_options + +app = Sanic("app") + +@app.route("/", methods=["GET", "POST"]) +async def do_stuff(request): + return text("...") + +# Add OPTIONS handlers to any route that is missing it +app.register_listener(setup_options, "before_server_start") + +# Fill in CORS headers +app.register_middleware(add_cors_headers, "response") +``` + +## `cors.py` + +```python +from typing import Iterable + +def _add_cors_headers(response, methods: Iterable[str]) -> None: + allow_methods = list(set(methods)) + if "OPTIONS" not in allow_methods: + allow_methods.append("OPTIONS") + headers = { + "Access-Control-Allow-Methods": ",".join(allow_methods), + "Access-Control-Allow-Origin": "mydomain.com", + "Access-Control-Allow-Credentials": "true", + "Access-Control-Allow-Headers": ( + "origin, content-type, accept, " + "authorization, x-xsrf-token, x-request-id" + ), + } + response.headers.extend(headers) + +def add_cors_headers(request, response): + if request.method != "OPTIONS": + methods = [method for method in request.route.methods] + _add_cors_headers(response, methods) +``` + +## `options.py` + +```python +from collections import defaultdict +from typing import Dict, FrozenSet + +from sanic import Sanic, response +from sanic.router import Route + +from cors import _add_cors_headers + +def _compile_routes_needing_options( + routes: Dict[str, Route] +) -> Dict[str, FrozenSet]: + needs_options = defaultdict(list) + # This is 21.12 and later. You will need to change this for older versions. + for route in routes.values(): + if "OPTIONS" not in route.methods: + needs_options[route.uri].extend(route.methods) + + return { + uri: frozenset(methods) for uri, methods in dict(needs_options).items() + } + +def _options_wrapper(handler, methods): + def wrapped_handler(request, *args, **kwargs): + nonlocal methods + return handler(request, methods) + + return wrapped_handler + +async def options_handler(request, methods) -> response.HTTPResponse: + resp = response.empty() + _add_cors_headers(resp, methods) + return resp + +def setup_options(app: Sanic, _): + app.router.reset() + needs_options = _compile_routes_needing_options(app.router.routes_all) + for uri, methods in needs_options.items(): + app.add_route( + _options_wrapper(options_handler, methods), + uri, + methods=["OPTIONS"], + ) + app.router.finalize() +``` + +--- + +``` +$ curl localhost:9999/ -i +HTTP/1.1 200 OK +Access-Control-Allow-Methods: OPTIONS,POST,GET +Access-Control-Allow-Origin: mydomain.com +Access-Control-Allow-Credentials: true +Access-Control-Allow-Headers: origin, content-type, accept, authorization, x-xsrf-token, x-request-id +content-length: 3 +connection: keep-alive +content-type: text/plain; charset=utf-8 + +... + +$ curl localhost:9999/ -i -X OPTIONS +HTTP/1.1 204 No Content +Access-Control-Allow-Methods: GET,POST,OPTIONS +Access-Control-Allow-Origin: mydomain.com +Access-Control-Allow-Credentials: true +Access-Control-Allow-Headers: origin, content-type, accept, authorization, x-xsrf-token, x-request-id +connection: keep-alive +``` + +此外,结算社区的一些资源: + +- [很棒的Sanic](https://github.com/mekicha/awesome-sanic/blob/master/README.md#frontend) diff --git a/guide/content/zh/guide/how-to/csrf.md b/guide/content/zh/guide/how-to/csrf.md new file mode 100644 index 0000000000..7f982ff12f --- /dev/null +++ b/guide/content/zh/guide/how-to/csrf.md @@ -0,0 +1 @@ +csrf diff --git a/guide/content/zh/guide/how-to/db.md b/guide/content/zh/guide/how-to/db.md new file mode 100644 index 0000000000..9ccd8b17ff --- /dev/null +++ b/guide/content/zh/guide/how-to/db.md @@ -0,0 +1 @@ +连接到数据源 diff --git a/guide/content/zh/guide/how-to/decorators.md b/guide/content/zh/guide/how-to/decorators.md new file mode 100644 index 0000000000..b101957cb9 --- /dev/null +++ b/guide/content/zh/guide/how-to/decorators.md @@ -0,0 +1 @@ +装饰 diff --git a/guide/content/zh/guide/how-to/mounting.md b/guide/content/zh/guide/how-to/mounting.md new file mode 100644 index 0000000000..765eb14720 --- /dev/null +++ b/guide/content/zh/guide/how-to/mounting.md @@ -0,0 +1,53 @@ +# 应用程序挂载 + +> 如何在root上方挂载我的应用程序? + +```python +# server.py +from sanic import Sanic, text + +app = Sanic("app") +app.config.SERVER_NAME = "example.com/api" + +@app.route("/foo") +def handler(request): + url = app.url_for("handler", _external=True) + return text(f"URL: {url}") +``` + +```yaml +# docker-compose.yml +版本: "3. +服务: + app: + 图像:nginx:alpine + ports: + - 80:80 + 卷: + - 类型:binding + 消息来源: conf + 目标:/etc/nginx/conf.d/default.conf +``` + +```nginx +# conf +server { + listen 80; + + # Computed data service + location /api/ { + proxy_pass http://:9999/; + proxy_set_header Host example.com; + } +} +``` + +```bash +$ docker-compose up -d +$ sanic server.app --port=9999 --host=0.0.0.0 +``` + +```bash +$ curl localhost/api/foo +URL: http://example.com/api/foo +``` diff --git a/guide/content/zh/guide/how-to/orm.md b/guide/content/zh/guide/how-to/orm.md new file mode 100644 index 0000000000..dcbe64c01a --- /dev/null +++ b/guide/content/zh/guide/how-to/orm.md @@ -0,0 +1,467 @@ +# ORM + +> 如何使用 SQLAlchemy 和 Sanic ? + +所有ORM工具都可以使用 Sanic 但非异步的 ORM 工具对Sanic 性能产生影响。 +有一些或者软件包支持 + +目前有许多ORM支持Python的“async`/`await\`”关键字。 一些可能的选项包括: + +- [Mayim](https://ahopkins.github.io/mayim/) +- [SQLAlchemy 1.4](https://docs.sqlalchemy.org/en/14/changelog/changelog_14.html) +- [tortoise-orm](https://github.com/tortoise/tortoise-orm) + +您的 Sanic 应用程序的集成相当简单: + +## 马伊姆 + +Mayim船上有[Sanic Extensions的扩展](https://ahopkins.github.io/mayim/guide/extensions.html#sanic),这使得开始萨尼克变得非常简单。 当然可以在没有扩展的情况下运行Mayim,但是建议它处理所有的[生命周期事件](https://sanic)。 ev/en/guide/basics/listeners.html)和[依赖注入](https://sanic.dev/en/plugins/sanic-ext/injection.html)。 + +.. 列: + +``` +### Dependencies + +First, we need to install the required dependencies. See [Mayim docs](https://ahopkins.github.io/mayim/guide/install.html#postgres) for the installation needed for your DB driver. +``` + +.. 列: + +```` +```shell +pip install sanic-ext +pip install mayim[postgres] +``` +```` + +.. 列: + +``` +### Define ORM Model + +Mayim allows you to use whatever you want for models. Whether it is [dataclasses](https://docs.python.org/3/library/dataclasses.html), [pydantic](https://pydantic-docs.helpmanual.io/), [attrs](https://www.attrs.org/en/stable/), or even just plain `dict` objects. Since it works very nicely [out of the box with Pydantic](https://ahopkins.github.io/mayim/guide/pydantic.html), that is what we will use here. +``` + +.. 列: + +```` +```python +# ./models。 y +from pydanticimporting BaseModel + +class City(BaseModel): + id: int + name: str + region: str + population: int + +class Country(BaseModel): + code: str + name: str + continent: str + region: str + capital: City +``` +```` + +.. 列: + +``` +### 定义SQL + +如果您不熟悉,也许与其他ORM不同,因为它是单向的,SQL-先的。 这意味着您可以在内部或者在一个单独的`.sql`文件中定义自己的查询,这就是我们将在这里做的。 +``` + +.. 列: + +```` +```sql +-- ./queries/select_all_countries.sql +SELECT country.code, + country.name, + country.continent, + country.region, + ( + SELECT row_to_json(q) + FROM ( + SELECT city.id, + city.name, + city.district, + city.population + ) q + ) capital +FROM country + JOIN city ON country.capital = city.id +ORDER BY country.name ASC +LIMIT $limit OFFSET $offset; +``` +```` + +.. 列: + +``` +### Create Sanic App and Async Engine + +We need to create the app instance and attach the `SanicMayimExtension` with any executors. +``` + +.. 列: + +```` +```python +# ./server.py +from sanic import Sanic, Request, json +from sanic_ext import Exten +from mayim.expertor import Postgresultor +from mayim. xtenes import SanicMayimextension +from model import Country + +class CountryExecutor(Postgresultor): + async def select_all_countries( + self, 限制:int = 4 偏移:int = 0 + ) -> 列表[Country]: + . + +app = Sanic("测试") +Extend。 egister( + SanicMayimExtension( + expertors=[CountryExecutor], + dsn="postgres://。 .", + ( +) +``` +```` + +.. 列: + +``` +### 注册航线 + +因为我们正在使用 Mayim's 扩展 Sanic, 我们在路由处理器中自动注入了 "CountryExecutor" 。 它使人们能够轻松地获得附加类型说明的发展经验。 +``` + +.. 列: + +```` +```python +@app.get("/") +async def handler(request: Request, executor: CountryExecutor): + countries = required executor.select_all_countries() + return json({"countries": [country.dict() for country in co +``` +```` + +.. 列: + +``` +### 发送请求 +``` + +.. 列: + +```` +```sh +curl 'http://127.0.0.1:8000' +{"countries":[{"code":"AFG","name":"Afghanistan","continent":"Asia","region":"Southern and Central Asia","capital":{"id":1,"name":"Kabul","district":"Kabol","population":1780000}},{"code":"ALB","name":"Albania","continent":"Europe","region":"Southern Europe","capital":{"id":34,"name":"Tirana","district":"Tirana","population":270000}},{"code":"DZA","name":"Algeria","continent":"Africa","region":"Northern Africa","capital":{"id":35,"name":"Alger","district":"Alger","population":2168000}},{"code":"ASM","name":"American Samoa","continent":"Oceania","region":"Polynesia","capital":{"id":54,"name":"Fagatogo","district":"Tutuila","population":2323}}]} +``` +```` + +## SQLAlchemy + +因为[SQLAlchemy 1.4](https://docs.sqlalchemy.org/en/14/changelog/changelog_14.html)添加了本机支持`asyncio`, Sanic最终可以与 SQLAlchemy 进行很好的工作。 请注意,SQLAlchemy项目仍然认为此功能是_测试版_。 + +.. 列: + +``` +### Dependencies + +First, we need to install the required dependencies. In the past, the dependencies installed were `sqlalchemy` and `pymysql`, but now `sqlalchemy` and `aiomysql` are needed. +``` + +.. 列: + +```` +```shell +pip install -U sqlalchemy +pip install -U aiomysql +``` +```` + +.. 列: + +``` +### 定义ORM Model + +ORM model 创建保持不变。 +``` + +.. 列: + +```` +```python +# ./models.py +from sqlalchemy import INTEGER, Column, ForeignKey, String +from sqlalchemy.orm import declarative_base, relationship + +Base = declarative_base() + +class BaseModel(Base): + __abstract__ = True + id = Column(INTEGER(), primary_key=True) + +class Person(BaseModel): + __tablename__ = "person" + name = Column(String()) + cars = relationship("Car") + + def to_dict(self): + return {"name": self.name, "cars": [{"brand": car.brand} for car in self.cars]} + +class Car(BaseModel): + __tablename__ = "car" + + brand = Column(String()) + user_id = Column(ForeignKey("person.id")) + user = relationship("Person", back_populates="cars") +``` +```` + +.. 列: + +``` +### Create Sanic App and Async Engine + +Here we use mysql as the database, and you can also choose PostgreSQL/SQLite. Pay attention to changing the driver from `aiomysql` to `asyncpg`/`aiosqlite`. +``` + +.. 列: + +```` +```python +# ./server.py +from sanic import Sanic +from sqlalchemy.ext.asyncio import create_async_engine + +app = Sanic("my_app") + +bind = create_async_engine("mysql+aiomysql://root:root@localhost/test", echo=True) +``` +```` + +.. 列: + +``` +### Register Middlewares + +The request middleware creates an usable `AsyncSession` object and set it to `request.ctx` and `_base_model_session_ctx`. + +Thread-safe variable `_base_model_session_ctx` helps you to use the session object instead of fetching it from `request.ctx`. +``` + +.. 列: + +```` +```python +# ./server.py +from contextVar Import ContextVar + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy rm importing sessionmaker + +_sessionmaker = sessionmaker(bind, AsyncSession, expire_on_commit=False) + +_base_model_session_ctx = ContextVar("session") + +@app.middleware("request") +async def inject_session(request): + request. tx.session = _sessionmaker() + request.ctx.session_ctx_token = _base_model_session_ctx.set(request.ctx.session) + +@app.middleware("response") +async def close_session(request, response): + if havasttrac(request). tx, "session_ctx_token"): + _base_model_session_ctx.reset(crequest.ctx.session_ctx_token) + 等待request.ctx.session.close() +``` +```` + +.. 列: + +``` +### Register Routes + +According to sqlalchemy official docs, `session.query` will be legacy in 2.0, and the 2.0 way to query an ORM object is using `select`. +``` + +.. 列: + +```` +```python +# ./server.py +from sqlalchemy import selected +from sqlalchemy.orm import selectinload +from sanic. esponse importer json + +from models importing Car, Person + +@app.post("/user") +async def create_user(request): + session = request。 tx.session + 异步与会话。 egin(): + car = Car(brand="Tesla") + person = Person(name="foo", cars=[car]() + 会话。 dd_all([person]) + return json(person). o_dict()) + +@app.get("/user/") +async def get_user(request, pk): + session = request。 tx.session + async with session.begin(): + stmt = select(Person).where(Person.id == pk). ptions(selectinload(Person.cars)) + results = 等待会话。 xecute(stmt) + persons = result。 calar() + + 如果不是个人,则为: + return json({}) + + return json(person). o_dict()) +``` +```` + +.. 列: + +``` +### 发送请求 +``` + +.. 列: + +```` +```sh +curl --location --request POST 'http://127.0.0.1:8000/user' +{"name":"foo","cars":[[{"brand":"Tesla"}]} +`` + +```sh +curl --location --request GET 'http://127.0.0.1:8000/user/1' +{"name":"foo","cars":[{"brand":"Tesla"}]} +``` +```` + +## Tortoise-ORM + +.. 列: + +``` +### 依赖项 + +tortoise-orm 的依赖关系非常简单,您只需要安装 tortoise-orm 。 +``` + +.. 列: + +```` +```shell +pip安装-U tortoise-orm +``` +```` + +.. 列: + +``` +### 定义ORM Model + +如果你熟悉Django,你应该发现这一部分非常熟悉。 +``` + +.. 列: + +```` +```python +# ./models.py +from tortoise import Model, fields + +class Users(Model): + id = fields.IntField(pk=True) + name = fields.CharField(50) + + def __str__(self): + return f"I am {self.name}" +``` +```` + +.. 列: + +``` +### 创建 Sanic App 和 Async 引擎 + +Tortoise-orm 提供了一套注册界面。 这对用户是方便的,您可以使用它来轻松地创建数据库连接。 +``` + +.. 列: + +```` +```python +# ./main.py + +from models import Users +from tortoise.contrib.sanic import register_tortoise + +app = Sanic(__name__) + +register_tortoise( + app, db_url="mysql://root:root@localhost/test", modules={"models": ["models"]}, generate_schemas=True +) + +``` +```` + +.. 列: + +``` +### 注册路由 +``` + +.. 列: + +```` +```python +# ./main.py + +from models import Users +from sanic import Sanic, response + +@app.route("/user") +async def list_all(request): + users = await Users.all() + return response.json({"users": [str(user) for user in users]}) + +@app.route("/user/") +async def get_user(request, pk): + user = await Users.query(pk=pk) + return response.json({"user": str(user)}) + +if __name__ == "__main__": + app.run(port=5000) +``` +```` + +.. 列: + +``` +### 发送请求 +``` + +.. 列: + +```` +```sh +curl --location --request POST 'http://127.0.0.1:8000/user' +{"users":["I are foo", "I are bar"]} +```\ + +```sh +curl --location --request GET 'http://127。 0.0.1:8000/user/1' +{"user": "我是foot"} +``` +```` diff --git a/guide/content/zh/guide/how-to/serialization.md b/guide/content/zh/guide/how-to/serialization.md new file mode 100644 index 0000000000..b7c18c08cb --- /dev/null +++ b/guide/content/zh/guide/how-to/serialization.md @@ -0,0 +1 @@ +# 序列化 diff --git a/guide/content/zh/guide/how-to/server-sent-events.md b/guide/content/zh/guide/how-to/server-sent-events.md new file mode 100644 index 0000000000..7daf86745e --- /dev/null +++ b/guide/content/zh/guide/how-to/server-sent-events.md @@ -0,0 +1 @@ +sse diff --git a/guide/content/zh/guide/how-to/static-redirects.md b/guide/content/zh/guide/how-to/static-redirects.md new file mode 100644 index 0000000000..4bc6dd54e7 --- /dev/null +++ b/guide/content/zh/guide/how-to/static-redirects.md @@ -0,0 +1,106 @@ +# “静态”重定向 + +> 如何配置静态重定向? + +## `app.py` + +```python +### 设置 ### +import typing +import sanic, sanic.response + +# 创建 Sanic app +app = sanic.Sanic(__name__) + +# 该目录代表你的 "static" 目录重定向。 +# 举个例子,这些值可以从配置文件中提取。 +REDIRECTS = { + '/':'/hello_world', # 重定向 '/' 到 '/hello_world' + '/hello_world':'/hello_world.html' #重定向 '/hello_world' 到 'hello_world.html' +} + +# 这个函数会返回另一个(已配置值)的函数,而且不受已传参数的限制。 +def get_static_function(value:typing.Any) -> typing.Callable[..., typing.Any]: + return lambda *_, **__: value + +### 路由 ### +# 遍历重定向 +for src, dest in REDIRECTS.items(): + # 创建重定向响应对象 + response:sanic.HTTPResponse = sanic.response.redirect(dest) + + # 创建处理函数,通常,仅将 sanic.Request 对象传递给该函数。 该对象将被忽略。 + handler = get_static_function(response) + + # 路由src到处理函数 + app.route(src)(handler) + +# 随便路由一些文件和client资源 +app.static('/files/', 'files') +app.static('/', 'client') + +### 运行 ### +if __name__ == '__main__': + app.run( + '127.0.0.1', + 10000 + ) +``` + +## `client/hello_world.html` + +```html + + + + + + + Hello World + + + +
+ Hello world! +
+ + +``` + +## `client/hello_world.css` + +```css +#hello_world { + width: 1000px; + margin-left: auto; + margin-right: auto; + margin-top: 100px; + + padding: 100px; + color: aqua; + text-align: center; + font-size: 100px; + font-family: monospace; + + background-color: rgba(0, 0, 0, 0.75); + + border-radius: 10px; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.75); +} + +body { + background-image: url("/files/grottoes.jpg"); + background-repeat: no-repeat; + background-size: cover; +} +``` + +## `files/grottoes.jpg` + +![lake](/assets/images/grottoes.jpg) + +--- + +此外,结算社区的一些资源: + +- [静态路由示例](https://github.com/Perzan/sanic-static-routing-example) diff --git a/guide/content/zh/guide/how-to/table-of-contents.md b/guide/content/zh/guide/how-to/table-of-contents.md new file mode 100644 index 0000000000..c2034c1d4c --- /dev/null +++ b/guide/content/zh/guide/how-to/table-of-contents.md @@ -0,0 +1,17 @@ +--- +title: 目录 +--- + +# 目录 + +我们汇编了充分运作的例子,以回答共同的问题和用户案例。 在大多数情况下,这些例子尽可能少一些,但应该是完整和可运作的解决办法。 + +| 页 | 如何做... | +| :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- | +| [应用程序挂载](./mounting.md) | ... 将我的应用程序挂载到root上方的某个路径? | +| [Authentication](./认证.md) | ... 控制认证和授权? | +| [Autodiscovery](./autocovery.md) | ... 自动发现我用来构建应用程序的组件? | +| [CORS](./cors.md) | ... 配置我的 CORS 应用程序? | +| [ORM](./orm) | ... 使用 Sanic 的 ORM 吗? | +| ["静态" 重定向](./static-redirects.md) | ... 配置静态重定向 | +| [TLS/SSL/HTTPS](./tls.md) | ... 通过 HTTPS 运行 Sanic 吗?
... 将 HTTP 重定向到 HTTPS? | diff --git a/guide/content/zh/guide/how-to/task-queue.md b/guide/content/zh/guide/how-to/task-queue.md new file mode 100644 index 0000000000..2e59ab8dd9 --- /dev/null +++ b/guide/content/zh/guide/how-to/task-queue.md @@ -0,0 +1 @@ +任务队列 diff --git a/guide/content/zh/guide/how-to/tls.md b/guide/content/zh/guide/how-to/tls.md new file mode 100644 index 0000000000..04211e1121 --- /dev/null +++ b/guide/content/zh/guide/how-to/tls.md @@ -0,0 +1,199 @@ +--- +title: TLS/SL/HTTPS +--- + +# TLS/SL/HTTPS + +> 如何通过 HTTPS 运行 Sanic ? + +如果您还没有TLS证书,[请查看此页面结尾处](./tls.md#get-certificates-for your domain-names)。 + +## 单域和单个证书 + +.. 列: + +``` +允许 Sanic 自动加载您的证书文件,这些文件需要在给定的文件夹中名为 `fullchain.pem` 和 `privkey.pem` : +``` + +.. 列: + +```` +```sh +sudanic myserver:app -H :: -p 443 \ + --tls /etc/letsenccrypt/live/example.com/ +``` +```python +app.run(":", 443, ssl="/etc/letsenccrypt/live/example.com/") +``` +```` + +.. 列: + +``` +或者,您可以作为字典单独传递证书和密钥文件名:此外还有 + +如果密钥被加密,可以添加`密码`,除密码外,所有字段都会传递到`请求'。 onn_info.cert`. +``` + +.. 列: + +```` +```python +ssl = Power + "cert": "/path/to/fullchain.pem", + "key": "/path/to/privkey". em", + "密码": "用于加密私钥文件", # Optional +} +app.run(host="0.0.0.0", port=8443, ssl=ssl) +``` +```` + +.. 列: + +``` +或者,[`ssl.SSLContext`](https://docs.python.org/3/library/sl.html)可以传递,如果你需要完全控制诸如允许加密算法等细节。 默认情况下,Sanic只允许使用安全算法,这可能限制使用非常旧的设备。 +``` + +.. 列: + +```` +```python +import ssl + +context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) +context.load_cert_chain("certs/fullchain.pem", "certs/privkey.pem") + +app.run(host="0.0.0.0", port=8443, ssl=context) +``` +```` + +## 具有单独证书的多个域 + +.. 列: + +``` +可以提供多个证书列表,在这种情况下,Sanic选择与用户连接的主机名匹配的一个。 这发生在TLS 握手中,以致Sanic尚未向客户端发送任何数据包。 + +如果客户端没有发送SNI (服务商名称指示), 列表上的第一个证书将被使用,即使在客户端浏览器上它可能会由于名称不匹配而导致TLS错误。 为了防止这种后退并导致客户端在没有已知主机名的情况下立即断开连接,请在列表中添加"无"作为第一项。 `--tls-strict-host` 是对应的 CLI 选项。 +``` + +.. 列: + +```` +```python +ssl = ["certs/example.com/", "certs/bigcorp.test/"] +app.run(host="0.0.0 ", port=8443, ssl=ssl) +``` +```sh +sanic myserver:app + --tls certs/示例。 om/ + --tls certs/bigcorp.test/ + --tls-strict-host +``` +```` + +.. tip:: + +``` +如果您不想透露您的证书,您也可以在单一证书前使用 `None` 。 真实主机名或站点内容到连接到IP地址而不是正确的DNS名称。 +``` + +.. 列: + +``` +字典可以在列表中使用。 这也允许指定证书匹配哪个域,尽管证书本身上的名称不能从这里控制。 如果未指明姓名,则使用证书本身的名称。 + +只允许连接到主域**example.com** 并且只允许连接到**bigcorp.test**的子域: +``` + +.. 列: + +```` +```python +ssl = [ + None, # 如果名称不匹配,则无回退! + Power + "cert": "certs/example"。 om/fullchain.pem, + "key": "certs/example"。 om/privkey.pem, + "names": ["example.com", "*.bigcorp. est"], + } +] +app.run(host="0.0.0.0", port=8443, ssl=ssl) +``` +```` + +## 通过 `request.conn_info` 字段访问处理程序中的 TLS 信息 + +- `.ssl` - 连接安全 (布尔) +- `.cert` - 当前活动证书的 cert 信息和 dict 字段 +- `.server_name` - 客户端发送的SNI (str, 可能为空) + +请注意,所有的`conn_info`字段都是在连接上,在连接中可能会有许多请求。 如果代理人在您的服务器面前使用,那么这些请求可能来自不同的用户。 + +## 将 HTTP 重定向到 HTTPS,证书请求仍然在 HTTP 上 + +除了运行HTTPS的正常服务器之外,运行另一个服务器来重定向,`http_redir.py`: + +```python +from sanic import Sanic, exceptions, response + +app = Sanic("http_redir") + +# Serve ACME/certbot files without HTTPS, for certificate renewals +app.static("/.well-known", "/var/www/.well-known", resource_type="dir") + +@app.exception(exceptions.NotFound, exceptions.MethodNotSupported) +def redirect_everything_else(request, exception): + server, path = request.server_name, request.path + if server and path.startswith("/"): + return response.redirect(f"https://{server}{path}", status=308) + return response.text("Bad Request. Please use HTTPS!", status=400) +``` + +最好是将此设置为一个系统单元,从您的 HTTPS 服务器中分离出来。 您可能需要在最初请求您的证书时运行 HTTP,同时您还不能运行 HTTPS 服务器。 IPv4和IPv6开始: + +``` +sanic http_redir:app -H 0.0.0.0 -p 80 +sanic http_redir:app -H :: -p 80 +``` + +或者,可以从主应用程序运行HTTP重定向应用程序: + +```python +# 应用 == 您的主要应用程序 +# 重定向 == 您的 http_redir应用程序 +@app。 efor_server_start +async def start(app, _): + app.ctx.redirect = 等待重定向. reate_server( + port=80, return_asyncio_server=True + ) + app.add_task(runner(redirect, app. + +@app.before_server_stop +async def stop(app, _): + 等待应用。 tx.redirect.close() + +async def runner(app, app_server): + app.state。 s_runing = True + 尝试: + app。 ignalize() + app.finalize() + app tate.is_started = True + 等待app_server。 erve_forumver() + 最后: + app。 tate.is_runction = False + app.state.is_start = True +``` + +## 获取域名证书 + +您可以从[我们的加密](https://letsencrypt.org/)获得免费证书。 通过您的软件包管理器安装 [certbot](https://certbot.eff.org/) 并请求证书: + +```sh +sudo certbot certonly --key-type ecdsa --opeded-chain "ISRG Root X1" -d example.com -d www.example.com +``` + +多个域名可以通过进一步的 "-d" 参数添加,所有的域名都存储到单个证书中,并保存到 "/etc/letsencrypt/live/example。 按照你在这里列出的首个域\*\* 的 om/\`。 + +关键类型和首选链选项是获取最小大小证书文件所必需的, 使您的服务器尽可能以 _快速_ 运行非常重要。 该链仍将包含一个RSA证书,直到让我们加密他们的新的EC 链在所有主要浏览器中得到信任。 diff --git a/guide/content/zh/guide/how-to/validation.md b/guide/content/zh/guide/how-to/validation.md new file mode 100644 index 0000000000..f2d01dad7e --- /dev/null +++ b/guide/content/zh/guide/how-to/validation.md @@ -0,0 +1 @@ +验证 diff --git a/guide/content/zh/guide/how-to/websocket-feed.md b/guide/content/zh/guide/how-to/websocket-feed.md new file mode 100644 index 0000000000..8995fc0e59 --- /dev/null +++ b/guide/content/zh/guide/how-to/websocket-feed.md @@ -0,0 +1 @@ +websocket 源 diff --git a/guide/content/zh/guide/introduction.md b/guide/content/zh/guide/introduction.md new file mode 100644 index 0000000000..cdff86ed17 --- /dev/null +++ b/guide/content/zh/guide/introduction.md @@ -0,0 +1,71 @@ +# 介绍(Introduction) + +Sanic 是 Python 3.9+ 网页服务器和网页框架,这是写得快的。 它允许使用 Python3.5 中添加的 async/await 异步语法,这使得您的代码有效地避免阻塞从而达到提升响应速度的目的。 + +.. attrs:: +:class: introduction-table + +``` +| | | +|--|--| +| Build | [![Tests](https://github.com/sanic-org/sanic/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/sanic-org/sanic/actions/workflows/tests.yml) | +| Docs | [![User Guide](https://img.shields.io/badge/user%20guide-sanic-ff0068)](https://sanicframework.org/) [![Documentation](https://readthedocs.org/projects/sanic/badge/?version=latest)](http://sanic.readthedocs.io/en/latest/?badge=latest) | +| Package | [![PyPI](https://img.shields.io/pypi/v/sanic.svg)](https://pypi.python.org/pypi/sanic/) [![PyPI version](https://img.shields.io/pypi/pyversions/sanic.svg)](https://pypi.python.org/pypi/sanic/) [![Wheel](https://img.shields.io/pypi/wheel/sanic.svg)](https://pypi.python.org/pypi/sanic) [![Supported implementations](https://img.shields.io/pypi/implementation/sanic.svg)](https://pypi.python.org/pypi/sanic) [![Code style black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) | +| Support | [![Forums](https://img.shields.io/badge/forums-community-ff0068.svg)](https://community.sanicframework.org/) [![Discord](https://img.shields.io/discord/812221182594121728?logo=discord)](https://discord.gg/FARQzAEMAA) [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/mekicha/awesome-sanic) | +| Stats | [![Monthly Downloads](https://img.shields.io/pypi/dm/sanic.svg)](https://pepy.tech/project/sanic) [![Weekly Downloads](https://img.shields.io/pypi/dw/sanic.svg)](https://pepy.tech/project/sanic) [![Conda downloads](https://img.shields.io/conda/dn/conda-forge/sanic.svg)](https://anaconda.org/conda-forge/sanic) | +``` + +## 是什么?(What is it?) + +首先,在入坑之前, 您应该知道 Sanic 框架和其他的框架相比是与众不同的。 + +Sanic 不仅仅是一个**框架(framework)**,更是一个**服务器(web server)** 我们将在部署(deployment )章节更多地谈论这个问题。 + +但是,请记住,Sanic 具备开箱即用的功能,它可以用于编写,部署和扩展生产级 Web 应用程序。 🚀 + +## 目标 + +> 提供一种简单且快速,集创建和启动于一体的方法,来实现一个易于修改和拓展的 HTTP 服务器 + +## 功能(Features) + +.. column:: + +``` +### 核心(Core) + +- 内置高性能的web server +- 生产就绪 +- 高度可扩展性 +- 遵循 ASGI 规范 +- 简单直观的API设计 +- 由社区强力驱动 +``` + +.. column:: + +``` +### 扩展(Sanic Extensions) [[learn more](../plugins/sanic-ext/getting-started.md)] + +- **CORS** 保护 +- 使用 **Jinja** 进行模板渲染 +- 将其他对象通过 **Dependency injection** (依赖注入)到路由处理程序中 +- 使用 **Redoc** 和/或 **Swagger** 编写 OpenAPI 文档 +- 预先定义好的**序列化函数**(eg `json` `text`)、作用于不同的路由入口(serializers) +- 请求查询参数和正文输入的**验证器**(validation) +- **自动创建** HEAD、OPTIONS 和 TRACE 入口(auto create) +``` + +## 赞助商 + +[点击这里](https://opencollective.com/sanic-org)来了解更多关于帮助融资Sanic的信息。 + +## 加入社区 + +讨论的主要渠道是[社区论坛](https://community.sanicframework.org/)。 还有一个 [Discord 聊天室](https://discord.gg/RARQzAEMAA) 进行现场讨论和聊天。 + +Stackoverflow \`[sanic]' 标签由项目维护者[积极关注](https://stackoverflow.com/questions/tagged/sanic)。 + +## 贡献 + +我们非常欢迎新的贡献者加入。 我们[有很清晰的issue标记对于那些想要快速上手的人](https://github.com/sanic-org/sanic/issues?q=is%3Aopen+is%3Aissue+label%3Abeginner),欢迎[ 在社区上提问/回答/讨论](https://community.sanicframework.org/)。 请查看我们的[贡献指南](https://github.com/sanic-org/sanic/blob/master/CONTRIBUTING.rst)。 diff --git a/guide/content/zh/guide/running/app-loader.md b/guide/content/zh/guide/running/app-loader.md new file mode 100644 index 0000000000..3872b97fcb --- /dev/null +++ b/guide/content/zh/guide/running/app-loader.md @@ -0,0 +1,98 @@ +--- +title: 动态应用 +--- + +# 动态应用(Dynamic Applications) + +我们优化了通过命令行来运行 Sanic 应用的过程。 如果您还不了解的话,最好在读这篇文章前去看一下 [运行 Sanic](./running.md#sanic-server) 以获取一些详细的信息。 + +.. column:: + +``` +既可以指定全局变量来启动应用: +``` + +.. column:: + +```` +```sh +sanic path.to.server:app +``` +```python +# server.py +app = Sanic("TestApp") + +@app.get("/") +async def handler(request: Request): + return json({"foo": "bar"}) +``` +```` + +.. column:: + +``` +也可以指定某创建 Sanic 对象的工厂函数来启动: +``` + +.. column:: + +```` +```sh +sanic path.to.server:create_app --factory +``` +```python +# server.py +def create_app(): + app = Sanic("TestApp") + + @app.get("/") + async def handler(request: Request): + return json({"foo": "bar"}) + + return app +``` +```` + +**但有些时候,这还不够... 🤔** + +在 [v22.9](../release-notes/v22.9.md) 的版本中,Sanic 添加了负责在各个[工作进程](./manager.md#how-sanic-server-starts-process)中创建一个应用程序的 `AppLoader` 。 如果你需要更加「动态」的运行体验,那可以用一下它。 + +.. column:: + +``` +我们可以将一个能够返回 `Sanic` 实例的工厂函数给 `AppLoader` 。`AppLoader` 可以和更底层的运行应用的 API 一起使用。 +``` + +.. column:: + +```` +```python +import sys +from functools import partial + +from sanic import Request, Sanic, json +from sanic.worker.loader import AppLoader + +def attach_endpoints(app: Sanic): + @app.get("/") + async def handler(request: Request): + return json({"app_name": request.app.name}) + +def create_app(app_name: str) -> Sanic: + app = Sanic(app_name) + attach_endpoints(app) + return app + +if __name__ == "__main__": + app_name = sys.argv[-1] + loader = AppLoader(factory=partial(create_app, app_name)) + app = loader.load() + app.prepare(port=9999, dev=True) + Sanic.serve(primary=app, app_loader=loader) +``` +```sh +python path/to/server.py MyTestAppName +``` +```` + +在这个例子中,`AppLoader` 与 `factory` 传入的可用于在不同进程中创建同一应用的拷贝的函数一起被创建。 然后您需要显式地使用 `Sanic.serve` ,这样您的 `AppLoader` 就不会被自动生成的应用替换。 diff --git a/guide/content/zh/guide/running/configuration.md b/guide/content/zh/guide/running/configuration.md new file mode 100644 index 0000000000..a0e226a848 --- /dev/null +++ b/guide/content/zh/guide/running/configuration.md @@ -0,0 +1,328 @@ +# 配置 + +## 基础知识 + +.. 列: + +``` +Sanic持有应用程序对象配置属性中的配置。 配置对象只是一个可以使用点符号或像字典修改的对象。 +``` + +.. 列: + +```` +```python +app = Sanic("myapp") +app.config.DB_NAME = "appdb" +app.config["DB_USER"] = "appuser" +``` +```` + +.. 列: + +``` +您也可以使用 `update()` 方法,例如在常规字典上。 +``` + +.. 列: + +```` +```python +db_settings= { + 'DB_HOST': 'localhost', + 'DB_NAME': 'appdb', + 'DB_USER': 'appuser' +} +app.config.update(db_settings) +``` +```` + +.. 注: + +``` +萨尼克的标准练习是在 **大写字母** 中命名您的配置值。 确实,如果你开始混合大写和小写地名,你可能会遇到奇怪的行为。 +``` + +## 正在加载 + +### 环境变量 + +.. 列: + +``` +任何通过 `SANIC_` 前缀定义的环境变量都将被应用到 Sanic 配置。 例如,设置 `SANIC_REQUEST_TIMEOUT` 将被自动加载,并输入`REQUEST_TIMEOUT` 配置变量。 +``` + +.. 列: + +```` +```bash +$ export SANIC_REQUEST_TIMEOUT=10 +``` +```python +>> > print(app.config.REQUEST_TIMEOUT) +10 +``` +```` + +.. 列: + +``` +您可以更改Sanic 在启动时预期的前缀。 +``` + +.. 列: + +```` +```bash +$ export MYAPP_REQUEST_TIMEOUT=10 +``` +```python +>>> app = Sanic(__name__, env_prefix='MYAPP_') +>>> print(app.config.REQUEST_TIMEOUT) +10 +``` +```` + +.. 列: + +``` +您也可以完全禁用环境变量。 +``` + +.. 列: + +```` +```python +app = Sanic(__name__, load_env=False) +``` +```` + +### 正在使用 Sanic.update_config + +`Sanic` 实例有一个用于加载配置的所有_多功能方法:`app.update_config`。 你可以为它提供到文件的路径、字典、类或任何其他类型的对象。 + +#### 从文件 + +.. 列: + +``` +让我们说你有看起来像这样的 `my_config.py` 文件。 +``` + +.. 列: + +```` +```python +# my_config.py +A = 1 +B = 2 +``` +```` + +.. 列: + +``` +您可以通过将其路径传递给`app.update_config`来加载它作为配置值。 +``` + +.. 列: + +```` +```python +>>> app.update_config("/path/to/my_config.py") +>>> print(app.config.A) +1 +``` +```` + +.. 列: + +``` +此路径也接受基础风格环境变量。 +``` + +.. 列: + +```` +```bash +$ export my_path="/path/to" +``` +```python +app.update_config("${my_path}/my_config.py") +``` +```` + +.. 注: + +``` +Just remember that you have to provide environment variables in the format `${environment_variable}` and that `$environment_variable` is not expanded (is treated as "plain" text). +``` + +#### 从一个词典 + +.. 列: + +``` +`app.update_config` 方法也适用于普通字典。 +``` + +.. 列: + +```` +```python +app.update_config({"A": 1, "B": 2}) +``` +```` + +#### 来自类或对象 + +.. 列: + +``` +您可以定义自己的配置类,然后传递到 `app.update_config` +``` + +.. 列: + +```` +```python +class MyConfig: + A = 1 + B = 2 + +app.update_config(MyConfig) +``` +```` + +.. 列: + +``` +它甚至可以实例化。 +``` + +.. 列: + +```` +```python +app.update_config(MyConfig()) +``` +```` + +### 铸造类型 + +当从环境变量加载时,Sanic会试图将这些值投射到预期的 Python 类型。 这尤其适用于: + +- `int` +- `float` +- `bool` + +关于“布尔值”,允许以下_case insensitive_value: + +- **`True`**: `y`, `yes`, `yep`, `yup`, `t`, `true`, `on`, `enable`, `enabled`, `1` +- **`False`**: `n`, `no`, `false`, `off`, `disabled`, `0` + +如果一个值不能投递,它将默认投递到一个 `str`。 + +.. 列: + +``` +此外,Sanic可以配置为使用其他类型转换器投射额外类型。 这应该是返回值或提升`ValueError`的任何可调用。 + +*添加于v21.12* +``` + +.. 列: + +```` +```python +app = Sanic(..., config=Config(converters=[UUID])) +``` +```` + +## 内置值 + +| **变量** | **默认** | **描述** | +| -------------------------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------- | +| ACCESS_LOG | 真的 | 禁用或启用访问日志 | +| AUTO_EXTEND | 真的 | 控制 [Sanic Extensions](../../plugins/sanic-ext/getting-started.md) 是否会在现有虚拟环境中加载 | +| AUTO_RELOAD | 真的 | 控制文件更改时应用程序是否会自动重新加载 | +| EVENT_AUTOREGISTER | 真的 | 当`True`使用`app.event()`方法在不存在的信号上将自动创建它,而不会引起异常 | +| FALLBACK_ERROR_FORMAT | .html | 未捕获和处理异常时的错误响应格式 | +| FORWARDED_FOR_HEADER | X-转发-输入 | 包含客户端和代理IP的 "X-Forwarded-for" HTTP 头名称 | +| FORWARDED_SECRET | 无 | 用于安全识别特定代理服务器(见下文) | +| GRACEFUL_SHUTDOWN_TIMEOUT | 15.0 | 强制关闭非空闲连接等待多长时间 (秒) | +| INSPECTO | 错误 | 是否启用检查器 | +| INSPECTOR_HOST | 本地主机 | 检查专员的主机 | +| INSPECTOR_PORT | 6457 | 检查员的端口 | +| INSPECTOR_TLS_TITLE | - | 检查员的TLS密钥 | +| INSPECTOR_TLS_CERT | - | 检查员的TLS证书 | +| INSPECTOR_API_KEY | - | 检查员的 API 密钥 | +| KEEP_ALIVEUT | 120 | 保持TCP连接打开多长时间(秒) | +| KEEP_ALIVE | 真的 | False 时禁用保持生命值 | +| MOTD | 真的 | 启动时是否显示 MOTD (当天消息) | +| MOTD_DISPLAY | {} | Key/value 对应显示额外的任意数据 | +| NOISY_EXCEPTIONS | 错误 | 强制所有“安静”异常被记录 | +| PROXIES _COUNT | 无 | 在应用程序前面的代理服务器数量 (例如,nginx;见下方) | +| VIP_HEADER | 无 | 包含真实客户端IP的 "X-Real-IP" HTTP 头名称 | +| 注册 | 真的 | 是否启用应用注册 | +| REQUEST_BUFFER_SIZE | 65536 | 请求暂停前请求缓冲区大小,默认是 64 Kib | +| REQUEST_HEADER | X-请求-ID | 包含请求/关联ID的 "X-Request-ID" HTTP 头名称 | +| REQUEST_MAX_SIZE | 100000000 | 请求的大小可能是 (bytes), 默认是 100 megabytes | +| REQUEST_MAX_HEADER_SIZE | 8192 | 请求头可能有多大(字节),默认值为8192字节 | +| REQUEST_TIMEOUT | 60 | 到达请求可能需要多长时间(秒) | +| 重置_TIMEOUT | 60 | 处理过程可能需要多长时间(秒) | +| USE_UVLOOP | 真的 | 是否覆盖循环策略使用 `uvloop` 。 只支持 `app.run` 。 | +| WEBSOCKET_MAX_SIZE | 2^20 | 收到消息的最大大小(字节) | +| WEBSOCKET_INTERVAL | 20 | 每个ping_interval 秒都会发送一个Ping 帧。 | +| WEBSOCKET_PING_TIMEOUT | 20 | 当Pong在ping_timeout秒后未收到时,连接将被关闭 | + +.. tip:: FYI + +``` +- The `USE_UVLOOP` value will be ignored if running with Gunicorn. Defaults to `False` on non-supported platforms (Windows). +- The `WEBSOCKET_` values will be ignored if in ASGI mode. +- v21.12 added: `AUTO_EXTEND`, `MOTD`, `MOTD_DISPLAY`, `NOISY_EXCEPTIONS` +- v22.9 added: `INSPECTOR` +- v22.12 added: `INSPECTOR_HOST`, `INSPECTOR_PORT`, `INSPECTOR_TLS_KEY`, `INSPECTOR_TLS_CERT`, `INSPECTOR_API_KEY` +``` + +## 超时 + +### REQUEST_TIMEOUT + +请求超时测量当一个新打开的 TCP 连接传递到 +Sanic 后端服务器时本机之间的时间长度。 和收到整个HTTP请求时的即时通讯。 如果所需时间超过 +`REQUEST_TIMEOUT` (秒), 这被视为客户端错误,所以Sanic生成一个 `HTTP 408` 响应 +并将其发送到客户端。 Set this parameter's value higher if your clients routinely pass very large request payloads +or upload requests very slowly. + +### 重置_TIMEOUT + +响应超时量度介于 Sanic 服务器通过 HTTP 请求到 Sanic App之间的时间。 即时HTTP响应发送到客户端。 如果时间超过`RESPONSE_TIMEOUT` (秒), 这被视为服务器错误,所以Sanic生成一个 `HTTP 503` 响应,并将其发送到客户端。 如果您的应用程序可能有长期运行的过程会延迟 +生成响应,则设置此参数的值更高。 + +### KEEP_ALIVEUT + +#### 什么是继续存活? 保持活的超时值是什么? + +`Keep-Alive`是`HTTP 1.1`中引入的HTTP功能。 发送 HTTP 请求时 客户端(通常是一个网页浏览器应用程序)可以设置一个 `Keep-Alive` 头来指示http服务器(Sanic)在发送响应后不关闭TCP连接。 这允许客户端重新使用现有的 TCP 连接来发送其后的 HTTP 请求。 并确保客户端和服务器更有效的网络通信。 + +默认情况下,`KEEP_ALIVE`配置变量设置为 `True`。 如果您在应用程序中不需要此功能, 设置为 `False` 以使所有客户端连接在响应发出后立即关闭。 无论请求时是否有“Keep-Alive”标题。 + +服务器保持TCP连接打开的时间长度由服务器自己决定。 在Sanic中,该值使用`KEEP_ALIVE_TIMEOUT` 来配置它。 默认情况下,**它被设置为 120 秒**。 这意味着,如果客户端发送一个 `Keep-Alive` 头,在发送响应后,服务器将保持TCP 连接打开120秒。 并且客户端可以在此时间内重新使用连接发送另一个 HTTP 请求。 + +供参考: + +- Apache httpd 服务器默认保留生命超时 = 5 秒 +- Nginx 服务器默认保留存活超时 = 75 秒 +- Nginx 性能调节准则使用了保持生命=15秒 +- Caddy 服务器默认保留存活超时 = 120秒 +- IE (5-9) 客户硬保留生命限制 = 60 秒 +- Firefox 客户硬存活限制 = 115秒 +- Opera 11 客户端硬存活限制 = 120 秒 +- Chrome 13+ 客户端保持生命限制 > 300+ 秒 + +## 代理配置 + +请参阅[代理配置部分](/guide/advanced/proxy-headers.md) diff --git a/guide/content/zh/guide/running/development.md b/guide/content/zh/guide/running/development.md new file mode 100644 index 0000000000..63896a08bd --- /dev/null +++ b/guide/content/zh/guide/running/development.md @@ -0,0 +1,308 @@ +# 贸易和发展会议 + +应该提到的第一件事是,集成到 Sanic 的 web 服务器是 **不是** 开发服务器。 + +它已准备就绪,除非您在调试模式下启用\*。 + +## 调试模式 + +通过设置调试模式,Sanic将会在其输出中更详细,并将禁用几个运行时的优化。 + +```python +# server.py +from sanic import Sanic +from sanic.response import json + +app = Sanic(__name__) + +@app.route("/") +async def hello_world(request): + return json({"hello": "world"}) +``` + +```sh +sanic server:app --host=0.0.0.0 --port=1234 --debug +``` + +.. 危险:: + +``` +Sanic's debug mode will slow down the server's performance, and is **NOT** intended for production environments. + +**DO NOT** enable debug mode in production. +``` + +## 自动重新加载 + +.. 列: + +``` +Sanic 提供了一种启用或禁用自动重新加载器的方式。 启用它的最简单方式是使用 CLI 的 `--reload` 参数来激活自动重新加载。 每次更改 Python 文件,读取器将自动重启您的应用程序。 正在开发时这非常方便。 + +... 注意: + + 读取器仅在使用 Sanic's 的[工人经理](.) 时才可用。 manager.md. 如果您已禁用它使用 "--lin-process" ,则读取器将不会对您开放。 +``` + +.. 列: + +```` +```sh +sanic path.to:app --reload +`` +你也可以使用短暂属性 +```sh +sanic path.to:app -r +``` +```` + +.. 列: + +``` +如果你有额外的目录,你想要在文件保存时自动重新加载 (例如) 一个 HTML 模板的目录,您可以使用 "--reload-dir" 添加。 +``` + +.. 列: + +```` +```sh +sanic path.to:app --reload --reload-dir=/path/to/templates +``` +Or multiple directories, shown here using the shorthand properties +```sh +sanic path.to:app -r -R /path/to/one -R /path/to/two +``` +```` + +## Development REPL + +Sanic CLI 带有一个 REPL (又名“读-写循环”),可用来与您的应用程序进行交互。 这对调试和测试非常有用。 一个 REPL 是当你在没有任何参数的情况下运行 `python` 时得到的交互式外壳。 + +.. 列: + +``` +你可以将 "--repli" 参数传递到 Sanic CLI 来启动 REPL +``` + +.. 列: + +```` +```sh +sanic path.to.server:app --repl +``` +```` + +.. 列: + +``` +也许更方便的是,当你运行`--dev`时,萨尼克会自动为你启动REPL。 然而,在这种情况下,你可能会在实际启动REPL之前被提示按“ENTER”键。 +``` + +.. 列: + +```` +```sh +sanic path.to.server:app --dev +``` +```` + +![](/assets/images/repli.png) + +如上文截图所示,REPL将自动为全局命名空间添加几个变量。 它们是: + +- `app` - Sanic 应用程序实例。 这是传递到 `sanic` CLI 的相同实例。 +- `sanic` - `sanic` 模块。 这是在您运行 "import sanic" 时导入的同一个模块。 +- `do` - 一个将创建模拟`Request`对象并将其传递给您的应用程序的函数。 这对测试你来自REPL的申请非常有用。 +- `client` - 一个`httpx.glient`的实例被配置为向您的应用程序提出请求。 这对测试你来自REPL的申请非常有用。 **注意:** 只有在你的环境中安装了 `httpx` 时,这才是可用的。 + +### Async/Awaw 支持 + +.. 列: + +``` +REPL 支持 `async`/`await` 语法。这意味着你可以使用 `await` 来等待异步操作完成。 这有助于测试异步代码。 +``` + +.. 列: + +```` +```python +>>> > 等待 app.ctx.db.fetchval("SELECT 1") +1 +``` +```` + +### `app`变量 + +你需要记住,`app`变量是你的应用程序实例,因为它是在REPL启动时存在的。 它是运行CLI 命令时加载的实例。 这意味着对你的源代码的任何更改,然后在工人中重新加载,都不会反映在`app`变量中。 如果你想要与重新加载的应用进行交互,你需要重新启动REPL。 + +然而,访问REPL中的原始应用程序以进行临时测试和调试也非常有用。 + +### "客户端" 变量 + +当 [httpx](https://www.python-httpx.org/) 安装在您的环境中时,"client" 变量将可在REPL中找到。 这是一个 `httpxClient` 的实例,它被配置为向您正在运行的应用程序提出请求。 + +.. 列: + +``` +若要使用它,只需调用客户端上的 HTTP 方法之一。请参阅[httpx documentation](https://www.python-httpx.org/api/#client)获取更多信息。 +``` + +.. 列: + +```` +```python +>>> client.get("/") + +``` +```` + +### `do`函数 + +正如上文所讨论的那样,“app”实例就像启动REPL时那样存在,并且在REPL内部进行了修改。 导致服务器重新加载的实例的任何更改都不会反映在`app`变量中。 这是`do`函数的位置。 + +让我们说你已经修改了你在REPL中的应用程序以添加一条新的路由: + +```python +>>> @app.get("/new-route") +... async def new_route(request): +... return sanic.json({"hello": "world"}) +... +>> +``` + +您可以使用 `do` 函数模拟请求,并将其传递到应用程序中,仿佛它是一个真正的 HTTP 请求。 这将允许您测试您的新路线,而不必重新启动REPL。 + +```python +>>>>正在等待 do("/new-route") +结果(request=, response=) +``` + +`do`函数返回一个包含 `Request` 和 `Response` 对象的 `Result` 对象。 这是一个 `NamedTuple` ,因此您可以按名称访问值: + +```python +>>>> 结果 = 等待完成("/new-route") +>>>> result.request + +>>> result.reply + +``` + +或者,通过摧毁管: + +```python +>>>>请求, 应答 = 等待完成("/new-route") +>>>>>>>>请求 + +>>>>>>>> 应答 + +``` + +### 什么时候使用 `do` 和 `client`? + +.. 列: + +``` +**Use `do` when ...** + +- You want to test a route that does not exist in the running application +- You want to test a route that has been modified in the REPL +- You make a change to your application inside the REPL +``` + +.. 列: + +``` +**Use `client` when ...** + +- You want to test a route that already exists in the running application +- You want to test a route that has been modified in your source code +- You want to send an actual HTTP request to your application +``` + +_添加于 v23.12_ + +## 完成开发模式 + +.. 列: + +``` +如果你想要处于调试模式**和** 运行自动重新加载器,你可以通过 `dev=True`。 这相当于**调试+自动重新加载+重新加载**。 + +*添加于v22.3* +``` + +.. 列: + +```` +```sh +sanic path.to:app --dev +`` +你也可以使用短暂属性 +```sh +sanic path.to:app -d +``` +```` + +在 v23.12 `--dev` 旗帜上添加了能够启动一个REPL 详见[Development REPL](./development.md#development-reply)部分。 + +到 v23.12,`--dev` 旗帜大致等于`--debug --reload --reload --repli`。 使用 "--dev" 将需要你明确开头点击 "ENTER",然后通过 "--repli" 旗帜明确开头。 +在 v23.12之前,"--dev" 旗帜更类似于“--debug --reload\`”。 + +.. 列: + +``` +如果你想要在使用 --dev' 标志时禁用REPL,你可以通过 "--no-reply "。 +``` + +.. 列: + +```` +```sh +sanic path.to:app --dev --no-repl +``` +```` + +## 自动TLS证书 + +当在 `DEBUG` 模式下运行时,您可以要求Sanic 处理本地主机临时TLS 证书的设置。 如果您想要访问 'https://' 本地发展环境,这将是很有帮助的。 + +此功能由 [mkcert](https://github.com/FiloSottile/mkcert) 或 [trustme](https://github.com/python-trio/trustme) 提供。 两者都是好的选择,但也有一些差异。 `Trustme` 是一个 Python 库,可以安装在 `pip` 里的环境。 这使得可以轻松地处理Envrionment,但在运行 HTTP/3 服务器时是不兼容的。 `mkcert`可能是一个更多的安装过程,但可以安装本地CA并使其更容易使用。 + +.. 列: + +``` +您可以通过设置 `config.LOCAL_CERT_CREATOR` 选择哪个平台。当设置为 `auto`时,它将选择任何一个选项,如果可能则选择`mkcert`。 +``` + +.. 列: + +```` +```python +app.config.LOCAL_CERT_CREATOR= "auto" +app.config.LOCAL_CERT_CREATOR= "mkcert" +app.config.LOCAL_CERT_CREATOR= "trustme" +``` +```` + +.. 列: + +``` +Sanic 服务器运行时间可以启用自动TLS: +``` + +.. 列: + +```` +```sh +sanic path.to.server:app --auto-tls --debug +``` +```` + +.. 警告:: + +``` +本地的 TLS 证书(就像`mkcert` 和 `trustme` 生成的证书一样)**不适用于生产环境。 如果您不熟悉如何获得*真实*TLS证书,请签出[如何...](../how-to/tls.md)。 +``` + +_添加于 v22.6_ diff --git a/guide/content/zh/guide/running/inspector.md b/guide/content/zh/guide/running/inspector.md new file mode 100644 index 0000000000..b8da974281 --- /dev/null +++ b/guide/content/zh/guide/running/inspector.md @@ -0,0 +1,245 @@ +# 检查员 + +Sanic 检查员是Sanic Server的一个特征。 在使用内置的 [工人经理] (./manager.md) 运行 Sanic 时,它是唯一可用的。 + +这是一个 HTTP 应用程序_可选_在您的应用程序后台运行,允许您与运行中的应用程序的实例进行交互。 + +.. tip:: INFO + +``` +在v22.9中,检查员的能力有限,但本页上的文件假定您正在使用v22.12或更多。 +``` + +## 正在开始 + +检查员默认是禁用的。 要启用它,您有两个选项。 + +.. 列: + +``` +在创建应用程序实例时设置一个标记。 +``` + +.. 列: + +```` +```python +app = Sanic("TestApp", inspector=True) +``` +```` + +.. 列: + +``` +或者设置一个配置值。 +``` + +.. 列: + +```` +```python +app = Sanic("测试应用") +app.config.INSPECTOR = True +``` +```` + +.. 警告:: + +``` +如果您正在使用配置值,它*必须在主工作流程开始之前尽早完成。 这意味着它要么应该是一个环境变量,要么应该在创建上文所示的应用程序实例之后马上设定。 +``` + +## 使用检查器 + +一旦检查员运行,您将可以通过 CLI 或通过 HTTP 直接访问 Web API 访问它。 + +.. 列: + +```` +**Via CLI** +```sh +sanic inspection +``` +```` + +.. 列: + +```` +**通过 HTTP** +```sh +curl http://localhost:6457 +``` +```` + +.. 注: + +``` +请记住,检查员没有在您的 Sanic 应用程序上运行。它是一个分隔过程,具有一个分隔的应用程序,并且在一个隔绝的套接字上暴露。 +``` + +## 内置命令 + +检查员带着以下内置命令。 + +| CLI 命令 | HTTP 操作 | 描述 | +| -------- | ---------------------------------- | ------------------- | +| `检查` | `GET /` | 显示正在运行的应用程序的基本细节。 | +| `检查重新加载` | `POST /重新加载` | 触发所有服务器员工的重新加载。 | +| \`检查关机' | `POST /shutdown` | 触发所有进程的关闭。 | +| `检查比例N` | `POST /scale`
`{"replicas": N}` | 缩放工人数量。 `N`是复制的目标数。 | + +## 自定义命令 + +检查员很容易添加自定义命令(和终点)。 + +.. 列: + +``` +将`Inspector`类子类并创建任意方法。 只要方法名称前面没有下划线(`_`), 然后方法的名称将是视察员上的一个新的子命令。 +``` + +.. 列: + +```` +```python +从Sanic.worker导入json +n旁观导入检查员 + +class MyInspector(Inspector): + async def something(self, *args, **kwargs: + print(args) + print(kwargs) + +app = Sanic("测试应用", spector_class=MyInspector, spector=True) +``` +```` + +这将会显示常规模式中的自定义方法: + +- CLI: `sanic inspection ` +- HTTP: `POST /` + +必须指出,新方法接受的参数来自你打算如何使用命令。 例如,上面的“something”方法接受所有基于位置和关键字的参数。 + +.. 列: + +``` +在 CLI 中,位置和关键字参数作为您方法的定位或关键词参数传递。 所有值都将是一个 `str` 但有以下例外情况: + +- 一个没有分配值的关键字参数将是: `True` +- 除非参数前缀为 `no `, 然后它将是:`False` +``` + +.. 列: + +```` +```sh +sanic 检查了两个--four --no-five --six=6 +`` +在您的应用程序日志控制台中, 你会看到: +``` +('one', 'tw', 'three') +{'four': True, 'five': False, 'six': '6'} +``` +```` + +.. 列: + +``` +直接点击 API 可以实现同样的目标。您可以在 JSON 有效载荷中将参数传递到方法中。 唯一需要注意的是,位置参数应该以`{"args": [...] }`的形式暴露。 +``` + +.. 列: + +```` +```sh +curl http://localhost:6457/something \ + --json '{"args":["one", "two", "three"], "four":true, "five":false, "six":6}' +``` +In your application log console, you will see: +``` +('one', 'two', 'three') +{'four': True, 'five': False, 'six': 6} +``` +```` + +## 在生产中使用 + +.. 危险:: + +``` +在向检查员展示产品之前,请仔细考虑本节中的所有选项。 +``` + +当在远程生产实例中运行检查员时,您可以通过需要 TLS 加密和需要 API 密钥认证来保护端点。 + +### TLS 加密 + +.. 列: + +``` +通过 TLS 向检查员HTTP 实例将路径传递到您的证书和密钥。 +``` + +.. 列: + +```` +```python +app.config.INSPECTOR_TLS_CERT = "/path/to/cert.pem" +app.config.INSPECTOR_TLS_KEY = "/path/to/key.pem" +``` +```` + +.. 列: + +``` +这将需要使用 "--secure" 标志或 "https://"。 +``` + +.. 列: + +````` +```sh +sanic inspection --secure --host= +```` +```sh +curl https:////:6457 +``` +````` + +### API 密钥认证 + +.. 列: + +``` +您可以通过持单人令牌身份验证来保护 API。 +``` + +.. 列: + +```` +```python +app.config.INSPECTOR_API_KEY = "Super-Secret-200" +``` +```` + +.. 列: + +``` +这将需要 "--api-key" 参数,或无记者令牌授权标题。 +``` + +.. 列: + +```` +```sh +sanic inspect --api-key=Super-Secret-200 +``` +```sh +curl http://localhost:6457 -H "Authorization: Bearer Super-Secret-200" +``` +```` + +## 配置 + +See [configuration](./configuration.md) diff --git a/guide/content/zh/guide/running/manager.md b/guide/content/zh/guide/running/manager.md new file mode 100644 index 0000000000..ffe0e1bad9 --- /dev/null +++ b/guide/content/zh/guide/running/manager.md @@ -0,0 +1,657 @@ +--- +title: 工人管理器 +--- + +# 工人管理器 + +22.9版引入了工人经理及其功能。 + +_本节的详细信息是为了更高级的用法,**无需** 开始。_ + +管理人员的目的是在开发和生产环境之间建立连贯性和灵活性。 您是否打算运行单个工人或多个工人,无论是否使用自动重新加载: 体验是一样的。 + +一般而言,它看起来像这样: + +![](https://user-images.githubusercontent.com/1662669/178677618-3b4089c3-6c6a-4ecc-8d7a-7eba2a7f29b0.png) + +当你运行 Sanic 时,主要进程会实例化一个 `WorkerManager` 。 该经理负责运行一个或多个`WorkerProcess`。 通常有两种程序: + +- 服务器进程和 +- 非服务器流程。 + +为了方便起见,用户指南通常使用“工人”或“工人处理”这个术语来表示服务器过程。 和“经理”指在您的主要进程中运行的单个工人管理员。 + +## Sanic 服务器如何启动进程 + +Sanic 将使用 [spawn](https://docs.python.org/3/library/multiprocessing.html#contextsand-start-methods) 启动进程。 这意味着对于每个进程/工作者,您的应用程序的全局范围将在自己的线程上运行。 The practical impact of this that _if_ you do not run Sanic with the CLI, you will need to nest the execution code inside a block to make sure it only runs on `__main__`. + +```python +if __name__ == "__main__": + app.run() +``` + +如果您没有,您可能会看到像这样的错误消息: + +``` +sanic.exceptions.ServerError: Sanic server could not start: [Errno 98] Address already in use. + +This may have happened if you are running Sanic in the global scope and not inside of a `if __name__ == "__main__"` block. + +See more information: https://sanic.dev/en/guide/deployment/manager.html#how-sanic-server-starts-processes +``` + +The likely fix for this problem is nesting your Sanic run call inside of the `__name__ == "__main__"` block. 如果您在嵌套后继续收到此消息,或者如果您在使用CLI时看到此消息, 然后,这意味着您正在尝试使用的端口在您的机器上不可用,您必须选择另一个端口。 + +### 开始工作者 + +所有工序_必须_在启动时发送确认信息。 这种情况发生在最严重的情况下,你作为开发者不需要做任何事情。 然而,如果一个或多个工人不发送`ack`消息,管理员将用状态码`1`退出, 或者工人进程在试图启动时抛出异常。 如果没有遇到例外情况,管理员将等待至多三十(30)秒的确认时间。 + +.. 列: + +``` +在你知道你需要更多时间来开始的情况下,你可以把管理员混为一谈。 阈值不包括侦听器内的任何内容, 并且限制在您的应用程序的全局范围内的执行时间。 + +如果你遇到这个问题,它可能会表明需要更深入地了解导致启动速度缓慢的原因。 +``` + +.. 列: + +```` +```python +from sanic.worker.many import WorkerManager + +WorkerManager.THRESHOLD = 100 # 值在 0.1s +``` +```` + +欲了解更多信息,请查看[工人的链接](#worker-ack)。 + +.. 列: + +``` +如上所述,Sanic将使用 [spawn](https://docs.python.org/3/library/multiprocessing.html#contextsand-start-methods) 启动工人进程。 如果你想改变这种行为并且知道使用不同的起始方法会产生什么影响,你可以在这里进行修改。 +``` + +.. 列: + +```` +```python +from sanic import Sanic + +Sanic.start_method = "fork" +``` +```` + +### 工人套装 + +当您所有的工人在子进程中运行时,可能出现的问题会被创建:僵局。 当儿童进程停止运作时,就可能出现这种情况,但主要进程并不知道发生了这种情况。 因此,Sanic 服务器将在启动后自动向主进程发送一个 'ack' 消息 (以便确认)。 + +在22.9版本中,`ack`超时短暂,仅限`5s`。 在第22.12版中,超时时间延长到\`30秒'。 如果您的应用程序在30秒后关闭,可能需要手动增加此阈值。 + +.. 列: + +``` +`WorkerManager.THRESHOLD`的值是 `0.1s`。因此,要将它设置为 1分钟,您应该将值设置为 `600'。 + +此值应尽早在您的应用程序中设置,并且最好在全局范围内出现。 在主要进程开始后设置它将是行不通的。 +``` + +.. 列: + +```` +```python +from sanic.worker.manager import WorkerManager + +WorkerManager.THRESHOLD = 600 +``` +```` + +### 零下限重启时间 + +默认情况下,当重启工人时,萨尼克会先拆除现有的进程,然后再开始一个新进程。 + +如果您打算在生产中使用重启功能,那么您可能会有兴趣进行零下机刷新。 可以通过强迫读取器改变订单来实现这一点。 等待它到 [ack](#worker-ack), 然后拆除旧的进程。 + +.. 列: + +``` +从多路程序中,使用 `zero_downtime` 参数 +``` + +.. 列: + +```` +```python +app.m.restt(zero_downtime=True) +``` +```` + +_在 v22.12中添加_ + +## 使用工人流程之间的共享环境 + +Python 提供了几种方法用于[交换对象](https://docs.python.org/3/library/multiprocessing.html#exchanging-objects-between en-processes)、 [synchronizing](https://docs.python.org/3/library/multiprocessing.html#synization-between process)和[sharing state](https://docs.python.org/3/library/multiprocessing.html#sharing-state-honen-processes) 之间的流程。 这通常涉及来自`multiprocessing` 和 `ctypes` 模块的对象。 + +如果你熟悉这些对象以及如何与它们合作, 你会很乐意知道, Sanic 提供了一个 API 用于在你的工序之间分享这些物体。 如果你不熟悉, 鼓励您阅读以上链接的 Python 文档,然后尝试一些示例,然后执行共享环境。 + +类似于[应用程序上下文](../basics/app.md#application-context)允许应用程序在应用程序整个生命周期内与 `app 共享状态。 tx`, 共享上下文为上述特殊对象提供了相同的内容。 此上下文可用于 `app.shared_ctx` 并且应该**ONLY** 用于共享用于此目的的物体。 + +`shared_ctx`将: + +- _NOT_ 共享常规对象,如`int`、`dict`或`list` +- _NOT_ 在不同机器上运行的 Sanic 实例之间共享状态 +- _NOT_ 共享状态到非工序中 +- **仅** 服务器工人之间由同一管理器管理的共享状态 + +将不恰当对象附加到 "shared_ctx" 可能会导致警告,而不是错误。 您应该小心不要意外添加一个不安全的对象到 "shared_ctx" ,因为它可能无法正常工作。 如果你因为其中一个警告而在这里被指向,你可能会意外地在 "shared_ctx" 中使用不安全的物体。 + +.. 列: + +``` +为了创建一个共享对象,你**必须** 在主流程中创建它,并将它附加在`main_process_start`监听器中。 +``` + +.. 列: + +```` +```python +来自多处理导入队列 + +@app.main_process_start +async def main_process_start(app): + app.shared_ctx.queue = Queue() +``` +```` + +尝试附加到监听器外面的`shared_ctx`对象可能会导致一个 `RuntimeError` 。 + +.. 列: + +``` +在 `main_process_start` 监听器中创建对象并附加到 `shared_ctx` 之后, 只要有应用程序实例就可以在您的工作人员中使用(例如:监听器、中间器、请求处理器)。 +``` + +.. 列: + +```` +```python +来自多处理导入队列 + +@app.get("") +async def 处理器(请求): + request.app.shared_ctx.queue.put(1) + ... +``` +```` + +## 访问多声道器 + +应用程序实例可以访问一个对象,它能够提供与经理和其他工人进程互动的权限。 此对象被附加为 `app.multiplexer` 属性,但它更容易被其别名访问:`app.m`。 + +.. 列: + +``` +例如,您可以访问当前的工人状态。 +``` + +.. 列: + +```` +```python +@app.on_request +async def print_state(request: Request): + print(request.app.m.name) + print(request.app.m.pid) + print(request.app.m.state) +``` +``` +Sanic-Server-0-0 +99999 +{'server': True, 'state': 'ACKED', 'pid': 99999, 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), 'starts': 2, 'restart_at': datetime.datetime(2022, 10, 1, 0, 0, 12, 861332, tzinfo=datetime.timezone.utc)} +``` +```` + +.. 列: + +``` +`multiplexer` 也可以终止管理器或重启工作人员进程 +``` + +.. 列: + +```` +```python +# shutdown the entire application and all processes +app.m.name.terminate() + +# restart the current worker only +app.m.name.restart() + +# restart specific workers only (comma delimited) +app.m.name.restart("Sanic-Server-4-0,Sanic-Server-7-0") + +# restart ALL workers +app.m.name.restart(all_workers=True) # Available v22.12+ +``` +```` + +## 工人状态 + +.. 列: + +``` +如上文所示,`multiplexer`可以报告当前运行的工人的状态。 然而,它也包含所有正在运行的进程的状态。 +``` + +.. 列: + +```` +```python +@app.on_request +async def print_state(request: Request): + print(request.app.m.workers) +``` +``` +{ + 'Sanic-Main': {'pid': 99997}, + 'Sanic-Server-0-0': { + 'server': True, + 'state': 'ACKED', + 'pid': 9999, + 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), + 'starts': 2, + 'restart_at': datetime.datetime(2022, 10, 1, 0, 0, 12, 861332, tzinfo=datetime.timezone.utc) + }, + 'Sanic-Reloader-0': { + 'server': False, + 'state': 'STARTED', + 'pid': 99998, + 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), + 'starts': 1 + } +} +``` +```` + +可能的州有: + +- `NONE` - 工人已被创建,但还没有进程 +- `IDLE` - 进程已创建,但尚未运行 +- `STARTING` - 进程正在开始 +- `STARTED` - 进程已经开始 +- `ACKED` - 进程已经启动并发送了一条确认信息(通常只适用于服务器进程) +- `JOINED` - 该进程已经退出并加入了主要进程 +- `TERNATED` - 该进程已经退出并终止 +- `RESTARTING` - 进程正在重启 +- `FAILED` - 进程遇到异常,已不再运行 +- `COMPLETED` - 该进程已经完成并成功退出 + +## 内置非服务器进程 + +如上所述,管理员也有能力运行非服务器程序。 Sanic 带有两种内置类型的非服务器进程,并允许[创建自定义进程](#running-custom-processes)。 + +两个内置进程是 + +- [auto-reloader](./development.md#automatic-reloader) 可选地启用监视文件系统进行更改并触发重启 +- [inspector](#spector), 可选地启用提供外部访问的运行实例状态 + +## 检查员 + +Sanic 有能力在 CLI 中暴露`multiplexer` 的状态和功能。 目前,这需要在 CLI 命令上运行与运行中的 Sanic 实例相同的机器。 默认情况下,检查员被禁用。 + +.. 列: + +``` +要启用它,请将配置值设置为“True”。 +``` + +.. 列: + +```` +```python +app.config.INSPECTOR = True +``` +```` + +您现在可以访问这些CLI命令: + +``` +净化检查重新加载服务器工作人员的一次重新加载 +净化检查关闭应用程序和所有进程 +净化检查员将工人人数缩放到N +净化检查 运行一个自定义命令 +``` + +![](https://user-images.githubusercontent.com/166269/190099384-2f2f3fae-22d5-4529-b279-8446f6b5f9bd.png) + +.. 列: + +``` +通过在您的机器上显示一个小的 HTTP 服务来实现这一点。您可以使用配置值控制位置: +``` + +.. 列: + +```` +```python +app.config.INSPECTOR_HOST = "localhost" +app.config.INSPECTOR_PORT = 6457 +``` +```` + +[了解更多](./检查员.md)来了解与检查员可能做些什么。 + +## 运行自定义进程 + +若要在 Sanic 上运行一个管理下的自定义进程,您必须创建一个可调用。 如果这个进程是打算长期运行的,那么它就应该用一个`SIGINT`或`SIGTERM`的信号来处理一个关机呼叫。 + +.. 列: + +``` +Python最简单的方法是把你的循环换成`KeyboardInterrupt'。 + +如果您打算运行另一个应用程序,像机器人那样, 然后,它很可能已经有能力处理这个信号,而且你可能不需要做任何事情。 +``` + +.. 列: + +```` +```python +from time import sleep + +def my_process(foo): + try: + while True: + sleep(1) + except KeyboardInterrupt: + print("done") +``` +```` + +.. 列: + +``` +此可调用必须在 `main_process_ready` 监听器中注册。 必须注意的是,**不** 是你应该注册[共享上下文](#使用共享上下文-工人之间的过程)对象的同一地点。 +``` + +.. 列: + +```` +```python +@app.main_process_ready +async def ready(app: Sanic, _): +# app.manager.manager.manag(, , ) + app.manager.manager.management("MyProcess", my_proces", {"foo": "bar"}) +``` +```` + +### B. 过渡时期与持久进程 + +.. 列: + +``` +When you manage a process with the `manage` method, you have the option to make it transient or durable. A transient process will be restarted by the auto-reloader, and a durable process will not. + +By default, all processes are durable. +``` + +.. 列: + +```` +```python +@app.main_process_ready +async def ready(app: Sanic, _): + app.manager.manager.management( + "MyProcess", + my_process, + {"foo": "bar"}, + transitent=True, + ) +``` +```` + +### 跟踪或未跟踪的进程 + +Sanic 将从盒子中追踪所有进程的状态。 这意味着您可以从 [multiplexer](./manager#access-to-the-multiplexer)对象或从 [Inspector](./manager#inspector)访问过程状态。 + +详见[工人状态](./manager#worker-state)。 + +有时运行并非长期运行的背景进程是有益的。 你运行一次,直到完成,然后他们退出。 完成后,它们将处于`FAILED`或`COMPLETED`状态。 + +.. 列: + +``` +当你正在运行一个非长期运行的过程时,你可以在"管理"方法中设置 "tracked=False" 来选择不跟踪它。 这意味着一旦完成这一进程,它将从跟踪的进程清单中删除。 您只能在进程运行时检查进程状态。 +``` + +.. 列: + +```` +```python +@app.main_process_ready +async def ready(app: Sanic, _): + app.manager.manager.manage( + "One AndDone", + do_once, + {}, + tracked=False, + ) +``` +```` + +_添加于 v23.12_ + +### 可重新启动自定义进程 + +临时性的自定义进程将\*\*总是可以重启。 这意味着自动重启将按预期进行。 然而,如果你想要能够_手动_重新启动一个过程,但它不会被自动重新加载器重新启动? + +.. 列: + +``` +在这个场景中,你可以在“管理”方法中设置 `rehartable=True`。 这将允许您手动重启进程,但它不会被自动重新加载器重启。 +``` + +.. 列: + +```` +```python +@app.main_process_ready +async def ready(app: Sanic, _): + app.manager.manager.management( + "MyProcess", + my_process, + {"foo": "bar"}, + restable=True, + ) +``` +```` + +.. 列: + +``` +您现在可以从多路程序手动重启此进程。 +``` + +.. 列: + +```` +```python +@app.get("/restart") +async def restest_handler(request: acquest): + request.app.m.restt("Sanic-MyProcess-0") + return json({"foo": request.app.m.name}) +``` +```` + +_添加于 v23.12_ + +### 飞行过程管理 + +自定义进程通常在 `main_process_ready` 监听器中添加。 然而,有时候您想要在应用程序启动后添加一个过程。 例如,您可能想要从请求处理器中添加一个过程。 多路程序提供了这样做的方法。 + +.. 列: + +``` +一旦你有一个多路由器的引用,你可以调用 `manage` 来添加一个过程。 它与经理上的 `manag` 方法相同。 +``` + +.. 列: + +```` +```python +@app.post("/start") +async def start_handler(request): + request.app.m. anage( + "我的过程", + my_process, + {"foo": "bar"}, + workers=2, + + return json({"foo": request. pweb name}) +``` +```` + +_添加于 v23.12_ + +## 单进程模式 + +.. 列: + +``` +如果你想要退出运行多个进程,你只能在一个进程中运行 Sanic。 在这种情况下,管理员不会运行。 您也无法访问任何需要处理的功能 (自动重新加载、检查员等)。 +``` + +.. 列: + +```` +```sh +sanic path.to.server:app --single-process +``` +```python +if __name__ == "__main__": + app.run(single_process=True) +``` +```python +if __name__ == "__main__": + app.prepare(single_process=True) + Sanic.serve_single() +``` +```` + +## 声学和多处理器 + +Sanic大量使用 [`multiprocessing` 模块](https://docs.python.org/3/library/multiprocessing.html) 来管理工人过程。 您通常应该避免低级别使用此模块(例如设置起始方法),因为它可能会干扰Sanic的功能。 + +### 在 Python 中启动方法 + +在解释萨尼克试图做什么之前,必须了解`start_method`是什么以及为什么它是重要的。 Python通常允许三种不同的方法启动进程: + +- `fork` +- `spawn` +- `forkserver` + +"fork" 和 "forkserver" 方法仅在 Unix 系统上可用,而"spawn" 是Windows上唯一可用的方法。 在您可以选择的 Unix 系统中,`fork` 通常是默认系统方法。 + +鼓励您阅读[Python文档](https://docs.python.org/3/library/multiprocessing.html#contextsand-start-methods),了解更多关于这些方法之间差异的信息。 然而,重要的是`fork`基本上将父过程的整个记忆复制到子过程中。 而`spawn`会创建一个新的流程,然后将应用程序加载到该流程中。 This is the reason why you need to nest your Sanic `run` call inside of the `__name__ == "__main__"` block if you are not using the CLI. + +### 智能和启动方法 + +默认情况下,Sanic会尝试使用 `spawn` 作为起始方法。 这是因为它是Windows上唯一可用的方法,它是Unix系统上最安全的方法。 + +.. 列: + +``` +然而,如果你在 Unix 系统上运行 Sanic 而你想使用 `fork` 你可以通过设置 `Sanic` 类的 `start_method` 来做到这一点。 您将尽早在您的应用程序中,并且最好是在全局范围内,然后再导入任何其他模块。 +``` + +.. 列: + +```` +```python +from sanic import Sanic + +Sanic.start_method = "fork" +``` +```` + +### 覆盖一个 `RuntimeError` + +您可能已经收到了一个看起来像这样的 `RuntimeError` : + +``` +运行时错误:请求了开始方法“spawn”,但“fork”已经设置。 +``` + +如果是,这意味着你在应用程序中的某个地方试图设置与萨尼克试图做什么相冲突的起始方法。 您有几个选项来解决这个问题: + +.. 列: + +``` +**选项1:** 你可以告诉Sanic, 开始方法已经设置, 不要再尝试. +``` + +.. 列: + +```` +```python +from sanic import Sanic + +Sanic.START_METHOD_SET = True +``` +```` + +.. 列: + +``` +**选项2:** 你可以告诉Sanic你打算使用 `fork` 而不尝试设置为 `spawn` 。 +``` + +.. 列: + +```` +```python +from sanic import Sanic + +Sanic.start_method = "fork" +``` +```` + +.. 列: + +``` +**选项3:** 您可以通过设置 `multiprocessing` 开始方法,告诉Python 使用 `spawn` 而不是 `fork` 。 +``` + +.. 列: + +```` +```python +import multiprocessing + +multiprocessing.set_start_method("spawn") +``` +```` + +在这些选项中,您应该尽早在应用程序中运行此代码。 根据具体的场景,您可能需要合并一些选项。 + +.. 注: + +```` +The potential issues that arise from this problem are usually easily solved by just allowing Sanic to be in charge of multiprocessing. This usually means making use of the `main_process_start` and `main_process_ready` listeners to deal with multiprocessing issues. For example, you should move instantiating multiprocessing primitives that do a lot of work under the hood from the global scope and into a listener. + +```python +# This is BAD; avoid the global scope +from multiprocessing import Queue + +q = Queue() +``` + +```python +# This is GOOD; the queue is made in a listener and shared to all the processes on the shared_ctx +from multiprocessing import Queue + +@app.main_process_start +async def main_process_start(app): + app.shared_ctx.q = Queue() +``` +```` diff --git a/guide/content/zh/guide/running/running.md b/guide/content/zh/guide/running/running.md new file mode 100644 index 0000000000..4616e2585f --- /dev/null +++ b/guide/content/zh/guide/running/running.md @@ -0,0 +1,585 @@ +--- +title: 正在运行 Sanic +--- + +# 正在运行 Sanic + +Sanic船上有自己的网络服务器。 在大多数情况下,这是最好的部署方法。 此外,您还可以将 Sanic 部署为 ASGI 应用,并且将其绑定到 ASGI-able Web 服务器。 + +## Sanic 服务器 + +运行Sanic的主要方式是使用包含 [CLI](#sanic-clik). + +```sh +sanic path.to.server:app +``` + +在这个示例中,已指示Sanic寻找一个名为 `path.to.server` 的 python 模块。 在该模块中,它将寻找一个叫做`app`的全球变量,它应该是`Sanic(...)`的实例。 + +```python +# ./path/to/server.py +来自Sanic import Sanic, Request, json + +app = Sanic("TestApp") + +@app.get("/") +async def handler(request): + return json({"foo": "bar"}) +``` + +你也可以下拉到[较低级别的 API](#low-level-apprun)来调用 `app.run` 作为脚本。 然而,如果您选择此选项,您应该比较舒适地处理可能因“多处理”而产生的问题。 + +### A. 工 人 + +.. 列: + +``` +By default, Sanic runs a main process and a single worker process (see [worker manager](./manager.md) for more details). + +To crank up the juice, just specify the number of workers in the run arguments. +``` + +.. 列: + +```` +```sh +sanic server:app --host=0.0.0.0 --port=1337 --workers=4 +``` +```` + +Sanic将自动旋转多个进程和它们之间的路由流量。 我们推荐像您现有的处理器一样多的工人。 + +.. 列: + +``` +获取最大CPU性能的最简单方法是使用“--fast”选项。 由于系统限制,此操作将自动运行最大数量的员工。 + +*在 v21.12* 中添加。 +``` + +.. 列: + +```` +```sh +sanic server:app --host=0.0.0.0 --port=1337 --fast +``` +```` + +在第22.9版中,Sanic引入了一个新的工人经理,以便在开发服务机和生产服务机之间提供更大的一致性和灵活性。 阅读[关于经理](./manager.md)了解更多有关工人的详情。 + +.. 列: + +``` +If you only want to run Sanic with a single process, specify `single_process` in the run arguments. This means that auto-reload, and the worker manager will be unavailable. + +*Added in v22.9* +``` + +.. 列: + +```` +```sh +sanic server:app --host=0.0.0.0 --port=1337 --sin-process +``` +```` + +### 通过命令运行 + +#### Sanic CLI + +使用 'sanic --help' 查看所有选项。 + +.. attrs:: +:title: Sanic CLI help output +:class: details + +```` +```text +$ sanic --help + + ▄███ █████ ██ ▄█▄ ██ █ █ ▄██████████ + ██ █ █ █ ██ █ █ ██ + ▀███████ ███▄ ▀ █ █ ██ ▄ █ ██ + ██ █████████ █ ██ █ █ ▄▄ + ████ ████████▀ █ █ █ ██ █ ▀██ ███████ + + To start running a Sanic application, provide a path to the module, where + app is a Sanic() instance: + + $ sanic path.to.server:app + + Or, a path to a callable that returns a Sanic() instance: + + $ sanic path.to.factory:create_app --factory + + Or, a path to a directory to run as a simple HTTP server: + + $ sanic ./path/to/static --simple + +Required +======== + Positional: + module Path to your Sanic app. Example: path.to.server:app + If running a Simple Server, path to directory to serve. Example: ./ + +Optional +======== + General: + -h, --help show this help message and exit + --version show program's version number and exit + + Application: + --factory Treat app as an application factory, i.e. a () -> callable + -s, --simple Run Sanic as a Simple Server, and serve the contents of a directory + (module arg should be a path) + --inspect Inspect the state of a running instance, human readable + --inspect-raw Inspect the state of a running instance, JSON output + --trigger-reload Trigger worker processes to reload + --trigger-shutdown Trigger all processes to shutdown + + HTTP version: + --http {1,3} Which HTTP version to use: HTTP/1.1 or HTTP/3. Value should + be either 1, or 3. [default 1] + -1 Run Sanic server using HTTP/1.1 + -3 Run Sanic server using HTTP/3 + + Socket binding: + -H HOST, --host HOST + Host address [default 127.0.0.1] + -p PORT, --port PORT + Port to serve on [default 8000] + -u UNIX, --unix UNIX + location of unix socket + + TLS certificate: + --cert CERT Location of fullchain.pem, bundle.crt or equivalent + --key KEY Location of privkey.pem or equivalent .key file + --tls DIR TLS certificate folder with fullchain.pem and privkey.pem + May be specified multiple times to choose multiple certificates + --tls-strict-host Only allow clients that send an SNI matching server certs + + Worker: + -w WORKERS, --workers WORKERS + Number of worker processes [default 1] + --fast Set the number of workers to max allowed + --single-process Do not use multiprocessing, run server in a single process + --legacy Use the legacy server manager + --access-logs Display access logs + --no-access-logs No display access logs + + Development: + --debug Run the server in debug mode + -r, --reload, --auto-reload + Watch source directory for file changes and reload on changes + -R PATH, --reload-dir PATH + Extra directories to watch and reload on changes + -d, --dev debug + auto reload + --auto-tls Create a temporary TLS certificate for local development (requires mkcert or trustme) + + Output: + --coffee Uhm, coffee? + --no-coffee No uhm, coffee? + --motd Show the startup display + --no-motd No show the startup display + -v, --verbosity Control logging noise, eg. -vv or --verbosity=2 [default 0] + --noisy-exceptions Output stack traces for all exceptions + --no-noisy-exceptions + No output stack traces for all exceptions + +``` +```` + +#### 作为一个模块 + +.. 列: + +``` +Sanic 应用程序也可以直接调用为模块。 +``` + +.. 列: + +```` +```bash +python -m sanic server.app --host=0.0.0.0 --port=1337 --workers=4 +``` +```` + +#### 使用一个工厂。 + +一个非常常见的解决办法是开发你的应用程序_不是_作为一个全局变量,而是使用出厂模式。 在这个上下文中,"factory" 是指返回一个 `Sanic(...)` 实例的函数。 + +.. 列: + +``` +假定你在 `server.py` 里有这个内容 +``` + +.. 列: + +```` +```python +from sanic import Sanic + +def create_app() -> Sanic: + app = Sanic("MyApp") + + return app +``` +```` + +.. 列: + +``` +您现在可以在 CLI 中明确地将其作为一个工厂来运行此应用程序: +``` + +.. 列: + +```` +```sh +sanic server:create_app --frant +``` +Or, 明示类似于: +```sh +sanic "server:create_app()" +``` +Or, 默示类似于: +```sh +sanic server:create_app +``` + +*默示命令添加于v23.3* +```` + +### 低级别 `app.run` + +当使用 `app.run` 时,你会像其他脚本一样调用 Python 文件。 + +.. 列: + +``` +`app.run`必须正确嵌套在一个名称的主块内。 +``` + +.. 列: + +```` +```python +# server.py +app = Sanic("MyApp") + +if __name__ == "__main__": + app.run() +``` +```` + +.. 危险:: + +```` +Be *careful* when using this pattern. A very common mistake is to put too much logic inside of the `if __name__ == "__main__":` block. + +🚫 This is a mistake + +```python +from sanic import Sanic +from my.other.module import bp + +app = Sanic("MyApp") + +if __name__ == "__main__": + app.blueprint(bp) + app.run() +``` + +If you do this, your [blueprint](../best-practices/blueprints.md) will not be attached to your application. This is because the `__main__` block will only run on Sanic's main worker process, **NOT** any of its [worker processes](../deployment/manager.md). This goes for anything else that might impact your application (like attaching listeners, signals, middleware, etc). The only safe operations are anything that is meant for the main process, like the `app.main_*` listeners. + +Perhaps something like this is more appropriate: + +```python +from sanic import Sanic +from my.other.module import bp + +app = Sanic("MyApp") + +if __name__ == "__mp_main__": + app.blueprint(bp) +elif __name__ == "__main__": + app.run() +``` +```` + +要使用低级别的 `run` API,在定义一个 `sanic.Sanic` 实例后,我们可以使用以下关键词参数来调用运行方法: + +| 参数 | 默认设置 | 描述 | +| :---------------------------------------: | :-------------: | :------------------------------------------------------------------------------------- | +| **主机** | `"127.0.0.1"` | 服务器主机地址已开启。 | +| **端口** | `8000` | 服务器端口已开启。 | +| **unix** | `无` | 服务器主机的 Unix 套接字名称 (而不是TCP)。 | +| **dev** | `False` | 等于`debug=True`和`auto_reload=True`。 | +| **debug** | `False` | 启用调试输出 (慢速服务器)。 | +| **ssl** | `无` | SSLContext for SSL 加密 (s)。 | +| **sock** | `无` | Socket 让服务器接受连接。 | +| **Workers** | `1` | 要生成的工序数量。 无法快速使用。 | +| **循环** | `无` | 一个异步兼容的事件循环。 如果没有具体说明,Sanic将创建自己的事件循环。 | +| **protocol** | `HttpProtocol` | asyncio.protocol的子类。 | +| **版本** | `HTTPVERSION_1` | 要使用的 HTTP 版本 (`HTTP.VERSION_1` 或 `HTTP.VERSION_3`). | +| **access_log** | `True` | 启用处理请求的日志 (大大减慢服务器)。 | +| **auto_reload** | `无` | 启用源目录自动重新加载。 | +| **reload_dir** | `无` | 自动读取加载器应该监视的目录路径或路径列表。 | +| **noisy_exceptions** | `无` | 是否设置全局噪音异常。 没有表示离开为默认值。 | +| **motd** | `True` | 是否显示启动消息。 | +| **motd_display** | `无` | 在启动消息中显示额外的密钥/值信息 | +| **fast** | `False` | 是否最大化工人工序。 无法与工人一起使用。 | +| **详细化** | `0` | 日志的详细级别。 最大值为 2。 | +| **自动_tls** | `False` | 是否为本地开发自动创建TLS证书。 不是生产的。 | +| **单独处理** | `False` | 是否在一个过程中运行 Sanic。 | + +.. 列: + +``` +例如,我们可以关闭访问日志以提高性能并绑定到自定义主机和端口。 +``` + +.. 列: + +```` +```python +# server.py +app = Sanic("MyApp") + +if __name__ == "__main__": + app.run(host='0.0.0.0', port=1337, access_log=False) +``` +```` + +.. 列: + +``` +现在,只需执行 'app.run(...)' 的 python 脚本 +``` + +.. 列: + +```` +```sh +python server.py +``` +```` + +对于稍微高级的实现来说,我们很高兴知道`app.run`会调用`app.preparre`和`Sanic.serve`。 + +.. 列: + +``` +因此,这些标准等同于: +``` + +.. 列: + +```` +```python +if __name__ == "__main__": + app.run(host='0.0.0.0', port=1337, access_log=False) +``` +```python +if __name__ == "__main__": + app.prepare(host='0.0.0.0', port=1337, access_log=False) + Sanic.serve() +``` +```` + +.. 列: + +``` +如果您需要将应用程序绑定到多个端口,这将是有用的。 +``` + +.. 列: + +```` +```python +if __name__ == "__main__": + app1.prepare(host='0.0.0.0', port=9990) + app1.prepare(host='0.0.0.0', port=9991) + app2.prepare(host='0.0.0.0', port=5555) + Sanic.serve() +``` +```` + +### Sanic 简单服务器 + +.. 列: + +``` +有时,你只是一个需要服务的静态文件目录。 这尤其可以方便快速站立本地主机服务器。 一个简单的服务器的神秘船,你只需要在一个目录上指明。 +``` + +.. 列: + +```` +```sh +sanic ./path/to/dir --imple +``` +```` + +.. 列: + +``` +这也可以与自动重新加载配对。 +``` + +.. 列: + +```` +```sh +sanic ./path/to/dir --simple --reload --reload-dir=./path/to/dir +``` +```` + +\*添加于 v21.6 \* + +### HTTP/3 + +Sanic 服务器使用 [aioquic](https://github.com/aiortc/aioquic) 提供 HTTP/3 支持。 此 \*\*must \*\* 安装后才能使用 HTTP/3: + +```sh +pip install sanic aioquic +``` + +```sh +pip install sanic[http3] +``` + +要启动 HTTP/3,您必须在运行应用程序时明确请求它。 + +.. 列: + +```` +```sh +sanic path.to.server:app --http=3 +``` + +```sh +sanic path.to.server:app -3 +``` +```` + +.. 列: + +```` +```python +app.run(version=3) +``` +```` + +要同时运行一个 HTTP/3 和 HTTP/1.1 服务器,您可以使用 v22.3 引入的 [application multi-servve](../release-notes/v22.3.html#application-multi-servve)。 这将自动添加一个 [Alt-Svc](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ Alt-Svc) 头到您的 HTTP/1.1 请求让客户端知道它也是可用的 HTTP/3。 + +.. 列: + +```` +```sh +sanic path.to.server:app --http=3 --http=1 +``` + +```sh +sanic path.to.server:app -3 -1 +``` +```` + +.. 列: + +```` +```python +app.preparre(version=3) +app.preparre(version=1) +Sanic.serve() +``` +```` + +因为HTTP 3 需要 TLS,您不能在没有TLS 证书的情况下启动 HTTP/3 服务器。 你应该[自己设置它](../how-to/tls.html),或者在 `DEBUG` 模式下使用 `mkcert` 。 目前,HTTP/3 的自动TLS设置与 `trustme` 不兼容。 更多详细信息请访问 [development](./development.md)。 + +_添加于 v22.6_ + +## ASGI + +Sanic也是ASGI合规者。 这意味着您可以使用您喜欢的 ASGI web服务器来运行 Sanic。 ASGI的三个主要实施方式是: [Daphne](http://github.com/django/daphne)、 [Uvicorn](https://www.uvicorn.org/)和 [Hypercorn](https://pgjones.gitlab.io/hypercorn/index.html)。 + +.. 警告:: + +``` +Daphne不支持ASGI `lifespan` 协议,因此不能用于运行 Sanic。详情请参阅[Issue #264](https://github.com/django/daphne/issues/264)。 +``` + +按照他们的文档来运行他们,但它应该看起来像这样: + +```sh +uvicorn myapp:app +``` + +```sh +超彩应用:app +``` + +使用ASGI时要注意的几件事: + +1. 当使用 Sanic 网络服务器时,websockets 将使用 `websockets` 软件包运行。 在 ASGI 模式下,没有必要使用这个软件包,因为websockets 是在 ASGI 服务器上管理的。 +2. ASGI 生命周期协议 ,只支持两个服务器事件:启动和关机。 萨里语有四个:启动前、启动后、关闭前和关机。 因此,以ASGI模式, 启动和关闭事件将连续运行,而不是围绕服务器进程开始和结束(因为现在是由 ASGI 服务器控制的)。 因此,最好使用 `after _server_start` 和 `previ_server_stop` 。 + +### 特里奥文 + +Sanic在Trio上运行时有实验支持: + +```sh +超精彩-k 三次应用:应用 +``` + +## 古尼科恩州 + +[Gunicorn](http://gunicorn.org/) ("Green Unicorn") 是一个基于UNIX的操作系统的 WSGI HTTP 服务器。 这是一个基于 Ruby Unicorn项目的前叉工人模型。 + +若要与 Gunicorn一起运行 Sanic 应用程序,您需要使用 [uvicorn]的适配器(https://www.uvicorn.org/)。 确保uvicorn已经安装并运行它与 `uvicorn.workers.UvicornWorker` for Gunicorn worker-class参数: + +```sh +gunicorn myapp:app --binding 0.0.0:1337 --worker-classuvicorn.workers.UvicornWorker +``` + +详见[Gunicorn Docs](http://docs.gunicorn.org/enarage/settings.html#max-requests)。 + +.. 警告:: + +``` +通常建议不要使用“gunicorn”,除非你需要它。 Sanic 服务器已经准备好在生产中运行 Sanic。在作出这一选择之前仔细加大您的考虑。 Gunicorn确实提供了许多配置选项,但它不是让Sanic以最快的速度运行的最佳选择。 +``` + +## 业绩考虑 + +.. 列: + +``` +在生产中运行时,请确保您关闭“debug”。 +``` + +.. 列: + +```` +```sh +sanic path.to.server:app +``` +```` + +.. 列: + +``` +如果您关闭了 "access_log" ,Sanic 也会执行最快的操作。 + +如果您仍然需要访问日志,但是想要享受此性能增强,请考虑使用 [Nginx 作为代理](。)。 nginx.md, 并让它处理您的访问记录。它将比任何Python能够处理的更快一些。 +``` + +.. 列: + +```` +```sh +sanic path.to.server:app --no-access-log +``` +```` diff --git a/guide/content/zh/help.md b/guide/content/zh/help.md new file mode 100644 index 0000000000..0e315690c7 --- /dev/null +++ b/guide/content/zh/help.md @@ -0,0 +1,32 @@ +--- +title: 需要帮助吗? +layout: 主界面 +--- + +# 需要帮助吗? + +作为活跃的开发者社区,我们尽力相互支持。 如果您需要帮助,请尝试以下中的一种: + +.. column:: + +``` +### Discord 💬 + +快速获取答案与在线聊天的最好去处 + +[Discord 聊天室](https://discord.gg/FARQzAEMAA) 的 `#sanic-support` 频道 +``` + +.. column:: + +``` +### 社区论坛 👥 + +分享代码片段 或者 想要持久的咨询支持 建议发布在这里 + +[论坛](https://community.sanicframework.org/c/questions-and-help/6)上的「问题与帮助」(Questions and Help) +``` + +--- + +我们还积极监视 [Stack Overflow](https://stackoverflow.com/questions/tagged/sanic) 上的\`[sanic]'标签。 diff --git a/guide/content/zh/index.md b/guide/content/zh/index.md new file mode 100644 index 0000000000..714aa47bfe --- /dev/null +++ b/guide/content/zh/index.md @@ -0,0 +1,335 @@ +--- +title: 快如闪电的异步 Python Web 框架 +layout: 首页 +features: + - title: 简单轻便 + details: 直观、智能、精简的API,让您可以直接开始构建应用程序。 + - title: 灵巧无束 + details: 可以用您自己的方式进行创作,不会对您造成任何约束 + - title: 高效且可拓展 + details: 关注应用的速度和可扩展性 随时为大大小小的网络应用程序提供支持 + - title: 生产就绪 + details: 开箱即用,Sanic 不仅是一个框架,也是一个服务器,并随时准备驱动您的 Web 应用 + - title: 备受信赖 + details: Sanic 是 PyPI 最受欢迎的框架之一,是顶级的异步python Web 框架 + - title: 社区驱动 + details: 从社区来,到社区去。Sanic 由社区共同维护和管理 +--- + +### ⚡ 快如闪电的异步 Python Web 框架 + +.. attrs:: +:class: columns is-multiline mt-6 + +``` +.. attrs:: + :class: column is-4 + + #### 简单轻便 + + 直观、智能、精简的API,让您可以直接开始构建应用程序。 + +.. attrs:: + :class: column is-4 + + #### 灵巧无束 + + 可以用您自己的方式进行创作,不会对您造成任何约束 + +.. attrs:: + :class: column is-4 + + #### 高效且可拓展 + + 关注应用的速度和可扩展性,可随时为大大小小的网络应用程序提供支持 + +.. attrs:: + :class: column is-4 + + #### 生产就绪 + + 开箱即用,Sanic 不仅是一个框架,也是一个服务器,并随时准备驱动您的 Web 应用 + +.. attrs:: + :class: column is-4 + + #### 备受信赖 + + Sanic 是 PyPI 最受欢迎的框架之一,是顶级的异步python Web 框架 + +.. attrs:: + :class: column is-4 + + #### 社区驱动 + + 从社区来,到社区去。Sanic 由社区共同维护和管理 +``` + +.. attrs:: +:class: is-size-3 mt-6 + +``` +**有你所期待的功能和工具。** +``` + +.. attrs:: +:class: is-size-3 ml-6 + +``` +**还有一些{span:has-text-primary:意料之外的惊喜}。** +``` + +.. tab:: 生产级别(Production-grade) + +```` +开箱即用,在安装后,Sanic 为您提供创建高扩展性、生产级服务器需的所有工具! + +甚至包括[完整的 TLS 支持](/zh/guide/how-to/tls)。 + +```python +from sanic import Sanic +from sanic.response import text + +app = Sanic("MyHelloWorldApp") + +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` + +```sh +sanic path.to.server:app +[2023-01-31 12:34:56 +0000] [999996] [INFO] Sanic v22.12.0 +[2023-01-31 12:34:56 +0000] [999996] [INFO] Goin' Fast @ http://127.0.0.1:8000 +[2023-01-31 12:34:56 +0000] [999996] [INFO] mode: production, single worker +[2023-01-31 12:34:56 +0000] [999996] [INFO] server: sanic, HTTP/1.1 +[2023-01-31 12:34:56 +0000] [999996] [INFO] python: 3.10.9 +[2023-01-31 12:34:56 +0000] [999996] [INFO] platform: SomeOS-9.8.7 +[2023-01-31 12:34:56 +0000] [999996] [INFO] packages: sanic-routing==22.8.0 +[2023-01-31 12:34:56 +0000] [999997] [INFO] Starting worker [999997] +``` +```` + +.. tab:: TLS 服务器(TLS server) + +```` +使用 带 TLS 的 Sanic 就像向其设置文件路径一样简单...... +```sh +sanic path.to.server:app --cert=/path/to/bundle。 rt --key=/path/to/privkey.pem +`` + +... 或是包含`fullchain.pem` 和 `privkey.pem`的目录文件夹 + +```sh +sanic path.to. erver:app --tls=/path/to/certs +``` + +**除此之外**,在开发中,让Sanic处理设置本地TLS证书,以便您可以通过 TLS 访问 [https://localhost:8443](https://localhost:8443) + +```sh +sanic path.to.server:app --dev --auto-tls +``` +```` + +.. tab:: Websockets + +```` +得益于 [websockets](https://websockets.readthedocs.io) 库,可以无缝实现的 WebSockets。 + +```python +from sanic import Request, Websocket + +@app.websocket("/feed") +async def feed(request: Request, ws: Websocket): + async for msg in ws: + await ws.send(msg) +``` +```` + +.. tab:: 静态文件(Static files) + +```` +为静态文件服务当然是既直观又容易。只需配置一个入口并且指定一个文件或一个目录文件夹即可。 + +```python +app.static("/", "/path/to/index.html") +app.static("/uploads/", "/path/to/uploads/") +``` + +此外,当参数是目录时,还有两个附加功能:自动提供索引和自动提供文件浏览器。 + +Sanic 可以自动将 `index.html` (或任何其他命名文件) 作为目录或其子目录中的索引页。 + +```python +app.static( + "/uploads/", + "/path/to/uploads/", + index="index.html" +) +``` + +让 Sanic 以显示文件浏览器需要这样设置。 + + +![image](/assets/images/directory-view.png) + +```python +app.static( + "/uploads/", + "/path/to/uploads/", + directory_view=True +) +``` +```` + +.. tab:: 生命周期(Lifecycle) + +```` +添加一个装饰器,就能在应用生命周期开始或结束时插入自定义的处理函数 + +```python +@app.on_request +async def add_key(request): + request.ctx.foo = "bar" + +@app.on_response +async def custom_banner(request, response): + response.headers["X-Foo"] = request.ctx.foo +``` + +服务器事件(server events)也是一样的。 + +```python +@app.before_server_start +async def setup_db(app): + app.ctx.db_pool = await db_setup() + +@app.after_server_stop +async def setup_db(app): + await app.ctx.db_pool.shutdown() +``` + +除此之外,Sanic 还允许您绑定一系列官方内置事件(称为信号),或创建和调度自己的事件。 + +```python +@app.signal("http.lifecycle.complete") # built-in (官方内置的) +async def my_signal_handler(conn_info): + print("Connection has been closed") + +@app.signal("something.happened.ohmy") # custom (自定义的) +async def my_signal_handler(): + print("something happened") + +await app.dispatch("something.happened.ohmy") +``` +```` + +.. tab:: 智能错误处理(Smart error handling) + +```` +出错后,会出现直观且合适的 HTTP 错误: + +```python +raise sanic.exceptions.NotFound # Automatically responds with HTTP 404 +``` + +或是实现您自己的错误处理方法: + +```python +from sanic.exceptions import SanicException + +class TeapotError(SanicException): + status_code = 418 + message = "Sorry, I cannot brew coffee" + +raise TeapotError +``` + +如果出现错误,Sanic 美观的开发模式错误页面将帮助您快速定位到错误所在。 + +![image](../assets/images/error-div-by-zero.png) + +无论如何,Sanic 自带的算法都会根据情况尝试响应 HTML、JSON 或 text-based 的错误。不要担心,您可以根据您的具体需求,非常简单的设置和自定义错误处理的方法。 +```` + +.. tab:: 应用查看器(App Inspector) + +```` +不管是在本地还是在远程,Sanic 都可以检查你正在运行的应用。 +```sh +sanic inspect + +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Sanic │ +│ Inspecting @ http://localhost:6457 │ +├───────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────┤ +│ │ mode: production, single worker │ +│ ▄███ █████ ██ │ server: unknown │ +│ ██ │ python: 3.10.9 │ +│ ▀███████ ███▄ │ platform: SomeOS-9.8.7 +│ ██ │ packages: sanic==22.12.0, sanic-routing==22.8.0, sanic-testing==22.12.0, sanic-ext==22.12.0 │ +│ ████ ████████▀ │ │ +│ │ │ +│ Build Fast. Run Fast. │ │ +└───────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────┘ + +Sanic-Main + pid: 999996 + +Sanic-Server-0-0 + server: True + state: ACKED + pid: 999997 + start_at: 2023-01-31T12:34:56.00000+00:00 + starts: 1 + +Sanic-Inspector-0 + server: False + state: STARTED + pid: 999998 + start_at: 2023-01-31T12:34:56.00000+00:00 + starts: 1 +``` + +并且可以使用像是 `reload`, `shutdown`, `scale` 的命令... + +```sh +sanic inspect scale 4 +``` + +... 甚至是创建你自己的! + +```sh +sanic inspect migrations +``` +```` + +.. tab:: 可扩展(Extendable) + +``` +除了 Sanic 自带的工具外,官方支持的 [Sanic 扩展](./plugins/sanic-ext/getting-started.md) 还提供了许多额外的好东西,使您的开发更加轻松。 + +- **CORS** 保护 +- 使用 **Jinja** 进行模板渲染 +- 将其他对象通过 **Dependency injection** (依赖注入)到路由处理程序中 +- 使用 **Redoc** 和/或 **Swagger** 编写 OpenAPI 文档 +- 预先定义好的**序列化函数**(eg `json` `text`)、作用于不同的路由入口(serializers) +- 请求查询参数和正文输入的**验证器**(validation) +- **自动创建** HEAD、OPTIONS 和 TRACE 入口(auto create) +- 实时**健康监控**(health monitor) +``` + +.. tab:: 开发体验(Developer Experience) + +``` +Sanic **为构建而生**。 + +从安装的那一刻起,Sanic 就包含帮助开发人员完成工作的有用工具。 + +- **单个服务器** - 在将运行生产应用程序的同一服务器上以开发模式进行本地开发 +- **自动重载** - 每次保存 Python 文件时重载正在运行的应用程序,也可在**任意目录**下自动重载,如 HTML 模板目录。 +- **调试工具** - 超级有用(而且漂亮)的[错误页面](/zh/guide/best-practices/exceptions),帮助你轻松遍历跟踪堆栈 +- **自动 TLS** - 使用 "https" 运行本地主机网站可能很困难,但是 [Sanic 让它变得简单](/zh/guide/how-to/tls) +- **简化测试** - 内置测试功能,使开发人员更容易创建和运行测试,确保服务的质量和可靠性 +- **现代 Python**- 体贴地使用类型提示,帮助开发人员获得集成开发环境体验 +``` diff --git a/guide/content/zh/organization/code-of-conduct.md b/guide/content/zh/organization/code-of-conduct.md new file mode 100644 index 0000000000..303972a49f --- /dev/null +++ b/guide/content/zh/organization/code-of-conduct.md @@ -0,0 +1,72 @@ +# 贡献者盟约行为守则 + +## 我们的保证 + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## 我们的标准 + +有助于创造积极环境的行为 +的例子包括: + +- 使用欢迎和包容的语言 +- 尊重不同的观点和经验 +- 优雅地接受建设性的评论 +- 侧重于最适合社区的内容 +- 对其他社区成员表示同情。 + +参与者不可接受的行为实例包括: + +- 性化语言或图像的使用以及不受欢迎的性关注或 + 的进展情况 +- 挑选、侮辱/贬损性评论以及个人或政治攻击 +- 公开或私下骚扰行为 +- 未经明确许可,发布他人的私人信息,例如物理或电子 + 地址 +- 职业环境中可以合理地认为不合适的其他行为 + +## 我们的责任 + +项目维护者负责澄清可接受的 +行为的标准,并且预期将在 +反应中采取适当和公平的纠正行动,应对任何不可接受的行为。 + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## 范围 + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. +代表一个项目或社区的例子包括使用官方项目电子邮件 +地址 在网上或离线活动中,通过官方社交媒体帐户发布信息,或担任指定的 +代表。 Representation of a project may be +further defined and clarified by project maintainers. + +## 执行 + +可能是通过联系项目团队Adam@sanicframework.org报告的滥用、骚扰或其他令人无法接受的行为。 所有 +申诉都将得到审查和调查,结果将产生 +被认为是必要和适合当时情况的答复。 项目组负责 +对事件报告者保密。 +具体执法政策的进一步细节可另行公布。 + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## 属性 + +本行为守则摘自[贡献者公约][homepage]第1.4版, +[http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-Covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/guide/content/zh/organization/contributing.md b/guide/content/zh/organization/contributing.md new file mode 100644 index 0000000000..1e086b7ed0 --- /dev/null +++ b/guide/content/zh/organization/contributing.md @@ -0,0 +1,166 @@ +# 贡献中 + +感谢您的兴趣! Sanic总是在寻找贡献者。 如果你觉得不舒服的贡献代码,请将文档添加到源文件中,或帮助[Sanic User Guide](https://github)。 请提供文件或执行实例,以此表示欢迎! + +我们致力于为所有人提供一个友好、安全和受欢迎的环境,不论其性别、性取向、残疾、族裔、宗教或类似的个人特征。 我们的[行为守则](https://github.com/sanic-org/sanic/blob/master/CONDUCT.md)规定了行为标准。 + +## 安装 + +要在 Sanic 上开发(主要是运行测试),强烈建议从源头安装。 + +所以假定你已经克隆了仓库,并且在工作目录中已经设置了虚拟环境,然后运行: + +```sh +pip install -e ".[dev]" +``` + +## 依赖关系变化 + +`Sanic` 不使用 `requirements*.txt` 文件来管理与此相关的任何依赖关系,以简化管理依赖关系所需的努力。 请确保您已经阅读并理解文档的下面一节,其中解释了`sanic` 如何管理`setup.py`文件中的依赖关系。 + +| 依赖类型 | 用法 | 安装 | +| ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | ----------------------------------------------------------------------------------------------- | +| 所需经费 | 智能正常运行所需的最低依赖关系 | `pip3 install -e .` | +| tests_request / extras_require['test'] | 运行 `sanic` 设备测试所需的依赖关系 | \`pip3 install -e '.[test]' | +| extras_require['dev'] | 增加捐款的额外发展要求 | \`pip3 install -e '.[dev]' | +| extras_require['docs'] | 建立和加强卫生文档所需的依赖关系 | \`pip3 install -e '.[docs]' | + +## 正在运行所有测试 + +要运行 Sanic 测试,建议像这样使用tox: + +```sh +tox +``` + +看看这么简单! + +`tox.ini`含有不同的环境。 Running `tox` without any arguments will +run all unittests, perform lint and other checks. + +## 不需要时运行 + +`tox` 环境 -> `[testenv]` + +只能执行空闲的玩家就能运行类似于环境的\`毒性': + +```sh + +tox -e py37 -v -- tests/test_config.py +# 或 +tox -e py310 -v -- tests/test_config.py +``` + +## 运行行检查 + +`tox` 环境 -> `[testenv:lin]` + +执行 `flake8` 、 `black` 和 `isort` 检查。 + +```sh +tox -e lint +``` + +## 运行类型批注检查 + +`tox` 环境 -> `[testenv:type-checking]` + +执行`mypy`检查。 + +```sh +tox -e 类型检查 +``` + +## 运行其他检查 + +`tox` 环境 -> `[testenv:check]` + +执行其他检查。 + +```sh +tox -e 检查 +``` + +## 运行静态分析 + +`tox` 环境 -> `[testenv:security]` + +执行静态分析安全扫描 + +```sh +tox -e 安全 +``` + +## 运行文档智能检查 + +`tox` 环境 -> `[testenv:docs]` + +对文档进行智能检查 + +```sh +tox -e 文档 +``` + +## 代码样式 + +为了保持代码的一致性,Sanic使用以下工具: + +1. [isort](https://github.com/timothycrosley/isort) +2. [black](https://github.com/python/black) +3. [flake8](https://github.com/PyCQA/flake8) +4. [slotscheck](https://github.com/ariebovenberg/slotscheck) + +### isort + +`isort` 排序的 Python 导入。 它将进口分为三类,按字母顺序排列: + +1. 内置的 +2. 第三方 +3. 特定项目 + +### 黑色 + +`black` 是一个 Python 代码格式。 + +### 火焰8 + +`flake8` 是一个 Python 风格指南,将以下工具包装成一个工具: + +1. PyFlakes +2. pycodestyle +3. Ned Batchid's McCabe 脚本 + +### slotscheck + +`slotscheck` ensures that there are no problems with `__slots__` (e.g., overlaps, or missing slots in base classes). + +`isort`, `black`, `flake8`, `slotscheck` 检查是在 `tox` 链接检查期间进行的。 + +**最简单** 使您的代码符合要求的方法是在提交之前运行以下内容: + +```bash +做得很好 +``` + +欲了解更多详情,请参阅[tox documentation](https://tox.readthedocs.io/en/ index.html)。 + +## 拉取请求 + +所以拉取请求批准规则非常简单: + +1. 所有合并请求必须通过单元测试。 +2. 所有合并请求必须经过核心开发团队中至少一个当前成员的审查和批准。 +3. 所有合并请求都必须通过 flake8 检查。 +4. 所有合并请求必须匹配 `isort` 和 `black` 要求。 +5. 所有合并请求必须为 **PROPERLY** 类型,除非给予豁免。 +6. 所有合并请求必须与现有代码一致。 +7. 如果您决定从任何通用接口中移除/更改任何内容,则应根据我们的[废弃政策](https://sanicframework.org/en/guide/project/policies.html#disposition)随附一个废弃的消息。 +8. 如果你实现了一个新功能,你应该至少有一个单元测试来伴随它。 +9. 一个例子必须是: + - 如何使用 Sanic 的例子 + - 如何使用 Sanic 扩展 + - 如何使用 Sanic 和异步库的例子 + +## 文件 + +回车. 我们正在重新编写我们的文件,以便改变这种情况。 diff --git a/guide/content/zh/organization/policies.md b/guide/content/zh/organization/policies.md new file mode 100644 index 0000000000..7033022360 --- /dev/null +++ b/guide/content/zh/organization/policies.md @@ -0,0 +1,80 @@ +# 政策 + +## Versioning + +Sanic 使用 [日历版本](https://calver.org/), 别名“calver”。 为了更具体地说明以下模式: + +``` +YY.MMM.MMRO +``` + +一般情况下,版本在他们的 `YY.MM` 表格中被引用。 `MICRO`数字表示一个递增补补版,从`0`开始。 + +## 报告脆弱性 + +如果你发现安全脆弱性,我们请你**不**在GitHub 上创建一个问题。 相反,请在社区论坛上[发送消息给core-devs](https://community.sanicframework.org/g/core-devs)。 登录后,您可以通过单击消息按钮向核心开发人员发送消息。 + +或者,您可以在 Discord 上向Adam Hopkins发送私信。 在 [Sanic Discord 服务器](https://discord.gg/RARQzAEMA) 查找他。 + +这将有助于在小组能够处理和解决问题之前不予公布。 + +## 发布计划 + +计划每年释放四(4)人:3月、6月、9月和12月。 因此,每年发布4种版本:`YY.3`、`YY.6`、`YY.9`和`YY.12`。 + +此发布时间表规定: + +- 一种可预见的释放方式, +- 相对较短的开发窗口,允许定期发布功能, +- 控制 [deprecations](#废弃),及 +- 与一年一度的LTS保持稳定性。 + +我们还使用年度发行周期,同时使用 [S.C.O.P.E.](./ scope.md) + +### 长期支助诉临时释放案 + +12月,Sanic每年发布一次长期支持释放(又名“LTS”)。 LTS 版本在 **24 个月**中获得错误修复和安全更新。 全年的临时释放每三个月进行一次,并在随后的释放之前得到支持。 + +| 版本 | 发布 | LTS | 支持的 | +| ------------------------------------- | ---------- | --------- | --- | +| 24.12 | 2024-12-31 | 到 2026-12 | ✅ | +| 24.6 | 2024-06-30 | | ⚪ | +| 23.12 | 2023-12-31 | 2025-12 | ☑️ | +| 23.6 | 2023-07-25 | | ⚪ | +| 23.3 | 2023-03-26 | | ⚪ | +| 22.12 | 2022-12-27 | | ☑️ | +| 22.9 | 2022-09-29 | | ⚪ | +| 22.6 | 2022-06-30 | | ⚪ | +| 22.3 | 2022-03-31 | | ⚪ | +| 21.12 | 2021-12-26 | | ⚪ | +| 21.9 | 2021-09-30 | | ⚪ | +| 21.6 | 2021-06-27 | | ⚪ | +| 21.3 | 2021-03-21 | | ⚪ | +| 20.12 | 2020-12-29 | | ⚪ | +| 20.9 | 2020-09-30 | | ⚪ | +| 20.6 | 2020-06-28 | | ⚪ | +| 20.3 | 2020-05-14 | | ⚪ | +| 19.12 | 2019-12-27 | | ⚪ | +| 19.9 | 2019-10-12 | | ⚪ | +| 19.6 | 2019-06-21 | | ⚪ | +| 19.3 | 2019-03-23 | | ⚪ | +| 18.12 | 2018-12-27 | | ⚪ | +| 0.8.3 | 2018-09-13 | | ⚪ | +| 0.7.0 | 2017-12-06 | | ⚪ | +| 0.6.0 | 2017-08-03 | | ⚪ | +| 0.5.4 | 2017-05-09 | | ⚪ | +| 0.4.1 | 2017-02-28 | | ⚪ | +| 0.3.1 | 2017-02-09 | | ⚪ | +| 0.2.0 | 2017-01-14 | | ⚪ | +| 0.1.9 | 2016-12-25 | | ⚪ | +| 0.1.0 | 2016-10-16 | | ⚪ | + +☑️ = security fixes\ +:check_mark_buton: = 完全支持\ +⚪ = 不支持 + +## 废弃的 + +在某个功能被废弃之前,或者对API引入破解的更改。 它应予公布,并应在两个释放周期内以过时警告出现。 LTS 版本中不应放弃使用。 + +在绝对必要的情况下,有可能在这些准则之外发生打破变化或取消特征的情况。 这种情况应该是少见的。 例如,如果没有其他办法来减少一个重大的安全问题,就可能出现这种情况。 diff --git a/guide/content/zh/organization/scope.md b/guide/content/zh/organization/scope.md new file mode 100644 index 0000000000..680c5c7a40 --- /dev/null +++ b/guide/content/zh/organization/scope.md @@ -0,0 +1,257 @@ +# 社区组织政策电子手册 + +.. attrs:: +:class: is-size-7 + +``` +_2019年12月,版本 1_ +``` + +## 目标 + +围绕萨尼语项目建立一个可持续的、由社区推动的组织,促进:(1) 稳定和可预测性,(2) 快速迭代和增强周期, (3) 外部贡献者的参与,(4) 通盘可靠的软件,和(5) 社区成员的安全有益环境。 + +## 概览 + +这项政策是萨尼语社区组织的管理模式。 上海合作组织是一个优秀的社区组织,负责该组织通过的所有项目。 任何对其中一个项目感兴趣的人都可以加入社区。 为社区或项目作出贡献,并参与决策进程。 本文件介绍了如何进行这种参与以及如何在项目界内确定赚取收入的长处。 + +## 结构 + +上海合作组织有多个**项目**。 每个项目都有一个单一的GitHub 仓库,位于Sanic社区保护伞下。 这些项目由 **users**、由 **contributors**开发,由 **core 开发者**、由**发行经理**发布,最终由**指导理事会**监督。 如果这听起来类似于Python项目和 PEP 8016 ,因为它是故意设计的。 + +## 作用和责任 + +### 用户 + +用户是需要项目的社区成员。 他们是下载和安装软件包的开发者和人员。 用户是社区中**最重要的**成员\*\*,没有他们,项目就毫无用处。 任何人都可以是用户,项目通过的许可证应该是适当的开源许可证。 + +_上海合作组织请其用户尽可能多地参与该项目和社区。 + +用户贡献使项目小组能够确保满足这些用户的需要。 共同用户贡献包括(但不限于): + +- 宣传项目(如网站上的链接和提高对口文字的认识) +- 从新用户角度向开发者通报优点和弱点。 +- 提供道德支持 (“谢谢”很长时间) +- 提供财政支持 (软件是开放源码,但其开发者需要吃) + +继续与上海合作组织、其项目和社区接触的用户往往越来越多地参与进来。 如下一节所述,这些用户可能会成为贡献者。 + +### 贡献者 + +捐助方是社区成员,他们以具体方式为一个或多个项目作出贡献。 任何人都可以成为捐助者,捐助可以采取多种形式。 捐款和需求由每个项目根据捐款政策分别管理。 + +**没有对项目的承诺**,**没有具体的技能要求**,**没有选择过程**。 + +除了作为用户采取的行动外,捐款者还可能会发现自己做以下一项或多项工作: + +- 支持新用户(现有用户往往是支持新用户的最佳用户) +- 报告错误 +- 鉴别要求 +- 提供图形和网页设计 +- 编程中 +- 示例使用 +- 协助项目基础设施 +- 撰写文档 +- 修复错误 +- 添加功能 +- 提供建设性意见和参与社区对话 + +参与者通过GitHub 和社区论坛参与这些项目。 他们通过拉动请求提交对项目本身的更改,一般社区将考虑将其纳入项目。 社区论坛是在作出首次贡献时寻求帮助的最适当场所。 + +的确,贡献者最重要的作用之一可能是**只是参与社区对话**。 关于项目方向的大多数决定都是以协商一致方式作出的。 下文将更详细地讨论这一问题。 然而,一般而言,这种情况都是如此。 这有助于项目的健康和方向,使参与者能够(在行为守则范围内)**自由地说** 和**表达他们的意见和经验** 以帮助推动建立共识。 + +随着捐助方获得经验和熟悉一个项目,他们在社区内的形象和对社区的承诺将增加。 在某个阶段,他们可能会被提名为核心开发者团队。 + +### 核心开发者 + +上海合作组织下属的每个项目都有自己的核心开发人员。 他们是该项目的负责人。 + +_什么是核心开发者?_ + +核心开发者是社区成员,他们已经表明,他们致力于通过与社区的持续接触继续发展项目。 作为一个核心开发者,捐助方能够更容易地开展与项目有关的活动,让他们直接获得项目的资源。 他们可以直接对项目仓库进行更改,而不必通过分叉的合并请求提交更改。 + +这并不意味着核心开发者可以自由地做他们想要做的事情。 事实上,核心开发者对最终发布一揽子计划没有比贡献者更直接的权力。 虽然这一荣誉确实表明社区中表现出对项目宗旨和目标的健康尊重的有价值的成员。 她们的工作继续由社区审查,然后才被正式释放。 + +_核心开发者在项目上可以做什么?_ + +每个项目对这一角色的定义可能略有不同。 然而,仍然存在着这种情况。 这种指定的一般用法是,个人在社区内已经发展到一定程度的信任程度,从而使他们现在能够得到某种程度的控制。 其形式是向无保护的分支推送权利以及在批准拉取请求时有发言权的能力。 + +这些项目采用各种交流机制,以确保所有捐款都得到整个社区的审查。 这包括GitHub 以及社区论坛提供的工具。 当邀请贡献者成为核心开发者时。 他们应该熟悉各种工具和工作流程,作为用户,然后作为贡献者。 + +_如何成为核心开发者?_ + +任何人都可以成为核心开发者; 除了表现出作为团队成员积极参加项目的意愿和能力之外,没有其他特殊要求。 + +通常,潜在的核心开发商需要表明他们了解项目、其目标和战略。 他们还将在一段时间内为该项目作出宝贵贡献。 然而,在资格方面没有**技术或其他技能**要求。 + +新的核心开发者可以在任何时候被任何现有的核心开发者\*\*提名。 指导委员会将每年至少举行两次投票(4月和10月)。 投票应以无记名投票方式进行。 该项目的每个现有核心开发者获得的票数相当于选票上被提名者的人数。 例如,如果有四个被提名者,那么现有的每个核心开发者都有四票。 核心开发者可以按自己选择的方式投票,但不能投票一次以上的被提名者。 被提名人必须得到选票数(而不是合格选票数)三分之二的批准。 一旦得到核心开发者的接受,指导委员会就有责任批准和最后确定提名。 指导委员会无权确定被提名人是否有足够的价值来获得核心开发者头衔。 但是,如果社区的健康有此需要,他们确实保留否决投票的权利。 + +一旦进行了投票,将在社区论坛上公布汇总投票结果。 被提名人有权要求对反对他们的任何否决作出解释。 不被接纳为核心开发者的被提名人将来可能再次被提名。 + +必须认识到,作为核心开发者是一种特权,而不是一种权利。 必须获得这项特权,一旦获得这项特权,指导委员会就可以在极端情况下取消(见下一节)。 然而,在正常情况下,只要个人愿意继续与项目和社区接触,核心开发商的所有权就存在。 + +项目委员会的成员显示对项目的贡献超过平均水平,特别是在其战略方向和长期健康方面, 可被提名为指导委员会成员或发布管理员。 下文介绍了这一作用。 + +_核心开发者的权利和责任是什么? _ + +正如所讨论的那样,将要作出的大多数决定都是通过建立共识作出的。 在某些情况下,某一问题变得更具争议性或需要作出重大决定。 发布主管或指导委员会可决定(或需要)实施成果文件进程,下文将更详细地概述。 + +核心开发者也有责任在社区治理中发表意见。 所有项目的所有核心开发者都有能力被提名参加指导委员会并在其选举中投票。 + +本政策(“SCOPE”)只能在三分之二活跃核心开发商的授权下修改。 但在收养后的头六(6)个月内, 核心开发者保留在简单多数活跃的核心开发者授权下进行更改的权利。 + +_核心开发者不活跃时怎么办?_ + +希望所有核心开发商都能定期参与并继续积极参加其项目。 然而,人们也认识到,这种承诺可能不现实或不时不可能。 + +因此, 指导委员会有责任鼓励参与,如果核心开发者不再愿意或不再有能力参与,则有责任使他们处于不活跃状态。 其主要目的是\*\*不是为了惩罚一个人的行为, 但有助于发展进程继续为那些仍然活跃的国家服务。 + +为此目的,“不活跃”的核心开发者不应拥有对储存库的权利,也不应参与任何投票。 要有资格在选举中投票,核心开发人员在上次计划的项目发布时必须已经活跃\*\*。 + +不活跃的成员可随时要求指导委员会恢复其地位。 在提出这种要求后,指导委员会应使核心开发商再次活跃。 + +如果个人知道他们在一段时间内无法维持其现役地位,则请他们与指导委员会联系,并在必要时宣布不再活动。 + +“积极的”核心开发者是在过去六个月中以有意义的方式参与的个人。 任何进一步的定义都属于指导委员会的酌处权。 + +### 发布管理器 + +核心开发者只能在无保护的分支上做出承诺和合并。 “主人”分支和其他受保护分支由该项目的释放管理小组控制。 单元管理人员应由核心开发小组从核心开发小组中选出,并应有一个完整的发放周期。 + +每个核心开发者团队可以决定每个发行周期有多少发行管理员。 人们深受鼓舞的是,至少有两名释放管理人员负责一个释放周期,以帮助分清责任,而不要把太多的精力强加给一个人。 然而,也不应有太多的管理人员,以至于他们的努力受到阻碍。 + +发布管理小组的主要职责包括: + +- 通过监测和促进技术讨论推进发展周期 +- 建立发布日历并执行发布包所需的操作 +- 批准对主分支和其他受保护分支的合并请求 +- 合并合并请求到主分支和其他受保护分支 + +释放管理员**无权否决或拒绝合并**在其他情况下符合贡献标准且已被社区接受的合并请求。 他们没有责任决定应该制定什么措施。 而是社区的决定正在执行,项目正在向前推进。 + +可能需要时常作出无法通过协商一致做出的决定。 在这种情况下,释放管理人员有权要求将决定移送《索取资料书》进程。 这种做法不应定期发生(除非如下文所述的要求),应鼓励使用这种做法,以利于采取更多的社区建立共识战略。 + +因为并非所有项目都有相同的要求, 关于某一项目的排放管理人员的具体规定应在本政策的附录或项目的贡献准则中加以规定。 + +如有必要,指导委员会有权撤换一名因其职责或其他正当理由而失职的释放管理员。 + +### 指导理事会 + +指导委员会是由那些被确定为“项目所有人”并控制上海合作组织资源和资产的个人组成的管理机构。 其最终目标是通过消除障碍和在需要时协助成员确保项目的顺利运作。 预计他们将在社区中经常发出声音。 + +_指导委员会能做些什么?_ + +指导委员会成员**个别拥有比任何其他核心开发者更多的权限**, 而且对某一项目无任何额外的决定、承诺、合并或类似权利。 + +然而,作为一个机构,指导委员会具有以下能力: + +- 接受、汇款和拒绝所有RFCs +- 强制执行社区行为守则 +- 管理社区资产,如仓库、服务器、论坛、集成服务等等(或将这种权力授予其他人) +- 酌情采取本政策给予核心开发商的任何其他强制执行措施,使核心开发商处于非活动状态, 包括在极端情况下移除核心开发者 +- 通过或从社区伞式组织中删除项目 + +人们高度鼓励指导委员会尽可能并酌情将其权力下放给其他愿意的社区成员。 + +指导委员会**没有权力**改变这项政策。 + +_有多少成员在指导委员会? + +四、结论和建议 + +虽然它似乎像一个四票的委员会,但有可能陷入僵局,无法打破多数票。 指导委员会不鼓励尽可能多地进行表决。 相反,它应该努力以协商一致方式开展工作,并在需要就某一事项进行表决时需要三次同意的表决。 + +_成员在指导委员会任职多长时间?_ + +从1月开始,任期为两个历年。 任期将错开,以便每年有两名成员从前一年的理事会继续任职。 + +因此,就职投票应有两个任期两年的职位和两个任期一年的职位。 + +可以使用的任期不受限制,个人可以连续任职。 + +_谁来管理指导理事会?_ + +指导委员会选出后,该小组应集体决定由一人担任主席。 主席对指导委员会的任何其他成员没有任何其他权利或权力。 + +主席的作用仅仅是协调人和调解人。 预期主席将确保所有治理进程都得到遵守。 这一职位更具行政性和文秘性,预计主席将制定小组的议程并协调小组的讨论。 + +_理事会成员是如何选举的?_ + +每个项目每年一次,**所有合格的核心开发者** 应有权选举指导委员会成员。 + +提名应从9月1日起开放,并于9月30日结束。 此后,表决应于10月1日开始,10月31日结束。 在该年度6月份发布《Sanic框架》之日活跃的每个核心开发商都有资格获得指导委员会每个空缺席位的一票。 为了明确起见,要有投票资格,核心开发者**不需要**就必须是Sanic Framework的核心开发者。 但实际上当时只是在其各自的项目范围内积极开展工作。 + +最高得票者将被宣布为获胜者。 如果有任何矛盾之处,受牵连的被提名人自己在随意作出决定之前解决争端,就会受到极大鼓舞。 + +关于指导委员会的首次投票,两名选票最多者任期两年。 接下来的两个选票接受国应占一年的席位。 + +成为指导委员会的合格候选人; 该个人必须是在过去12个月中至少一个项目上处于活跃状态的核心开发者。 + +_如果有空缺怎么办?_ + +如果指导委员会在任期内存在空缺, 然后应向上次选举中获得最高票数的人提供完成剩余任期的机会。 如果找不到这种方法, 指导委员会可决定填补该席位的最适当行动方针(通过任命、表决或其他方式)。 + +如果指导委员会的一名成员不活跃, 然后应立即将此人从指导委员会除名,该职位应出缺。 + +在极端情况下, 所有核心开发者的机构都有权以所有有资格投票的核心开发者的三分之二多数表决取消指导委员会的一名成员。 + +_指导委员会应如何开展工作?_ + +指导委员会应尽可能公开开展工作和讨论。 应允许社区的任何成员参加与他们的对话。 然而,有时可能有必要或适合私下进行讨论。 选择适当的会话地点是主席行政职责的一部分。 + +虽然如何运作的具体规定不属于该政策的范围, 令人感到鼓舞的是,指导委员会试图每季度至少举行一次“实时”讨论。 这可以通过电视会议、实况聊天或其他适当手段来实现。 + +## 支持 + +鼓励社区所有参与者在项目管理基础设施内向用户提供支助。 提供这种支助是促进社区发展的一种方式。 寻求支助者应认识到,项目内的所有支助活动都是自愿性的,因此在时间允许的情况下提供。 因此,要求有保证的反应时间或结果的用户应设法从社区成员那里购买支持合同。 然而,对于那些愿意按照自己的条件参与项目的人来说,则是如此。 社区支助渠道是最理想的,愿意帮助其他用户。 + +## 决策过程 + +有关项目未来的决定是通过与社区所有成员讨论作出的。 从最新用户到经验最丰富的会员。 每个人都有发言权。 + +所有非敏感项目管理讨论都在社区论坛或其他指定渠道进行。 偶尔还会私下进行敏感的讨论。 + +为了确保该项目不会因无休止的讨论和持续表决而陷入僵局,该项目实行了**迟迟不能达成共识**的政策。 这使得大多数决定不必经过正式表决就能作出。 对于任何**重大决定** (下面界定),都有一个单独的征求意见程序。 + +### 技术决定 + +合并请求和技术决定一般应分为以下几类。 + +- **Routine**: 文档修复,用于清理或额外测试的代码更改。 功能没有变化。 +- **Minor**: 更改代码基础以修复一个bug或引入一个微不足道的功能。 没有中断的更改。 +- **Major**: 对代码基础的任何更改都会破坏或废弃现有的API, 以非微不足道的方式更改操作, 或添加一个重要的功能。 + +通常,发布管理员有责任确保在合并之前对储存库的更改得到适当的授权。 + +释放管理人员保留个别审查和接受符合编码质量标准的例行决定的权力,而无需额外投入。 + +### 延迟的协议 + +决策(无论是由社区还是指导委员会)通常涉及下列步骤: + +- 建 议 +- 讨论情况 +- 投票(如果不能通过讨论达成协商一致意见) +- 决 定 + +任何社区成员都可以提出建议,供社区考虑。 为了发起关于新想法的讨论,他们应在共同体论坛的适当渠道上发表信息, 或者在GitHub上提交一个实现想法的拉取请求。 这将促使人们进行审查,并在必要时讨论这一想法。 + +这次审查和讨论的目的是获得对捐款的批准。 由于项目界的大多数人都有共同的愿景,达成共识往往没有必要进行讨论。 + +一般来说,只要无人明确反对某项建议或修补,就会被认为得到了社区的支持。 这就是说,那些没有明确表示其意见的人默示同意执行该建议。 + +迟迟不达成共识是上海合作组织内一个非常重要的概念。 正是这一进程使大批人能够有效地达成共识。 因为对某项建议没有异议的人不需要花时间说明其立场,其他人不需要花时间阅读这些信息。 + +为了使迟迟不达成的协商一致意见行之有效, 有必要在假定对该提案没有异议之前留出适当的时间。 这在某种程度上取决于情况,但一般认为72小时是合理的。 这项要求确保每个人都有足够的时间阅读、消化和回应这项建议。 选择这一时限是为了尽可能包容所有参与者,无论其地点和时间如何。 讨论主持人(不论是主席还是发布经理) 应负责确定达成这种协商一致意见的适当时间。 + +如上所述,关于所谓的例行决定,释放管理人员有权在较短的时间内作出决定。 在这种情况下,将会暗示迟迟不能达成共识。 + +### 请求评论 (RFC) + +指导委员会负责监督改革成果框架进程。 这一进程仍然可以向社区所有成员进行辩论。 并应留出充分时间审议某项提案,并使成员能够答复和进行有意义的讨论。 + +最后决定由指导委员会作出。 然而,指导委员会通过的决定与社区可能存在的任何共识背道而驰,令人强烈沮丧。 如果共识与总体项目和社区目标之间不时发生冲突,就可能发生这种情况。 + +将按照指导委员会的规定,以公开方式向指导委员会提交一份征求意见。 辩论应继续进行,并由指导委员会和具体由主席主持。 + +在指导委员会认为适当的情况下,区域渔业委员会的程序可能被放弃,转而赞成迟迟不达成共识。 diff --git a/guide/content/zh/plugins/sanic-ext/configuration.md b/guide/content/zh/plugins/sanic-ext/configuration.md new file mode 100644 index 0000000000..58ec95a2c9 --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/configuration.md @@ -0,0 +1,288 @@ +--- +title: Sanic 扩展 - 配置 +--- + +# 配置 + +可使用[你可以配置Sanic](../../guide/deplment/configuration.md)的所有相同方式配置Sanic 扩展。 这使得配置Sanic扩展变得非常容易。 + +```python +app = Sanic("MyApp") +app.config.OAS_URL_PREFIX = "/apidocs" +``` + +然而,还有一些其他配置方案应加以考虑。 + +## 手动`extend` + +.. 列: + +``` +尽管Sanic 扩展会自动附加到您的应用程序,但您可以手动选择 `extend`。 当你这样做时,你可以传递所有的配置值作为关键字参数(小写)。 +``` + +.. 列: + +```` +```python +app = Sanic("MyApp") +app.extend(oas_url_prefix="/apidocs") +``` +```` + +.. 列: + +``` +或者,也可以同时将其作为一个单一的词语。 +``` + +.. 列: + +```` +```python +app = Sanic("MyApp") +app.extend(config={"oas_url_prefix": "/apidocs"}) +``` +```` + +.. 列: + +``` +由于配置设置的名称无法被IDE发现,这两种解决办法都受到影响。 因此,您也可以使用一个类型注释的对象。这将有助于开发体验。 +``` + +.. 列: + +```` +```python +来自sanic_ext import Config + +app = Sanic("MyApp") +app.extend(config=Config(oas_url_prefix="/apidocs")) +``` +```` + +## 设置 + +.. 注: + +```` +通常最容易改变申请的方式(因为它们可能不会因环境而改变), 将它们直接设置在“应用”上。 onfig`对象。 + +只需使用这里显示的配置密钥的大写版本: + +```python +app = Sanic("MyApp") +app.config.OAS_URL_PREFIX = "/apidocs" +``` +```` + +### `cors` + +- **类型**: `bool` +- **默认**: `True` +- **描述**:是否启用 CORS 保护 + +### `cors_allow_headers` + +- **类型**: `str` +- **默认**: `"*"` +- **描述**: 标题值: `access-control-allow-headers` + +### `cors_always_send` + +- **类型**: `bool` +- **默认**: `True` +- **描述**: 是否总是发送头部: `access-control-allow-origin` + +### `cors_automatic_options` + +- **类型**: `bool` +- **默认**: `True` +- **描述**:是否为那些没有\*定义过一个的路由自动生成 "OPTIONS" 端点 + +### `cors_expose_headers` + +- **类型**: `str` +- **默认**: `""` +- **描述**: 标题值: `access-control-expose-headers` + +### `cors_max_age` + +- **类型**: `int` +- **默认**: `5` +- **描述**: 标题值: `access-control-max-age` + +### `cors_methods` + +- **类型**: `str` +- **默认**: `""` +- **描述**: 标题值: `access-control-access-allow-methods` + +### `cors_origins` + +- **类型**: `str` +- **默认**: `""` +- **描述**: 标题值: `access-control-allow-origin` + +.. 警告:: + +``` +如果你把`*`放在这里,请非常小心。 不要这样做,除非你知道你正在做什么,因为它可能是一个安全问题。 +``` + +### `cors_send_wildcard` + +- **类型**: `bool` +- **默认**: `False` +- **描述**:是否发送通配符来源而不是传入请求来源 + +### `cors_supports_credentials` + +- **类型**: `bool` +- **默认**: `False` +- **描述**: 标题值: `access-control-allow-credentials` + +### `cors_vary_header` + +- **类型**: `bool` +- **默认**: `True` +- **描述**:是否添加 `vary` 标题 + +### `http_all_methods` + +- **类型**: `bool` +- **默认**: `True` +- **描述**: 添加 HTTP `CONNECT` 和 `TRACE` 方法为允许方法 + +### `http_auto_head` + +- **类型**: `bool` +- **默认**: `True` +- **描述**:自动将 `HEAD` 处理程序添加到任何 `GET` 路由 + +### `http_auto_options` + +- **类型**: `bool` +- **默认**: `True` +- **描述**:自动将 `OpTIONS` 处理程序添加到任何路由中,无需此说明 + +### `http_auto_trace` + +- **类型**: `bool` +- **默认**: `False` +- **描述**:自动将 `TRACE` 处理程序添加到任何路由中无需添加。 + +### `oas` + +- **类型**: `bool` +- **默认**: `True` +- **描述**:是否启用 OpenAPI 规范生成 + +### `oas_autodoc` + +- **类型**: `bool` +- **默认**: `True` +- **描述**:是否从路由函数的 docstring 自动提取OpenAPI 详细信息 + +### `oas_ignore_head` + +- **类型**: `bool` +- **默认**: `True` +- **描述**: WHen `True`, 它将不会在 OpenAPI 规范中添加 `HEAD` 端点 + +### `oas_ignore_options` + +- **类型**: `bool` +- **默认**: `True` +- **描述**: WHen `True`, 它不会在 OpenAPI 规范中添加 `OPTIONS` 端点 + +### `oas_path_to_redoc_html` + +- **类型**: `可选的[str]` +- **默认**: `None` +- **描述**:HTML文件路径以覆盖现有的 Redoc HTML + +### `oas_path_to_swagger_html` + +- **类型**: `可选的[str]` +- **默认**: `None` +- **描述**:要覆盖现有的 Swagger HTML 文件的路径 + +### `oas_ui_default` + +- **类型**: `可选的[str]` +- **默认**: \`"redoc"" +- **描述**:哪些OAS 文档将用于bare `oas_url_prefix`端点;当`None`时,该位置将没有文档。 + +### `oas_ui_redoc` + +- **类型**: `bool` +- **默认**: `True` +- **描述**:是否启用 Redoc 界面 + +### `oas_ui_swagger` + +- **类型**: `bool` +- **默认**: `True` +- **描述**:是否启用 Swagger 界面 + +### `oas_ui_swagger_version` + +- **类型**: `str` +- **默认**: `"4.1.0"` +- **描述**:要使用哪个Swagger版本 + +### `oas_uri_to_config` + +- **类型**: `str` +- **Default**: `"/swagger-config"` +- **描述**:为 Swagger 配置服务的路径 + +### `oas_uri_to_json` + +- **类型**: `str` +- **Default**: `"/openapi.json"` +- **描述**:提供 OpenAPI JSON 的路径 + +### `oas_uri_to_redoc` + +- **类型**: `str` +- **默认**: \`"/redoc"" +- **描述**:Redoc 路径 + +### `oas_uri_to_swagger` + +- **类型**: `str` +- **Default**: `"/swagger"` +- **描述**:到 Swagger 的路径 + +### `oas_url_prefix` + +- **类型**: `str` +- **Default**: `"/docs"` +- **描述**:所有OA文档都有意附加的蓝图的 URL 前缀 + +### `swagger_ui_configuration` + +- **Type**: `Dict[str, Any]` +- **默认**: "{"apisSorter": "alph", "operationsSorter": "alpha", "docExpassion": "full"}" +- **描述**: 即将服务到前端的 Swagger 文档 + +### `templating_enable_async` + +- **类型**: `bool` +- **默认**: `True` +- **描述**:是否在 Jinja `Environment` 中设置 `enable_async` + +### `templating_path_to_templates` + +- **Type**: \`Union[str, os.PathLike, Sequence[Union[str, os.PathLie]]] +- **默认**: `templates` +- **描述**:单一路径,或多个路径到您的模板文件所在位置 + +### `Trace_excluded_headers` + +- **Type**: `序列[str]` +- **默认**: `("authorization", "cookie")` +- **描述**:哪个头应该从对 `TRACE` 请求的响应中停止 diff --git a/guide/content/zh/plugins/sanic-ext/convenience.md b/guide/content/zh/plugins/sanic-ext/convenience.md new file mode 100644 index 0000000000..a498b4249e --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/convenience.md @@ -0,0 +1,134 @@ +--- +title: 无声扩展 - 便捷性 +--- + +# 便捷性 + +## 固定序列转换器 + +.. 列: + +``` +在开发应用程序时,往往会有某些路径总是返回同样的响应。 如果情况如此,您可以预定义返回序列转换器和端点。 然后所有需要返回的都是内容。 +``` + +.. 列: + +```` +```python +from sanic_ext import serializer + +@app.get("/") +@serializer(text) +async def hello_world(request, name: str): + if name.isnumeric(): + return "hello " * int(name) + return f"Hello, {name}" +``` +```` + +.. 列: + +``` +`序列化器`装饰器也可以添加状态码。 +``` + +.. 列: + +```` +```python +from sanic_ext import serializer + +@app.post("/") +@serializer(text, status=202) +async def create_something(request): + ... +``` +```` + +## 自定义序列转换器 + +.. 列: + +``` +使用 `@serializer` 装饰器,您也可以传递您自己的自定义函数只要他们返回一个有效的类型(`HTTPResonse`)。 +``` + +.. 列: + +```` +```python +def message(retval, request, action, status): + return json( + { + "request_id": str(request.id), + "action": action, + "message": retval, + }, + status=status, + ) + +@app.post("/") +@serializer(message) +async def do_action(request, action: str): + return "This is a message" +``` +```` + +.. 列: + +``` +现在,返回一个字符串应该返回一个很好的序列化输出。 +``` + +.. 列: + +```` +```python +$ curl localhost:8000/eat_cookies -X POST +own + "request_id": "ef81c45b-235c-46ddd-b50f8fa77f9", + "action": "eat_cookies", + "message": "这是一个消息" +} + +``` +```` + +## 请求计数器 + +.. 列: + +``` +Sanic 扩展有一个子类的 `Request` ,可以设置来自动跟踪每个工人进程处理的请求数量。 为了启用此功能,您应该将 ' CountedRequest` 类传递给您的应用程序contrator。 +``` + +.. 列: + +```` +```python +来自sanic_ext import CountedRequest + +app = Sanic(...request_class=CountedRequest) +``` +```` + +.. 列: + +``` +您现在将能够访问在工人整个过程中服务的请求数量。 +``` + +.. 列: + +```` +```python +@app.get("/") +async def handler(request: CountedRequest): + return json({"count": request.count}) +``` +```` + +如果可能,请求计数也将添加到[工人状态](../../guide/deplement/manager.md#worker-state)。 + +![](https://user-images.githubusercontent.com/166269/190922460-43bd2cfc-f81a-443b-b84f-07b6ce475cbf.png) diff --git a/guide/content/zh/plugins/sanic-ext/custom.md b/guide/content/zh/plugins/sanic-ext/custom.md new file mode 100644 index 0000000000..f33d0808e6 --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/custom.md @@ -0,0 +1,92 @@ +--- +title: Sanic 扩展 - 自定义 +--- + +# 自定义扩展 + +可以创建您自己的自定义扩展。 + +22.9版本添加了 `Extend.register` [method](#extension-preregistration)。 这使得向应用程序添加自定义扩展变得非常容易。 + +## 扩展的构成 + +所有的扩展都必须继承自 Extension 类。 + +### 必填 + +- `name`: 按惯例, 名称是一个纯小写字符串 +- `startup`: 当扩展被添加时运行的方法 + +### 可选项 + +- `label`: 一个在 MOTD 中返回有关扩展的附加信息的方法 +- `included`: 一个返回布尔值的方法,用于确定扩展是否应该启用(例如,可以用于检查配置状态) + +### 示例 + +```python +from sanic import Request, Sanic, json +from sanic_ext import Extend, Extension + +app = Sanic(__name__) +app.config.MONITOR = True + +class AutoMonitor(Extension): + name = "automonitor" + + def startup(self, bootstrap) -> None: + if self.included(): + self.app.before_server_start(self.ensure_monitor_set) + self.app.on_request(self.monitor) + + @staticmethod + async def monitor(request: Request): + if request.route and request.route.ctx.monitor: + print("....") + + @staticmethod + async def ensure_monitor_set(app: Sanic): + for route in app.router.routes: + if not hasattr(route.ctx, "monitor"): + route.ctx.monitor = False + + def label(self): + has_monitor = [ + route + for route in self.app.router.routes + if getattr(route.ctx, "monitor", None) + ] + return f"{len(has_monitor)} endpoint(s)" + + def included(self): + return self.app.config.MONITOR + +Extend.register(AutoMonitor) + +@app.get("/", ctx_monitor=True) +async def handler(request: Request): + return json({"foo": "bar"}) +``` + +## 扩展预注册 + +.. 列: + +``` +`Extend.register` 简化了自定义扩展的添加。 +``` + +.. 列: + +```` +```python +from sanic_ext import Extend, extension + +class MyCustomExtension(Extension): + + +Extend.register(MyCustomExtencion()) +``` +```` + +_添加于 v22.9_ diff --git a/guide/content/zh/plugins/sanic-ext/getting-started.md b/guide/content/zh/plugins/sanic-ext/getting-started.md new file mode 100644 index 0000000000..e5afa6da06 --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/getting-started.md @@ -0,0 +1,92 @@ +--- +title: Sanic 扩展 - 开始使用 +--- + +# 正在开始 + +Sanic 扩展是由 SCO开发和维护的 _官方支持_ 插件。 这个项目的主要目标是增加额外的功能,帮助Web API 和Web 应用程序开发更容易。 + +## 功能 + +- CORS 保护 +- 使用 Jinja 渲染模板 +- 依赖注入路由处理 +- 与 Redoc 或 Swagger 的 OpenAPI 文档 +- 预定义的端点特定响应序列转换器 +- 请求查询参数和实体输入验证 +- 自动创建 `HEAD`, `OPTIONS` 和 `TRACE` 终点 + +## 最低要求 + +- **Python**: 3.8+ +- **Sanic**: 21.9+ + +## 安装 + +最好的方法是立即安装 Sanic 扩展以及Sanic 本身: + +```bash +pip install sanic[ext] +``` + +当然你也可以自己安装它。 + +```bash +pip 安装 sanic-ext +``` + +## 扩展您的应用程序 + +Sanic 扩展将从箱子中为您启用一堆功能。 + +.. 列: + +``` +若要设置 Sanic 扩展 (v21.12+), 您需要做: **nithing** 。如果它是在环境中安装的,它将被设置并准备就绪。 + +此代码是Hello, 世界应用在 [Sanic Getting Started page](../../guide/getting-started). d) _没有任何更改_, 但使用 Sanic Extensions 使用 `sanic-ext` 在后台安装。 +``` + +.. 列: + +```` +```python +from sanic importing Sanic +from sanic.response import text + +app = Sanic("MyHelloWorldApp") + +@app. et("/") +async def hello_world(request): + return text("Hello, world.") +``` +```` + +.. 列: + +``` +**_OLD DEPRECATED SETUP_** + +在 v21.9中,最容易启动的方法是使用 `Extend` 实例化它。 + +如果你回到你好,世界应用在 [Sanic Getting Started页面](../) 向导/getting-started.md, 您将看到这里唯一的添加是两行高亮显示。 +``` + +.. 列: + +```` +```python +from sanic import Sanic +from sanic.response import text +from sanic_ext import Extend + +app = Sanic("MyHelloWorldApp") +Extend(app) + +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` +```` + +无论如何设置,您现在都应该能够查看 OpenAPI 文档,并查看一些功能:[[http://localhost:8000/docs](http://localhost:8000/docs](http://localhost:8000/docs)。 diff --git a/guide/content/zh/plugins/sanic-ext/health-monitor.md b/guide/content/zh/plugins/sanic-ext/health-monitor.md new file mode 100644 index 0000000000..1533584684 --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/health-monitor.md @@ -0,0 +1,81 @@ +--- +title: 神经扩展-健康监测 +--- + +# 健康监测 + +健康监视器需要`sanic>=22.9`和`sanic-ext>=22.9`。 + +您可以设置 Sanic 扩展来监测您的工作流程的健康状况。 这要求您不要处于[单一进程模式](../../guide/deplement/manager.md#单一进程模式)。 + +## 设置 + +.. 列: + +``` +离开框,健康监视器被禁用。如果您想要使用它,您需要选择进入并启用端点。 +``` + +.. 列: + +```` +```python +app.config.HEALTH = True +app.config.HEALTH_ENDPOINT = True +``` +```` + +## 如何工作 + +监测员建立了一个新的背景程序,将定期从每个工人过程中收到直观的确认书。 如果某个工人进程错过多次报告,则监视器会重新启动该工人。 + +## 诊断终点 + +.. 列: + +``` +健康监视器还将启用一个诊断端点,输出[工人状态](../../guide/deplement/manager)。 d#worker-state)。默认情况下ID已禁用。 + +。危险: + + 诊断端点不安全。 如果你在生产环境中部署它,你应该采取步骤来保护它,如果你在使用代理服务器的话。 如果没有,您可能想要考虑在生产中禁用此功能,因为它会泄露您服务器状态的详细信息。 +``` + +.. 列: + +```` +``` +$ curl http://localhost:8000/__health__ +{ + 'Sanic-Main': {'pid': 99997}, + 'Sanic-Server-0-0': { + 'server': True, + 'state': 'ACKED', + 'pid': 9999, + 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), + 'starts': 2, + 'restart_at': datetime.datetime(2022, 10, 1, 0, 0, 12, 861332, tzinfo=datetime.timezone.utc) + }, + 'Sanic-Reloader-0': { + 'server': False, + 'state': 'STARTED', + 'pid': 99998, + 'start_at': datetime.datetime(2022, 10, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc), + 'starts': 1 + } +} +``` +```` + +## 配置 + +| 关键字 | 类型 | 默认设置 | 描述 | +| --------------------------------------------------------------------------------- | ------ | --------------- | ---------------- | +| 生命值 | `bool` | `False` | 是否启用此扩展。 | +| HEALTH_ENDPOINT | `bool` | `False` | 是否启用诊断端点。 | +| HEALTH_MEX_MISSES | `int` | `3` | 工作流程重新启动前连续缺失次数。 | +| HEALTH_MISSED_TITLE | `int` | `10` | 检查工人流程健康的秒数。 | +| HEALTH_MONITOR | `bool` | `True` | 是否启用健康监视器。 | +| HEALTH_REPORT_INTERVAL | `int` | `5` | 每次确认活动之间的秒数。 | +| HEALTH_URI_TO_INFO | `str` | `""` | 诊断端点的 URI 路径。 | +| HEALTH_URL_PREFIX | `str` | `"/__health__"` | 诊断蓝图的 URI 前缀。 | diff --git a/guide/content/zh/plugins/sanic-ext/http/cors.md b/guide/content/zh/plugins/sanic-ext/http/cors.md new file mode 100644 index 0000000000..08d7e9baa1 --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/http/cors.md @@ -0,0 +1,97 @@ +--- +title: Sanic Extensions - CORS 保护 +--- + +# CORS 保护 + +跨源资源共享(又称核心资源共享)本身就是一个_大量_的主题。 这里的文档无法详细了解_它是什么_。 我们非常鼓励你自己进行一些研究,以了解它所提出的安全问题以及解决办法背后的理论。 [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)是很好的第一步。 + +以超简短的措辞, CORS 保护是一个浏览器用来方便网页如何和何时可以从另一个域访问信息的框架。 这与任何人建立单页应用程序都是极其相关的。 您的前端常常可能位于一个域名上,例如`https://portal.myapp.com`,但它需要访问`https://api.myapp.com`的后端。 + +此处的执行受到[`sanic-cors`](https://github.com/ashleysommer/sanic-cors)的大力启发,而这又是基于[`flask-cors`](https://github.com/corydolphin/flask-cors)。 因此,你很可能会用`sanic-ext`来取代\`sanic-cors'。 + +## 基本执行 + +.. 列: + +``` +如[自动端点示例](方法)中的示例所示。 d#options), Sanic Extensions 会自动使CORS 保护无需采取进一步行动, 但它不会提供太多的箱子. + +在 *bare minimum * 上,**强烈** 建议您将 `config.CORS_ORIGINS` 设置为将访问应用程序的原始(s)。 +``` + +.. 列: + +```` +```python +from sanic import Sanic, text +from sanic_ext import Extend + +app = Sanic(__name__) +app.config.CORS_ORIGINS = "http://foobar.com,http://bar.com" +Extend(app) + +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` + +``` +$ curl localhost:8000 -X OPTIONS -i +HTTP/1.1 204 No Content +allow: GET,HEAD,OPTIONS +access-control-allow-origin: http://foobar.com +connection: keep-alive +``` +```` + +## 配置 + +然而,一旦你开始配置CORS 保护的真正功效。 这是所有选项的一个表。 + +| 关键字 | 类型 | 默认设置 | 描述 | +| --------------------------- | -------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `CORS_ALLOW_HEADERS` | `str` 或 `list[str]` | `"*"` | 显示在"访问控制-允许-headers"中的标题列表。 | +| `CORS_ALWAYS_SEND` | `bool` | `True` | 当`True`时,总是会设置`access-control-allow-origin`的值。 当`False`时,只有在存在`原始`标题时才会设置它。 | +| `CORS_AUTOMATIC_OPTIONS` | `bool` | `True` | 当收到传入的飞行前请求时,是否为`access-controll-allow-headers`、`access-control-max-age`和`access-control-allow-methods`设置自动设置值。 如果`False`,则这些值将只设在使用 `@cors` 装饰器装饰的路径上。 | +| `CORS_EXPOSE_HEADERS` | `str` 或 `list[str]` | `""` | 要在 `access-controll-expose-headers` 标题中设置的特殊标题列表。 | +| `CORS_MAX_AGE` | `str`, `int`, `timedelta` | `0` | 飞行前响应的最大秒数可以使用 'access-control-max-age' 头缓存。 falsey 值将导致页眉不被设置。 | +| `CORS_METHODS` | `str` 或 `list[str]` | `""` | 允许的源代码访问的 HTTP 方法,设置在 `access-control-allow-methods` 标题上。 | +| `CORS_ORIGINS` | `str`, `List[str]`, `re.Pattern` | `"*"` | 允许访问资源的来源,如在 `access-control-allow-origin` 标题上设定。 | +| `CORS_SEND_WILDCARD` | `bool` | `False` | 如果`True`,将发送通配符`*`而不是\`orig'请求标题。 | +| `CORS_SUPPORTS_CREDENTIALS` | `bool` | `False` | 是否设置 `access-control-allow-credentials` 标题。 | +| `CORS_VARY_HEADER` | `bool` | `True` | 是否在适当时添加 `vary` 标题。 | + +_For the sake of brevity, where the above says `List[str]` any instance of a `list`, `set`, `frozenset`, or `tuple` will be acceptable. 或者,如果值是 `str`,它可以是逗号分隔的列表。_ + +## 路由级别覆盖 + +.. 列: + +``` +有时可能需要覆盖特定路由的应用程序设置。 为了允许这一点,您可以使用 `@sanic_ext.cors()` 装饰符来设置不同的路径特定值。 + +此装饰符可以覆盖的值是: + +- `origin` +- `expose_headers' +- `allow_headers` +- `allow_methods` +- `supports_credentials` +- `max_age` +``` + +.. 列: + +```` +```python +from sanic_ext import cors + +app.config.CORS_ORIGINS = "https://foo.com" + +@app.get("/", host="bar.com") +@cors(orig="https://bar.com") +async def hello_world(request): + return text("Hello, world.") +``` +```` diff --git a/guide/content/zh/plugins/sanic-ext/http/methods.md b/guide/content/zh/plugins/sanic-ext/http/methods.md new file mode 100644 index 0000000000..ab95d82701 --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/http/methods.md @@ -0,0 +1,165 @@ +--- +title: Sanic 扩展 - HTTP 方法 +--- + +# HTTP 方法 + +## 自动终点 + +默认行为是自动生成所有的 `GET` 路径的 `HEAD` 端点,以及所有 +路径的 `OPTIONS` 端点。 此外,还有自动生成 `TRACE` 端点的选项。 然而, +默认情况下没有启用这些功能。 + +### 黑色 + +.. 列: + +``` +- **配置**: `AUTO_HEAD` (默认 `True`) +- **MDN**: [阅读更多](https://developer.mozilla). rg/en-US/docs/Web/HTTPMethods/HEAD + +A `HEAD` 请求提供了标题和对一个 `GET` 请求提供的相同的响应。 +然而,实际上并没有归还尸体。 +``` + +.. 列: + +```` +```python +@app.get("/") +async def hello_world(request): + return text("Hello, world" +`` + +鉴于上述路由定义,Sanic Extensions 将能使`HEAD` 反应,如上所示。 + +``` +$ curl localhost:8000 --head +HTTP/1。 200 OK +access-allow-origin: * +content-length: 13 +connection: keep-alive +content-type: text/plain; charset=utf-8 +``` +```` + +### 选项 + +.. 列: + +``` +- **配置**: `AUTO_OPTIONS` (默认 `True`) +- **MDN**: [阅读更多] (https://developer.mozilla). rg/en-US/docs/Web/HTTP/Methods/OPTIONS + +"OPTIONS" 请求向收件人详细介绍了如何允许客户端与指定 +端口进行通信。 +``` + +.. 列: + +```` +```python +@app.get("/") +async def hello_world(request): + return text("Hello, world.") +``` + +Given the above route definition, Sanic Extensions will enable `OPTIONS` responses, as seen here. + +It is important to note that we also see `access-control-allow-origins` in this example. This is because +the [CORS protection](cors.md) is enabled by default. + +``` +$ curl localhost:8000 -X OPTIONS -i +HTTP/1.1 204 No Content +allow: GET,HEAD,OPTIONS +access-control-allow-origin: * +connection: keep-alive +``` +```` + +.. tip:: + +``` +即使Sanic 扩展会自动为您设置这些路径,如果您决定手动创建一个 `@app.options` 路径,它将*不*被覆盖。 +``` + +### 追踪器 + +.. 列: + +``` +- **Configuration**: `AUTO_TRACE` (default `False`) +- **MDN**: [Read more](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/TRACE) + +By default, `TRACE` endpoints will **not** be automatically created. However, Sanic Extensions **will allow** you to +create them if you wanted. This is something that is not allowed in vanilla Sanic. +``` + +.. 列: + +```` +```python +@app.route("/", methods=["追踪"]) +async def 处理器(请求): + +``` + +要启用这些端点的自动创建,您必须先启用它们才能扩展 Sanic。 + +```python +from sanic_ext import Extend, Config + +app. xtend(config=Config(http_auto_trace=True)) +``` + +现在,假定您有一些端点设置, 你可以在这里追踪它们: + +``` +$ curl localhost:8000 -X TRACE +TRACE / HTTP/1。 +主机:localhost:9999 +User-Agent:curl/7.76.1 +接受:*/* +``` +```` + +.. tip:: + +``` +设置 `AUTO_TRACE` 可以提供超级帮助, 尤其是当您的应用程序被部署在代理后面,因为它将帮助您确定代理人的行为方式。 +``` + +## 额外方法支持 + +Vanilla Sanic允许您使用 HTTP 方法构建终点: + +- [GET](/en/guide/basics/routing.html#get) +- [POST](/en/guide/basics/routing.html#post) +- [PUT](/en/guide/basics/routing.html#put) +- [HEAD](/en/guide/basics/routing.html#head) +- [OPTIONS](/en/guide/basics/routing.html#选项) +- [PATCH](/en/guide/basics/routing.html#patch) +- [DELETE](/en/guide/basics/routing.html#delete) + +详见[MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) + +.. 列: + +``` +然而,还有两种"标准"HTTP方法:`TRACE`和`CONNECT`。 Sanic 扩展将允许您使用这些方法构建 +端点,否则这些方法是不允许的。 + +值得指出的是,这将*无* 启用方便方法:`@app。 竞技`或`@app.connect`。您需要 +使用示例`@app.route`。 +``` + +.. 列: + +```` +```python +@app.route("/", methods=["追踪", "connect"]) +async def handler(_): + return empty() +``` +```` diff --git a/guide/content/zh/plugins/sanic-ext/injection.md b/guide/content/zh/plugins/sanic-ext/injection.md new file mode 100644 index 0000000000..53e6dfc45b --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/injection.md @@ -0,0 +1,396 @@ +--- +title: 神经扩展 - 依赖注入量 +--- + +# 依赖注入次数 + +依赖注入是基于定义函数签名向路由处理程序添加参数的方法。 具体而言,它看了处理器中参数的 **类型注释** 。 这在一些情况下可能是有用的,例如: + +- 正在获取基于请求头的对象 (像当前会话用户) +- 将某些对象重置为特定类型 +- 使用请求对象预获取数据 +- 自动注入服务 + +`Extend`实例有两种基本方法用于依赖注入:较低级别的 `add_dependency`和较高级别的 `dependency`。 + +**较低级别**: `app.ext.add_dependency(...)` + +- `type,`: 某些独特的类将是对象的类型 +- `constructor: Optional[Callable[..., Any],` (OPTIONAL): 一个返回这种类型的函数 + +**更高级别**: `app.ext.dependency(...)` + +- `obj: Any`: 任何你想要注入的对象 +- `name: 可选的[str]`: 可以交替用作参考的一些名称 + +让我们在这里探索一些案例。 + +.. 警告:: + +``` +如果您在 v21.12之前使用依赖注入,较低级别的 API 方法被称为“注入”。 其后更名为`add_dependency`,并从v21开始。 2 `injection` 是一个 `add_dependency` 的别名。`injection` 方法已经不推荐在 v22.6 中移除。 +``` + +## 基本执行 + +最简单的情况是只是重定一个数值。 + +.. 列: + +``` +如果你有一个模型,你想要根据匹配的路径参数生成,这可能是有用的。 +``` + +.. 列: + +```` +```python +@dataclass +class IceCream: + flavor: str + + def __str__(self) -> str: + return f"{self.flavor.title()} (Yum!)" + +app.ext.add_dependency(IceCream) + +@app.get("/") +async def ice_cream(request, flavor: IceCream): + return text(f"You chose: {flavor}") +``` + +``` +$ curl localhost:8000/chocolate +You chose Chocolate (Yum!) +``` +```` + +.. 列: + +``` +将关键字参数传递给`type`参数的构造函数。前一个例子就是这个例子。 +``` + +.. 列: + +```` +```python +flavor = IceCream(flavor="巧克力") +``` +```` + +## 附加构造器 + +.. 列: + +``` +有时你可能也需要传递构造函数。这可能是一个函数,甚至可能是一个作为构造函数的类方法。 在这个例子中,我们正在创建一种叫做“人”的注射。 先得到`。 + +同样重要的是注意到这个示例。我们实际上正在注入**两个(2)** 对象! 当然不需要这样做,但我们将根据函数签名注入物体。 +``` + +.. 列: + +```` +```python +@dataclass +class PersonID: + person_id: int + +@dataclass +class Person: + person_id: PersonID + name: str + age: int + + @classmethod + async def create(cls, request: Request, person_id: int): + return cls(person_id=PersonID(person_id), name="noname", age=111) + + +app.ext.add_dependency(Person, Person.create) +app.ext.add_dependency(PersonID) + +@app.get("/person/") +async def person_details( + request: Request, person_id: PersonID, person: Person +): + return text(f"{person_id}\n{person}") +``` + +``` +$ curl localhost:8000/person/123 +PersonID(person_id=123) +Person(person_id=PersonID(person_id=123), name='noname', age=111) +``` +```` + +当一个 `constructor` 传递到 `ext.add_dependency` (就像在这个例子中那样)时,它将被调用。 如果没有,则将通过调用 `type` 来创建对象。 有几件重要的事情要注意到一个\`构造器': + +1. 一个位置的 `request: Request' 参数是 *通常的*。 请参阅“人员”。 获取上面的方法作为示例使用 `request`和 [arbitrary-constructors](#arbitry-constructors) 来使用不需要`request\` 的电报。 +2. 所有匹配的路径参数都作为关键字参数注入。 +3. 依赖关系可以被链和嵌套。 请注意在前一个示例中,`Person` 数据集如何有一个 `PersonID` ? 这意味着`PersonID`将首先被调用,并且在调用 `Person.create` 时将该值添加到关键字参数中。 + +## 任意构造器 + +.. 列: + +``` +有时您可能想要构建您注入的 _with_the `Request` 对象。 如果您有任意的类或函数创建您的对象,这是有用的。 如果传唤书中确实有任何必要的参数,那么传唤书本身应当是可以注射的物体。 + +如果您有服务或其他类型的对象只能在单个请求的一生中存在,这是非常有用的。 例如,您可以使用此模式从数据库池中拉取单个连接。 +``` + +.. 列: + +```` +```python +class Alpha: + ... + +class Beta: + def __init__(self, alpha: Alpha) -> None: + self.alpha = alpha + +app.ext.add_dependency(Alpha) +app.ext.add_dependency(Beta) + +@app.get("/beta") +async def handler(request: Request, beta: Beta): + assert isinstance(beta.alpha, Alpha) +``` +```` + +_添加于 v22.9_ + +## 来自 `Request` 的对象 + +.. 列: + +```` +有时您可能想要从请求中提取详细信息并预先处理。 例如,您可以将 JSON 投射到一个 Python 对象,然后添加一些基于数据库查询的附加逻辑。 + +... 警告:: + + 如果您计划使用此方法,, 您应该注意到注射实际上发生在*先于*圣诞老人有机会阅读请求身体之前。 头应该已经消耗。 所以,如果你想要访问身体,你需要手动消耗在这个例子中看到。 + + ``python + 正在等待请求。 eceive_body() + ``` + + + 这可以用于否则你可能: + + - 使用中间件预处理并添加某些内容到 "请求"。 tx` + - 使用装饰器预处理并注入参数到请求处理程序 + + 在此示例 我们正在使用 `compile_profile` 构造函数中的 `Request` 对象来运行一个假DB 查询来生成并返回一个 `UserProfile` 对象。 +```` + +.. 列: + +```` +```python +@dataclass +class User: + name: str + +@dataclass +class UserProfile: + user: User + age: int = field(default=0) + email: str = field(default="") + + def __json__(self): + return ujson.dumps( + { + "name": self.user.name, + "age": self.age, + "email": self.email, + } + ) + +async def fake_request_to_db(body): + today = date.today() + email = f'{body["name"]}@something.com'.lower() + difference = today - date.fromisoformat(body["birthday"]) + age = int(difference.days / 365) + return UserProfile( + User(body["name"]), + age=age, + email=email, + ) + +async def compile_profile(request: Request): + await request.receive_body() + profile = await fake_request_to_db(request.json) + return profile + +app.ext.add_dependency(UserProfile, compile_profile) + +@app.patch("/profile") +async def update_profile(request, profile: UserProfile): + return json(profile) +``` + +``` +$ curl localhost:8000/profile -X PATCH -d '{"name": "Alice", "birthday": "2000-01-01"}' +{ + "name":"Alice", + "age":21, + "email":"alice@something.com" +} +``` +```` + +## 注射服务 + +创建数据库连接池并将其存储在`app.ctx`对象上是常见的模式。 这将使它们能够在您的整个应用程序中使用,这肯定是一种方便。 然而,一个缺点是你不再有一个要处理的打字对象。 您可以使用依赖注入来解决这个问题。 首先,我们将使用下级`add_dependency`来显示这个概念,就像我们在前面的例子中所使用的那样。 但是,可以更好地使用更高级别的 \`依赖' 方法。 + +### 使用 `add_dependency` 的较低级别 API + +.. 列: + +``` +这个操作非常类似于[最后一个例子](#objects-frow-the-request) 的目标是从 `Request` 对象中提取某些东西。 在这个示例中,在 `app.ctx` 实例上创建了一个数据库对象,正在返回依赖注入构造器。 +``` + +.. 列: + +```` +```python +class FakeConnection: + async def execute(self, query: str, **arguments): + return "result" + +@app.before_server_start +async def setup_db(app, _): + app.ctx.db_conn = FakeConnection() + app.ext.add_dependency(FakeConnection, get_db) + +def get_db(request: Request): + return request.app.ctx.db_conn + + + +@app.get("/") +async def handler(request, conn: FakeConnection): + response = await conn.execute("...") + return text(response) +``` +``` +$ curl localhost:8000/ +result +``` +```` + +### 较高级别的 API 使用 `dependency` + +.. 列: + +``` +由于我们在添加依赖注入时有一个可用的实际的 *对象*,我们可以使用更高级别的 "依赖" 方法。 这将使写入模式变得更容易。 + +当您想要注入在应用程序实例整个生命周期中存在而不是请求具体的东西时,这个方法应该始终使用。 它对服务、第三方客户和连接池非常有用,因为它们没有具体要求。 +``` + +.. 列: + +```` +```python +class FakeConnection: + async def execute(self, query: str, **arguments): + return "result" + +@app.before_server_start +async def setup_db(app, _): + db_conn = FakeConnection() + app.ext.dependency(db_conn) + +@app.get("/") +async def handler(request, conn: FakeConnection): + response = await conn.execute("...") + return text(response) +``` +``` +$ curl localhost:8000/ +result +``` +```` + +## 通用类型 + +使用[通用类型](https://docs.python.org/3/library/typing.html#typing.Generic)时小心谨慎。 Sanic依赖注入的方式是匹配整个类型的定义。 因此,`Foo`与`Foo[str] `不同。 当尝试使用 [更高级别的 '依赖' 方法](#the-high-level-api-using-dependency)时,这可能特别棘手,因为这种类型被推断。 + +.. 列: + +``` +例如,由于没有`测试[str] `的定义,**不** 会正常工作。 +``` + +.. 列: + +```` +```python +import typing +from sanic import Sanic, text + +T = typing.TypeVar("T") + +class Test(typing.Generic[T]): + test: T + +app = Sanic("testapp") +app.ext.dependency(Test()) + +@app.get("/") +def test(request, test: Test[str]): + ... +``` +```` + +.. 列: + +``` +要使这个示例发挥作用,您需要为您打算注入的类型添加一个明确的定义。 +``` + +.. 列: + +```` +```python +import +from sanic import Sanic, text + +T = typing.TypeVar("T") + +class Test(键入)。 enric[T]: + test: T + +app = Sanic("taspp") +_sinleton = Test() +app. xt.add_dependency(Test[str], lambda: _sinleton) + +@app.get("/") +def 测试 (请求, test: 测试[str]): + ... +``` +```` + +## 配置 + +.. 列: + +``` +By default, dependencies will be injected after the `http.routing.after` [signal](../../guide/advanced/signals.md#built-in-signals). Starting in v22.9, you can change this to the `http.handler.before` signal. +``` + +.. 列: + +```` +```python +app.config.INJECTION_SIGAL = "http.handler.before" +``` +```` + +_添加于 v22.9_ diff --git a/guide/content/zh/plugins/sanic-ext/logger.md b/guide/content/zh/plugins/sanic-ext/logger.md new file mode 100644 index 0000000000..4d59adc1d7 --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/logger.md @@ -0,0 +1,38 @@ +--- +title: Sanic 扩展-背景记录器 +--- + +# 背景记录器 + +背景记录器需要 `sanic>=22.9` 和 `sanic-ext>=22.9`。 + +您可以设置 Sanic 扩展来从后台进程中记录您所有的消息。 这要求您不要处于[单一进程模式](../../guide/deplement/manager.md#单一进程模式)。 + +日志记录有时可能是一个昂贵的操作。 通过将所有登录推出到后台流程,您可以获得一些性能效益。 + +## 设置 + +.. 列: + +``` +在方框之外,后台记录器已禁用。如果您想要使用它,您将需要选入它。 +``` + +.. 列: + +```` +```python +app.config.LOGING = True +``` +```` + +## 如何工作 + +如果启用,扩展将创建 `multiprocessing.Queue` 。 它将移除[默认 Sanic loggers](../../guide/best practices/logging.md) 上的所有处理程序,并将其替换为 [`QueueHandler`](https://docs.python.org/3/library/logging.handlers.html#queuehandler)。 当消息被记录时,它将被处理器推送到队列。 并通过后台进程读取原有的日志处理程序。 这意味着您仍然可以将日志配置为正常,它应该“只能工作”。 + +## 配置 + +| 关键字 | 类型 | 默认设置 | 描述 | +| ------------------------------------------------------------------------------------ | ------ | ------- | -------------- | +| 正在登录 | `bool` | `False` | 是否启用此扩展。 | +| LOGING_QUEUE_MAX_SIZE | `int` | `4096` | 拒绝消息之前队列的最大尺寸。 | diff --git a/guide/content/zh/plugins/sanic-ext/openapi.md b/guide/content/zh/plugins/sanic-ext/openapi.md new file mode 100644 index 0000000000..ad8490dca6 --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/openapi.md @@ -0,0 +1,11 @@ +--- +title: 无声扩展 - OAS +--- + +# Openapi + +- 添加文档到装饰 +- 正在文档CBV +- 使用 autodoc +- 使用redoc/swagger渲染docs +- 验证 diff --git a/guide/content/zh/plugins/sanic-ext/openapi/advanced.md b/guide/content/zh/plugins/sanic-ext/openapi/advanced.md new file mode 100644 index 0000000000..83c86956b8 --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/openapi/advanced.md @@ -0,0 +1,13 @@ +--- +title: 无声扩展 - 高级OAS +--- + +# 高级版 + +正在编写文档_ + +## CBV + +## 蓝图 + +## 组件 diff --git a/guide/content/zh/plugins/sanic-ext/openapi/autodoc.md b/guide/content/zh/plugins/sanic-ext/openapi/autodoc.md new file mode 100644 index 0000000000..91b44c99f9 --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/openapi/autodoc.md @@ -0,0 +1,154 @@ +--- +title: Sanic 扩展 - 自动生成文档 +--- + +# 自动文档 + +要使得创建API文档页面变得更容易,Sanic 扩展可以使用函数的 docstring 来自动化填充您的文档。 + +## 摘要和说明 + +.. 列: + +``` +函数的 docstring 将用于创建摘要和描述。 从这里的例子中你可以看到, docstring 已被解析成:第一行作为总结,字符串的其余部分作为描述。 +``` + +.. 列: + +```` +```python +@app.get("/foo") +async def handler(request, something: str): + """This is a simple foo handler + + It is helpful to know that you could also use **markdown** inside your + docstrings. + + - one + - two + - three""" + return text(">>>") +``` +```json +"paths": { + "/foo": { + "get": { + "summary": "This is a simple foo handler", + "description": "It is helpful to know that you could also use **markdown** inside your
docstrings.

- one
- two
- three", + "responses": { + "default": { + "description": "OK" + } + }, + "operationId": "get_handler" + } + } +} +``` +```` + +## 操作级别的 YAML + +.. 列: + +``` +通过将有效的 OpenAPI YAML 添加到文档字符串中,你可以扩展自动文档功能。只需在文档字符串中添加一行包含 `openapi:` 的字符串,后面跟随你提供的 YAML 即可。 + +在示例中,"---" 这个标记是*非必要的*,它只是为了帮助将 YAML 在视觉上与文档的其他部分区分开来。 +``` + +.. 列: + +```` +```python +@app.get("/foo") +async def handler(request, something: str): + """This is a simple foo handler + + Now we will add some more details + + openapi: + --- + operationId: fooDots + tags: + - one + - two + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: Just some dots + """ + return text("...") +``` +```json +"paths": { + "/foo": { + "get": { + "operationId": "fooDots", + "summary": "This is a simple foo handler", + "description": "Now we will add some more details", + "tags": [ + "one", + "two" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "How many items to return at one time (max 100)", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Just some dots" + } + } + } + } +} +``` +```` + +.. 注: + +``` +当使用YAML文档和装饰器时,在生成文档时优先考虑的是装饰器的内容。 +``` + +## 不包括文档字符串 + +.. 列: + +``` +有时候函数可能包含一个文档字符串,该字符串不打算在文档内消耗。 + +**选项1**:全局关闭自动文档“应用”。 onfig.OAS_AUTODOC = False` + +**选项2**:使用 `@openapi.no_autodoc` 装饰器禁用单个处理程序 +``` + +.. 列: + +```` +```python +@app.get("/foo") +@openapi.no_autodoc +async def handler(request, something: str): + """This is a docstring about internal info only. Do not parse it. + """ + return text("...") +``` +```` diff --git a/guide/content/zh/plugins/sanic-ext/openapi/basics.md b/guide/content/zh/plugins/sanic-ext/openapi/basics.md new file mode 100644 index 0000000000..962e4361e7 --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/openapi/basics.md @@ -0,0 +1,84 @@ +--- +title: 无声扩展 - 基本OAS +--- + +# 基础知识 + +.. 注: + +``` +在 Sanic 扩展中实现OpenAPI 是基于从 [`sanic-openapi` ](https://github.com/sanic-org/sanic-openapi )实现的 OAS3 实现。 事实上,Sanic扩展在很大程度上是该项目的继承者,该项目在释放Sanic扩展后进入了维护模式。 如果你先前使用 `sanic-openapi` 的 OAS3 ,你应该有一个简单的路径升级到 Sanic Extensions. 不幸的是,该项目支持 OAS2 的规格。 +``` + +.. 列: + +``` +在方框中,Sanic 扩展使用[v3.0 OpenAPI 规范](https://swagger.io/specification/)自动生成的 API 文档。您不需要做任何特殊操作 +``` + +.. 列: + +```` +```python +from sanic import Sanic + +app = Sanic("MyApp") + +# 添加您所有的观点 +``` +```` + +在这么做之后,您现在将根据您现有的应用程序为您生成美丽的文档: + +- [http://localhost:8000/docs](http://localhost:8000/docs) +- [http://localhost:8000/docs/redoc](http://localhost:8000/docs/redoc) +- [http://localhost:8000/docs/swagger](http://localhost:8000/docs/swagger) + +签出[配置部分](../configuration.md) 以了解如何更改文档的路由。 您也可以关闭两个界面中的一个,并自定义界面,在`/docs`路由上可用。 + +.. 列: + +``` +使用 [Redoc](https://github.com/Redocly/Redoc) + +![Redoc](/assets/images/sanic-ext-redoc.png) +``` + +.. 列: + +``` +或 [Swagger UI](https://github.com/swagger-api/swagger-ui) + +![Swagger UI](/assets/images/sanic-ext-swagger.png) +``` + +## 更改规范元数据 + +.. 列: + +``` +如果你想要更改任何元数据, 你应该使用 "描述" 方法。 + +在这个示例中使用 `edent` 和 `description` 参数来使多行字符串变得更加清洁。 这是不必要的,您可以在此传递任何字符串值。 +``` + +.. 列: + +```` +```python +from textwrap import dedent + +app.ext.openapi.describe( + "Testing API", + version="1.2.3", + description=dedent( + """ + # Info + This is a description. It is a good place to add some _extra_ doccumentation. + + **MARKDOWN** is supported. + """ + ), +) +``` +```` diff --git a/guide/content/zh/plugins/sanic-ext/openapi/decorators.md b/guide/content/zh/plugins/sanic-ext/openapi/decorators.md new file mode 100644 index 0000000000..8506c6651a --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/openapi/decorators.md @@ -0,0 +1,520 @@ +--- +title: 无声扩展 - 美洲组织装饰师 +--- + +# 装饰符 + +将内容添加到您的方案的主要机制是通过装饰您的终点。 If you have +used `sanic-openapi` in the past, this should be familiar to you. 装饰者及其参数与[OAS v3.0 规格](https://swagger.io/specialization/)密切匹配 +。 + +.. 列: + +``` +All of the examples show will wrap around a route definition. When you are creating these, you should make sure that +your Sanic route decorator (`@app.route`, `@app.get`, etc) is the outermost decorator. That is to say that you should +put that first and then one or more of the below decorators after. +``` + +.. 列: + +```` +```python +来自sanic_ext importing openapi + +@app.get("/path/to/") +@openapi.summary("这是一个摘要") +@openapi。 escription("这是一个描述") +async def 处理器(请求, 内容: str): + ... +``` +```` + +.. 列: + +``` +您还将看到许多下面的示例引用模型对象。 为了简洁起见,示例将为 +使用 'UserProfile` 。 问题是,它可以是任何类型良好的类。 您可以轻松地想象一下 +这是一个 `dataclass` 或某种其他类型的模型对象。 +``` + +.. 列: + +```` +```python +class UserProfile: + name: str + age: int + email: str +``` +```` + +## 定义装饰符 + +### `@openapi.definition` + +`@openapi.definition`装饰器允许您同时在路径上定义操作的所有部分。 它是一个 +装饰器,它具有与其他装饰者相同的创建操作定义的能力。 使用 +多个特定字段的装饰符或单个装饰符是你开发者的样式选择。 + +字段有意允许接受多种类型,使您最容易定义您的操作。 + +**参数** + +| 字段 | 类型 | +| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `body` | **dict, RequestBody, _YourModel_** | +| `已弃用` | **布尔** | +| `描述` | **str** | +| `文档` | **str, ExternalDocumentation** | +| `exclude` | **布尔** | +| `operation` | **str** | +| `参数` | **str, dict, 参数, [str], [dict], [Parameter]** | +| `response` | **dict, Response, _YourModel_, [dict], [Response]** | +| `summary` | **str** | +| `tag` | **str, Tag, [str], [Tag]** | +| `securd` | **Dict[str, Any]** | + +**示例** + +.. 列: + +```` +```python +@openapi.definition( + body=RequestBody(UserProfile, required=True), + summary="User profile", + tag="one", + response=[Success, ResponseFailure, status=400)], +) +``` +```` + +.. 列: + +- 更多例子见下文。 以下装饰符的任何值都可以在对应的 + 关键字参数中使用。\* + +## 场地特定装饰 + +以下所有装饰符都基于 `@openapi` + +### 正文内容 + +**参数** + +| 字段 | 类型 | +| ------ | ---------------------------------- | +| **内容** | **_YourModel_, dict, RequestBody** | + +**示例** + +.. 列: + +```` +```python +@openapi.body(UserProfile) +``` + +``python +@openapi.body({"application/json": UserProfile}) +``` + +```python +@openapi.body(RequestBody({"application/json": UserProfile})) +``` +```` + +.. 列: + +```` +```python +@openapi.body({"content": UserProfile}) +``` + +```python +@openapi.body(RequestBody(UserProfile)) +``` + +```python +@openapi.body({"application/json": {"description": ...}}) +``` +```` + +### 已弃用 + +**参数** + +_无_ + +**示例** + +.. 列: + +```` +```python +@openapi.过时() +``` +```` + +.. 列: + +```` +```python +@openapi.废弃的 +``` +```` + +### 描述 + +**参数** + +| 字段 | 类型 | +| ------ | ------- | +| `text` | **str** | + +**示例** + +.. 列: + +```` +```python +@openapi.description( + """这是一个**描述** 。 + +## 你可以使用 "markdown" + +- 和 +- make +- 列表。 +""" +) +``` +```` + +.. 列: + +### 文档 + +**参数** + +| 字段 | 类型 | +| ----- | ------- | +| `url` | **str** | +| `描述` | **str** | + +**示例** + +.. 列: + +```` +```python +@openapi.document("http://example.com/docs") +``` +```` + +.. 列: + +```` +```python +@openapi.document(ExternalDocumentation("http://example.com/more")) +``` +```` + +### 不包含 + +可以像所有其他装饰器一样用于路由定义,或者可以在蓝图上调用 + +**参数** + +| 字段 | 类型 | 默认设置 | +| ------ | ------ | -------- | +| `flag` | **布尔** | **True** | +| `bp` | **蓝图** | | + +**示例** + +.. 列: + +```` +```python +@openapi.exclude() +``` +```` + +.. 列: + +```` +```python +openapi.exclude(bp=some_bluprint) +``` +```` + +### 操作 + +设置操作 ID。 + +**参数** + +| 字段 | 类型 | +| ------ | ------- | +| `name` | **str** | + +**示例** + +.. 列: + +```` +```python +@openapi.operation("doNothing") +``` +```` + +.. 列: + +**参数** + +| 字段 | 类型 | 默认设置 | +| ---------- | ------------------------------------- | -------- | +| `name` | **str** | | +| `schema` | _**type**_ | **str** | +| `location` | **"查询", "header", "path" 或 "cookie"** | **"查询"** | + +**示例** + +.. 列: + +```` +```python +@openapi.parameter("thing") +``` + +``python +@openapi.parameter(parameter=Parameter("foobar", 过时=True)) +``` +```` + +.. 列: + +```` +```python +@openapi.parameter("Authorization", str, "header") +``` + +``python +@openapi.parameter("thing", required=True, allowEmptyValue=False) +``` +```` + +### 应答 + +**参数** + +如果使用 "Response" 对象,您不应传递任何其他参数。 + +| 字段 | 类型 | +| ---------- | --------------------------- | +| `status` | **int** | +| `content` | **_类型_, _YourModel_, dict** | +| `描述` | **str** | +| `response` | **答复** | + +**示例** + +.. 列: + +```` +```python +@openapi.response(200, str, "This is endpoint returns a string") +``` + +```python +@openapi.response(200, {"text/plain": str}, "...") +``` + +```python +@openapi.response(response=Response(UserProfile, description="...")) +``` + +```python +@openapi.response( + response=Response( + { + "application/json": UserProfile, + }, + description="...", + status=201, + ) +) +``` +```` + +.. 列: + +```` +```python +@openapi.response200, UserProfile, "...") +``` + +```python +@openapi。 esponse( + 200, + 然后才能 + "application/json": UserProfile, + }, + "描述. .", +) +``` +```` + +### summary + +**参数** + +| 字段 | 类型 | +| ------ | ------- | +| `text` | **str** | + +**示例** + +.. 列: + +```` +```python +@openapi.summary("这是一个终点") + +```` + +.. 列: + +### 标签 + +**参数** + +| 字段 | 类型 | +| ------- | ------------ | +| `*args` | **str, Tag** | + +**示例** + +.. 列: + +```` +```python +@openapi.tag("foo") +``` +```` + +.. 列: + +```` +```python +@openapi.tag("foo", Tag("bar")) +``` +```` + +### 安全的 + +**参数** + +| 字段 | 类型 | +| ----------------- | --------------------------------------------------------------------------- | +| `*args, **kwargs` | **str, Dict[str, Any]** | + +**示例** + +.. 列: + +```` +```python +@openapi.secured() +``` +```` + +.. 列: + +.. 列: + +```` +```python +@openapi.secured("foo") +``` +```` + +.. 列: + +```` +```python +@openapi.secured("token1", "token2") +``` +```` + +.. 列: + +```` +```python +@openapi.secured({"my_api_key": []}) +``` +```` + +.. 列: + +```` +```python +@openapi.secured(my_api_key=[]) +``` +```` + +不要忘记使用 add_security_scheme\` 。 更多详细信息请访问 [security](./security.md)。 +\`\` + +## 与 Pydantic集成 + +Pydantic模型有能力[生成 OpenAPI 方案](https://pydantic-docs.helpmanual.io/usage/schema/)。 + +.. 列: + +``` +为了利用Pydantic模型架构生成,将输出转换为架构。 +``` + +.. 列: + +```` +```python +from sanic import Sanic, json +from sanic_ext import validate, openapi +from pydantic import BaseModel, Field + +@openapi.component +class Item(BaseModel): + name: str + description: str = None + price: float + tax: float = None + +class ItemList(BaseModel): + items: List[Item] + +app = Sanic("test") + +@app.get("/") +@openapi.definition( + body={ + "application/json": ItemList.schema( + ref_template="#/components/schemas/{model}" + ) + }, +) +async def get(request): + return json({}) +``` +```` + +.. 注: + +``` +设置`ref_template`非常重要。默认情况下,Pydantic将选择一个非标准的 OAS模板。 这将导致在生成最后文档时找不到样式。 +``` + +_添加于 v22.9_ diff --git a/guide/content/zh/plugins/sanic-ext/openapi/security.md b/guide/content/zh/plugins/sanic-ext/openapi/security.md new file mode 100644 index 0000000000..7ad5b195fa --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/openapi/security.md @@ -0,0 +1,110 @@ +--- +title: 无声扩展——美洲国家组织的安全计划 +--- + +# 安全方案 + +要记载认证计划,有两个步骤。 + +_Security 仅在 v21.12.2_ 开始 + +## 记录方案 + +.. 列: + +```` +您需要做的第一件事是定义一个或多个安全方案。 基本模式将定义为: + +``python +add_security_scheme("", "") +``` + +`type` 应该对应于允许的安全方案之一:`"apiKey", `"http", `oauth2", `"openIdConnect"。 然后您可以通过指定允许的适当关键字参数。 + +您应该咨询[OpenAPI Specification](https://swagger.io/specification/) 了解什么值是合适的。 +```` + +.. 列: + +```` +```python +app.ext.openapi.add_security_scheme("api_key", "apiKey") +app.ext.openapi。 dd_security_scheme( + "token", + "http", + scheme="bearer", + bearer_form="JWT", +) +应用。 xt.openapi.add_security_scheme("token2", "http") +app.ext.openapi。 dd_security_scheme( + "老学校", + "http", + scheme="basic", +) +app.ext.openapi。 dd_security_scheme( + "oa2", + "oauth2", + flows=own + "implicit": 哇, + "authorizationUrl": "http://example"。 om/auth”, + "scopes": Power + "on:two": "something", + "the:four ": "some other", + "三": "其他东西. .", + }, + } + }, +) +``` +```` + +## 记录终点 + +.. 列: + +``` +有两个选项,文件 _all_endpoint。 +``` + +.. 列: + +```` +```python +app.ext.openapi.secured() +app.ext.openapi.secured("token") +``` +```` + +.. 列: + +``` +或者只文档特定的路由。 +``` + +.. 列: + +```` +```python +@app。 oute("/one") +async def handler1(request): + """ + openapi: + - + security: + - foo: [] + ""” + +@app. oute("/tw") +@openapi.secured("foo") +@openapi。 普遍({"bar":[]}) +@openapi.secured(baz=[]) +async def 处理器2(请求): + ... + +@app.route("/the") +@openapi。 finition(secured="foo") +@openapi.definition(secured={"bar": []}) +async def handler3(request): + ... +``` +```` diff --git a/guide/content/zh/plugins/sanic-ext/openapi/ui.md b/guide/content/zh/plugins/sanic-ext/openapi/ui.md new file mode 100644 index 0000000000..f929bffdb5 --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/openapi/ui.md @@ -0,0 +1,30 @@ +--- +title: Sanic 扩展 - OAS UI +--- + +# UI + +Sanic 扩展使用Redoc和Swagger两种接口。 你可以选择使用一个,或两个。 下面的终点是为您设置的,下面的 bare `/docs` 显示重启。 + +- `/docs` +- `/docs/openapi.json` +- `/docs/redoc` +- `/docs/swagger` +- `/docs/openapi-config` + +## 配置选项 + +| **密钥** | **Type** | **默认** | **去除提示** | +| -------------------------- | ---------- | ------------------- | ----------------------------------------------------------- | +| `OAS_IGNORE_HEAD` | `bool` | `True` | 是否显示 `HEAD` 终点。 | +| `OAS_IGNORE_OPTIONS` | `bool` | `True` | 是否显示 "OPTIONS" 端点。 | +| `OAS_PATH_TO_REDOC_HTML` | `可选的[str]` | `无` | 覆盖默认的 Redoc HTML 路径 | +| `OAS_PATH_TO_SWAGGER_HTML` | `可选的[str]` | `无` | 覆盖默认的 Swagger HTML 路径 | +| `OAS_UI_DEFAULT` | `可选的[str]` | `"redoc"` | 可以设置为 `redoc` 或 `swagger` 。 控制基线路上显示的界面。 如果设置为“无”,基线路将不会设置。 | +| `OAS_UI_REDOC` | `bool` | `True` | 是否启用 Redoc UI | +| `OAS_UI_SWAGGER` | `bool` | `True` | 是否启用 Swagger UI | +| `OAS_URI_TO_CONFIG` | `str` | `"/openapi-config"` | Swagger 使用的 OpenAPI 配置的 URI 路径 | +| `OAS_URI_TO_JSON` | `str` | `"/openapi.json"` | JSON文档的 URI 路径。 | +| `OAS_URI_TO_REDOC` | `str` | `"/redoc"` | 重新编码的 URI 路径。 | +| `OAS_URI_TO_SWAGGER` | `str` | `"/swagger"` | URI 到 Swagger 的路径。 | +| `OAS_URL_PREFIX` | `str` | `"/docs"` | 用于OpenAPI 文档蓝图的 URL 前缀。 | diff --git a/guide/content/zh/plugins/sanic-ext/templating/html5tagger.md b/guide/content/zh/plugins/sanic-ext/templating/html5tagger.md new file mode 100644 index 0000000000..e3790c4e7a --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/templating/html5tagger.md @@ -0,0 +1,7 @@ +--- +title: Sanic 扩展 - html5tagger +--- + +# 即将开始 + +见 [sanic-org/html5tagger on GitHub](https://github.com/sanic-org/html5tagger/) diff --git a/guide/content/zh/plugins/sanic-ext/templating/jinja.md b/guide/content/zh/plugins/sanic-ext/templating/jinja.md new file mode 100644 index 0000000000..e55ced9a99 --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/templating/jinja.md @@ -0,0 +1,171 @@ +--- +title: Sanic Extensions - Jinja +--- + +# 模板 + +Sanic 扩展可以轻松地帮助您将模板整合到您的路由处理器中。 + +## 依赖关系 + +**目前,我们只支持 [Jinja](https://github.com/pallets/jinja/)。** + +[如果你不熟悉如何创建模板,请先阅读Jinja 文档](https://jinja.palletsprojects.com/en/3.1.x/)。 + +Sanic 扩展如果安装在您的环境中,它将自动为您设置并加载Jinja。 因此,您需要做的唯一设置是安装 Jinja: + +``` +pip install Jinja2 +``` + +## 从文件渲染模板 + +您有三(3)种方式: + +1. 使用装饰器预加载模板文件 +2. 返回渲染`HTTPResponse`对象 +3. 混合模式创建一个 `LazyResponse` + +让我们想象你有一个叫做`./templates/foo.html`的文件: + +```html + + + + + My Webpage + + + +

Hello, world!!!!

+
    + {% for item in seq %} +
  • {{ item }}
  • + {% endfor %} +
+ + + +``` + +让我们看看你如何用 Sanic + Jinja来渲染它。 + +### 备选案文1 - 装饰符 + +.. 列: + +``` +这种方法的好处是,可以在启动时预定义模板。 这将意味着较少需要在处理器中进行获取,因此应该是最快的选择。 +``` + +.. 列: + +```` +```python +@app.get("/") +@app.ext.template("foo.html") +async def handler(request: Request): + return {"seq": ["one", "two"]} +``` +```` + +### 备选案文2 - 作为退货对象 + +.. 列: + +``` +这意在模仿核心Sanic的“文本”、“json”、“html”、“file”等模式。 它将允许对响应对象进行最直接的自定义,因为它可以直接控制它。 就像在其他 `HTTPResponse` 对象一样,你可以控制头、 cookie 等。 +``` + +.. 列: + +```` +```python +来自sanic_ext import render + +@app。 et("/alt") +异步处理器(请求: 请求): + 正在等待渲染( + "foo". tml, context={"seq": ["the", "four"]}, status=400 + +``` +```` + +### 备选案文3 - 混合的 lazy + +.. 列: + +``` +在这个方法中,模板是先定义的,而不是在处理程序内(用于性能)。 然后,`render` 函数返回一个 `LazyResponse` ,该函数可以用于在装配器内构建一个 `HTTPResponse` 。 +``` + +.. 列: + +```` +```python +from sanic_ext importing render + +@app.get("/") +@app.ext.template("foo.html") +async def handler(request: Request): + returning render(context={"seq": ["fir", "six"]}, status=400) +``` +```` + +## 从字符串渲染模板 + +.. 列: + +``` +有时,您可能想要在 Python 代码中写入(或生成) 您的模板和 _not_ 从 HTML 文件中读取。 在这种情况下,你仍然可以使用 `render` 函数。只需使用 `template_source` 。 +``` + +.. 列: + +```` +```python +from sanic_ext import render +from textwrap import dedent + +@app.get("/") +async def handler(request): + template = dedent(""" + + + + + My Webpage + + + +

Hello, world!!!!

+
    + {% for item in seq %} +
  • {{ item }}
  • + {% endfor %} +
+ + + + """) + return await render( + template_source=template, + context={"seq": ["three", "four"]}, + app=app, + ) +``` +```` + +.. 注: + +``` +在这个示例中,我们使用 `texttwrap.dedent` 来删除多行字符串开始处的空格。 它是不必要的,而只是一个很好的触摸来保持代码和生成的源代码的清理。 +``` + +## 开发和自动重载 + +如果启用自动重新加载,则更改您的模板文件会触发服务器的重新加载。 + +## 配置 + +See `templating_enable_async` and `templating_path_to_templates` in [settings](./configuration.md#settings). diff --git a/guide/content/zh/plugins/sanic-ext/validation.md b/guide/content/zh/plugins/sanic-ext/validation.md new file mode 100644 index 0000000000..923e96c10a --- /dev/null +++ b/guide/content/zh/plugins/sanic-ext/validation.md @@ -0,0 +1,201 @@ +--- +title: Sanic 扩展 - 验证 +--- + +# 验证 + +网络应用程序最常用的功能之一是用户输入验证。 由于明显的原因,这不仅是一个安全问题,而且也是一个明显的良好做法。 你想要确保你的数据符合预期,并且在没有响应时扔出一个 \`400'。 + +## 二. 执行情况 + +### 与 Dataclasses 验证 + +随着[Data Classes](https://docs.python.org/3/library/dataclasses.html)的引入,Python使得创建符合定义模式的对象变得非常简单。 但是,标准库只支持类型检查验证, **不** 运行时间验证。 Sanic 扩展增加了使用`dataclasses`从方框中下载的请求进行运行时验证的能力。 如果你也安装了"pydantic"或"景点",你也可以使用这些库中的一个。 + +.. 列: + +``` +首先,定义一个模型。 +``` + +.. 列: + +```` +```python +@dataclass +class SearchParams: + q: str +``` +```` + +.. 列: + +``` +然后将其附加到您的路由 +``` + +.. 列: + +```` +```python +from sanic_ext import validate + +@app.route("/search") +@validate(query=SearchParams) +async def handler(request, query: SearchParams): + return json(asdict(query)) +``` +```` + +.. 列: + +``` +您现在应该对传入请求进行验证。 +``` + +.. 列: + +```` +`` +$ curl localhost:8000/search +⚠️ 400 - Bad Request +============== +无效的请求正文: 搜索参数 错误:缺少一个必需的参数:'q' +``` +``` +$ curl localhost:8000/search\? =python +{"q":"python"} +``` +```` + +### 使用 Pydantic验证 + +您也可以使用 Pydantic模型。 + +.. 列: + +``` +首先,定义一个模型。 +``` + +.. 列: + +```` +```python +class Person(BaseModel): + name: str + age: int +``` +```` + +.. 列: + +``` +然后将其附加到您的路由 +``` + +.. 列: + +```` +```python +from sanic_ext import valides + +@app.post("/person") +@validate(json=Person) +async def handler(request, body: Person): + return json(Body.dict()) +``` +```` + +.. 列: + +``` +您现在应该对传入请求进行验证。 +``` + +.. 列: + +```` +``` +$ curl localhost:8000/personn -d '{"name": "Alice", "age": 21}" -X POST +{"name":"Alice","age":21} +``` +```` + +### 使用Attrs进行验证 + +您也可以使用Attrso。 + +.. 列: + +``` +首先,定义一个模型。 +``` + +.. 列: + +```` +```python +@trans.define +class person: + name: str + age: int + +``` +```` + +.. 列: + +``` +然后将其附加到您的路由 +``` + +.. 列: + +```` +```python +from sanic_ext import valides + +@app.post("/person") +@validate(json=Person) +async def handler(request, body: Person): + return json(Body)) +``` +```` + +.. 列: + +``` +您现在应该对传入请求进行验证。 +``` + +.. 列: + +```` +``` +$ curl localhost:8000/personn -d '{"name": "Alice", "age": 21}" -X POST +{"name":"Alice","age":21} +``` +```` + +## 什么可以验证? + +`validate`装饰符可以用来验证来自三个地方的用户数据:JSON body data (\`request ). 这种情况可能会影响到国家的经济和社会经济利益。 + +.. 列: + +``` +正如您可能期望的那样,您可以使用装饰器的关键字参数附上您的模型。 +``` + +.. 列: + +```` +```python +@validate( + json=ModelA, + query=ModelB, + form=ModelC, +) +``` +```` diff --git a/guide/content/zh/plugins/sanic-testing/clients.md b/guide/content/zh/plugins/sanic-testing/clients.md new file mode 100644 index 0000000000..7465218a85 --- /dev/null +++ b/guide/content/zh/plugins/sanic-testing/clients.md @@ -0,0 +1,152 @@ +--- +title: Sanic 测试-测试客户端 +--- + +# 测试客户端 + +您有三个不同的测试客户端,每个客户端具有不同的能力。 + +## 常规同步客户端: `SanicTestClient` + +`SanicTestClient` 在您的本地网络上运行一个实际版本的 Sanic Server 来运行测试程序。 每次调用端点时,它会旋转应用程序的版本,并将它绑定到主机OS上的套接字。 然后,它将使用 `httpx` 直接拨打该应用程序。 + +这是测试Sanic应用的典型方式。 + +.. 列: + +``` +安装 Sanic 测试后,普通的 `SanicTestClient` 可以在不需要进一步设置的情况下使用。 这是因为Sanic在树枝下为你工作。 +``` + +.. 列: + +```` +```python +app.test_client.get("/path/to/endpoint") + +```` + +.. 列: + +``` +然而,您可能会发现自己需要实例化客户端。 +``` + +.. 列: + +```` +```python +from sanic_testing.testimate importing SanicTestClient + +test_client = SanicTestClient(app) +test_client.get("/path/to/endpoint") +``` +```` + +.. 列: + +``` +开始测试客户端的第三个选项是使用 `TestManager` 。 这个方便对象同时设置 `SanicTestClient` 和 `SanicASGITestClient` 。 +``` + +.. 列: + +```` +```python +来自sanic_testination TestManager + +mgr = TestManager(app) +app.test_client.get("/path/to/endpoint") +# 或 +mgr.test_client.get("/path/to/endpoint") +``` +```` + +您可以通过以下方法之一提出请求 + +- `SanicTestClient.get` +- `SanicTestClient.post` +- `SanicTestClient.put` +- `SanicTestClient.patch` +- `SanicTestClient.delete` +- `SanicTestClient.options` +- `SanicTestClient.head` +- `SanicTestClient.websocket` +- `SanicTestClient.request` + +您可以使用这些方法 _almost_ 和您使用 `httpx`时的相同方法。 你传递到`httpx`的任何参数都将被接受,**有一个警告**:如果你在使用`test_client。 赤道`并想手动指定 HTTP 方法,你应该使用: `http_method`: + +```python +test_client.request("/path/to/endpoint", http_method="get") +``` + +## ASGI async client: `SanicASGITestClient` + +不同于“SanicTestClient”在每个请求上旋转服务器,`SanicASGITestClient`不是。 相反,它使用`httpx`库来执行 Sanic 作为ASGI 应用程序来到内部并执行路由处理器。 + +.. 列: + +``` +此测试客户端提供了所有相同的方法,通常与“SanicTestClient”相同。 唯一的区别是您需要在每次通话中添加一个 "等待" : +``` + +.. 列: + +```` +```python +等待app.test_client.get("/path/to/endpoint") + +```` + +`SanicASGITestClient`可以用与`SanicTestClient`完全相同的三种方式使用。 + +.. 注: + +``` +`SanicASGITestClient` 不需要只能用于ASGI 应用程序。 类似于“SanicTestClient”不需要只测试同步端点。这两个客户端都能测试*任何*无声应用程序。 +``` + +## 持久服务客户端: `ReusableClient` + +此客户端在类似于`SanicTestClient`的前提下工作,因为它代表了您应用程序的实例,并且向它提出了真正的 HTTP 请求。 然而,与`SanicTestClient`不同的是,当使用 `ReusableClient` 时,你会控制应用程序的生命周期。 + +这意味着每个请求 **不** 启动一个新的 web 服务器。 相反,您将根据需要启动并停止服务器,并且可以向同一个运行中的实例多次提出请求。 + +.. 列: + +``` +不同于其他两个客户端,您**必须** 实例化此客户端: +``` + +.. 列: + +```` +```python +来自sanic_testing.reusableClient + +client = ReusableClient(app) +``` +```` + +.. 列: + +``` +一旦创建,您将在上下文管理器中使用客户端。一旦管理器超出范围,服务器将关闭。 +``` + +.. 列: + +```` +```python +来自sanic_testing。 可用于导入可重复使用的客户端 + +def test_multiple_endpoints_on_same_server(app): + 客户端= ReusableClient(app) + 带客户端: + _, 响应 = 客户端。 et("/path/to/1") + 要求响应。 tatus == 200 + + _, 响应 = 客户。 et("/path/to/2") + claim response.status == 200 +``` +```` diff --git a/guide/content/zh/plugins/sanic-testing/getting-started.md b/guide/content/zh/plugins/sanic-testing/getting-started.md new file mode 100644 index 0000000000..81a177f57c --- /dev/null +++ b/guide/content/zh/plugins/sanic-testing/getting-started.md @@ -0,0 +1,85 @@ +--- +title: Sanic 测试-入门开始 +--- + +# 正在开始 + +Sanic Testing 是 _official_ 测试Sanic 客户。 它的主要用途是为萨尼克项目本身的测试提供动力。 然而,它也是一个易于使用的客户端,可以让您的 API 测试升级并快速运行。 + +## 最低要求 + +- **Python**: 3.7+ +- **Sanic**: 21.3+ + +年龄在21岁以上的Sanic版本将此模块并入Sanic本身,作为“sanic.testing”。 + +## 安装 + +可从 PyPI 安装Sanic 测试: + +``` +pip 安装卫生测试 +``` + +## 基本用法 + +只要`sanic-testing`软件包处于环境中,你就不需要开始使用它。 + +### 写入同步测试 + +为了使用测试客户端,您只需要在您的应用程序实例中访问属性 'test_client' : + +```python +从 sanic import Sanic, 导入 pytest +,响应 + +@pytest。 ixture +def app(): + sanic_app = Sanic("TestSanic") + + @sanic_app. et("/") + def basic(request): + return response. ext("foo") + + return sanic_app + +def test_basic_test_client(app): + request, response = app.test_client. et("/") + + passage request.method.lower() == "get" + signing response。 did == b"foo" + passage response.status == 200 +``` + +### 写入异步测试 + +为了使用 pytest`中的 async 测试客户端,您应该安装`pytest-asyncio\` 插件。 + +``` +pip install pest-asyncio +``` + +然后您可以创建异步测试并使用 ASGI 客户端: + +```python +import pytest +from sanic import Sanic, response + +@pytest.fixture +def app(): + sanic_app = Sanic(__name__) + + @sanic_app.get("/") + def basic(request): + return response.text("foo") + + return sanic_app + +@pytest.mark.asyncio +async def test_basic_asgi_client(app): + request, response = await app.asgi_client.get("/") + + assert request.method.lower() == "get" + assert response.body == b"foo" + assert response.status == 200 +``` diff --git a/guide/content/zh/release-notes/2021/v21.12.md b/guide/content/zh/release-notes/2021/v21.12.md new file mode 100644 index 0000000000..ee1670dd45 --- /dev/null +++ b/guide/content/zh/release-notes/2021/v21.12.md @@ -0,0 +1,531 @@ +--- +title: 第21.12版 (LTS) +--- + +# 第21.12版 (LTS) + +.. toc:: + +## 一. 导言 + +这是版本21的最后版本[发行周期](../../org/policies.md#release-schedule)。 第21版现在将提供长期支助,为期两年,直至2023年12月。 + +## 了解什么 + +更多详细信息在 [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html)。 显著的新功能或破损功能以及升级内容... + +### 严格的应用和蓝图名称 + +在 [v21.6](./v21.6.md#严格应用与蓝图名称-废弃)应用和蓝图名称必须符合一套新的限制。 这种改变现在正在启动时执行。 + +名称 **必须** : + +1. 仅使用字母数字字符(`a-zA-Z0-9`) +2. 可能包含连线(`-`) 或下划线(`_`) +3. 必须以字母或下划线开头(`a-zA-Z_`) + +### 严格的应用和蓝图属性 + +旧的宽大处理允许直接设置一个 `Sanic` 或 `Blueprint` 对象的属性已被废弃,不再允许。 您必须使用 `ctx` 对象。 + +```python +app = Sanic("MyApp") +app.ctx.db = Database() +``` + +### 移除 + +以下已废弃的功能不再存在: + +- `sanic.exceptions.abot` +- `sanic.views.compositionView` +- `sanic.response.StreamingHTTPResponse` + +### 升级您的流媒体响应(如果尚未完成) + +`sanic.response.stream`响应方法已被**废弃** 并将在 v22.6中删除。 如果您使用旧的学校流媒体响应,请升级它。 + +**OLD - 过时** + +```python +async def sample_streaming_fn(响应): + 等待响应.write("foo, ") + 等待响应.write("bar") + +@app.route("/") +async def 测试(请求: 请求): + return (Sample_streing_fn, content_type="text/csv") +``` + +**当前** + +```python +async def sample_streaming_fn(响应): + 等待响应.write("foo, ") + 等待响应.write("bar") + +@app. oute("/") +异步测试(请求: 请求): + response = 等待请求。 espond(content_type="text/csv") + 等待响应.send("foo,") + 等待响应.send("bar") +``` + +### CLI 全面检查和MOTD (白天) + +Sanic CLI得到了相当广泛的升级。 它添加了一系列新功能,使它与`app.run()`相同。 它还包括一个新的 MOTD 显示,以提供快速、简洁的关于您运行环境的高亮。 贸易和发展中心知道TTY-knowledge,因此服务器日志中的详细信息将会减少。 它主要是为了方便开发应用程序。 + +``` +$ sanic --help +usage: sanic [-h] [--version] [--factory] [-s] [-H HOST] [-p PORT] [-u UNIX] [--cert CERT] [--key KEY] [--tls DIR] [--tls-strict-host] + [-w WORKERS | --fast] [--access-logs | --no-access-logs] [--debug] [-d] [-r] [-R PATH] [--motd | --no-motd] [-v] + [--noisy-exceptions | --no-noisy-exceptions] + module + + ▄███ █████ ██ ▄█▄ ██ █ █ ▄██████████ + ██ █ █ █ ██ █ █ ██ + ▀███████ ███▄ ▀ █ █ ██ ▄ █ ██ + ██ █████████ █ ██ █ █ ▄▄ + ████ ████████▀ █ █ █ ██ █ ▀██ ███████ + + To start running a Sanic application, provide a path to the module, where + app is a Sanic() instance: + + $ sanic path.to.server:app + + Or, a path to a callable that returns a Sanic() instance: + + $ sanic path.to.factory:create_app --factory + + Or, a path to a directory to run as a simple HTTP server: + + $ sanic ./path/to/static --simple + +Required +======== + Positional: + module Path to your Sanic app. Example: path.to.server:app + If running a Simple Server, path to directory to serve. Example: ./ + +Optional +======== + General: + -h, --help show this help message and exit + --version show program's version number and exit + + Application: + --factory Treat app as an application factory, i.e. a () -> callable + -s, --simple Run Sanic as a Simple Server, and serve the contents of a directory + (module arg should be a path) + + Socket binding: + -H HOST, --host HOST Host address [default 127.0.0.1] + -p PORT, --port PORT Port to serve on [default 8000] + -u UNIX, --unix UNIX location of unix socket + + TLS certificate: + --cert CERT Location of fullchain.pem, bundle.crt or equivalent + --key KEY Location of privkey.pem or equivalent .key file + --tls DIR TLS certificate folder with fullchain.pem and privkey.pem + May be specified multiple times to choose multiple certificates + --tls-strict-host Only allow clients that send an SNI matching server certs + + Worker: + -w WORKERS, --workers WORKERS Number of worker processes [default 1] + --fast Set the number of workers to max allowed + --access-logs Display access logs + --no-access-logs No display access logs + + Development: + --debug Run the server in debug mode + -d, --dev Currently is an alias for --debug. But starting in v22.3, + --debug will no longer automatically trigger auto_restart. + However, --dev will continue, effectively making it the + same as debug + auto_reload. + -r, --reload, --auto-reload Watch source directory for file changes and reload on changes + -R PATH, --reload-dir PATH Extra directories to watch and reload on changes + + Output: + --motd Show the startup display + --no-motd No show the startup display + -v, --verbosity Control logging noise, eg. -vv or --verbosity=2 [default 0] + --noisy-exceptions Output stack traces for all exceptions + --no-noisy-exceptions No output stack traces for all exceptions +``` + +### 服务器运行模式和将调试变换到 `debug` + +现在有两个运行模式:`DEV`和\`PRODUCTION'。 默认情况下,Sanic 服务器将在 "PRODUCTION" 模式下运行。 这是用于部署的。 + +目前,`DEV`模式将非常类似于`debug=True`在旧版本的 Sanic 中所做的操作。 然而,在第v22.3段中。 `debug=True` 将\*\*不再启用自动重新加载。 如果你想要调试和自动重新加载,你应该启用 `DEV` 模式。 + +**最新情况** + +``` +$ sanic server:app --dev +``` + +```python +运行(调试=True, auto_reload=True) +``` + +**产品** + +``` +$ sanic server:app +``` + +```python +run() +``` + +从 v22.3 开始,`PRODUCTION` 模式将不再启用访问日志。 + +这些变动概述如下: + +| 标志 | 模式 | 后退 | 日志记录 | 访问日志 | Reload | 最大员工人数 | +| ------- | ----- | -- | ------- | ---- | ------ | ------ | +| --debug | DEBUG | 是的 | DEBUG | 是的 | ^1 | | +| | PROD | 否 | INFO ^2 | ^3 | | | +| --dev | DEBUG | 是的 | DEBUG | 是的 | 是的 | | +| --fast | | | | | | 是的 | + +- ^1 `--debug` 不推荐自动重新加载并在 22.3 中删除 +- ^2 22.3 之后移动到警告 +- ^3 22.3之后:无 + +### 最大允许的工人 + +您可以轻松地使用"--fast"来增加允许的工人的最大数量。 + +``` +$ sanic server:app --fast +``` + +```python +运行 (ast=True) +``` + +### 一等神经扩展支持 + +[Sanic Extensions](../../plugins/sanic-ext/getting-started.md) 提供了一些专门用于 API 开发者的额外功能。 只要包处于环境中,您现在可以轻松地实现它必须提供的所有功能,而无需附加设置。 这些特点包括: + +- 自动创建 `HEAD`, `OPTIONS` 和 `TRACE` 终点 +- CORS 保护 +- 预定义的端点特定响应序列转换器 +- 依赖注入路由处理 +- 与 Redoc 或 Swagger 的 OpenAPI 文档 +- 请求查询参数和实体输入验证 + +首选方法是与 Sanic 一起安装,但您也可以自己安装软件包。 + +.. 列: + +```` +``` +$ pip install sanic[ext] +``` +```` + +.. 列: + +```` +``` +$ pip install sanic-ext +``` +```` + +此后,不需要额外的配置。 Sanic 扩展将附加到您的应用程序,并提供所有附加功能,**不再配置**。 + +如果您想要更改它的工作方式,或提供额外的配置,您可以使用 `app.extend` 更改Sanic 扩展。 下面的例子是相同的。 `Config`对象是为IDE开发提供有用的类型注释。 + +.. 列: + +```` +```python +# 这是可选的,不需要 +app = Sanic("MyApp") +app.extend(config={"oas_url_prefix": "/apidocs"}) +``` +```` + +.. 列: + +```` +```python +# 这是可选的,不需要 +app = Sanic("MyApp") +app.config.OAS_URL_PREFIX = "/apidocs" +``` +```` + +.. 列: + +```` +```python +# This is optional, not required +from sanic_ext import Config + +app = Sanic("MyApp") +app.extend(config=Config(oas_url_prefix="/apidocs")) +``` +```` + +.. 列: + +### 上下文异常 + +在 [v21.9](./v21.9.md#default-exception-messages) 中,我们添加了默认消息到异常,简化了在您的整个应用程序中始终如一地提出异常的能力。 + +```python +class TeapotError(SanicException): + status_code = 418 + message = “对不起,我不能酿造咖啡” + +rappot错误 +``` + +但缺少两件事: + +1. 动态和可预测的信息格式 +2. 添加附加上下文到错误消息的能力 (暂时更多) + +当前版本允许任何Sanic异常在写错误消息时提供背景信息: + +```python +class TeapotError(SanicException): + status_code = 418 + + @property + def message(self): + return f"对不起{self.extr['name']}, 我不能让你咖啡” + +raising TeapotError(extranti={"name": "Adam"}) +``` + +新功能允许将 `extra` 元传递给异常实例。 这个“额外”信息对象 **将在 `PRODUCTION` 模式下被抑制** ,但将以`DevelopMENT` 模式显示。 + +.. 列: + +``` +**PRODUCTION** + +![image](https://user-images.githubusercontent.com/166269/139014161-cda67cd1-843f-4ad2-9fa1-acb94a59fc4d.png) +``` + +.. 列: + +``` +**developMENT** + +![image](https://user-images.githubusercontent.com/1662669/139014121-0596b084-b3c5-4adb-994e-31ba6eba6dad.png) +``` + +从以上获取到项目2:_添加附加上下文到错误消息的能力 + +这在创建微型服务或您打算以 JSON 格式传递错误消息的API时特别有用。 在这种情况下,我们想要围绕例外的某些上下文,而不仅仅是一个可解析的错误信息来返回客户端。 + +```python +raised TeapotError(context={"foot": "bar"}) +``` + +这是**我们想要**总是错误传递的信息(当它可用时)。 以下是它应该看起来的样子: + +.. 列: + +```` +**PRODUCTION** + +```json +{ + "description": "I'm a teapot", + "status": 418, + "message": "Sorry Adam, I cannot make you coffee", + "context": { + "foo": "bar" + } +} +``` +```` + +.. 列: + +```` +**developMENT** + +```json +Paper + "description": "我是个茶", + "status": 418, + "消息": "对不起, 我不能让你咖啡", + "context": Power + "foo": "bar" + }, + "extran": Power + "name": "Adam", + "more ": "lines", + "复合": Power + "one": "two" + } + }, + "路径": "/", + "args": {}, + "exceptions": [ + len + "type": "TeapotError", + "异常": "对不起,Adam, 我不能让你喝咖啡”, + "frames": [ + Power + "file": "handle_request", + "line": 83, + "name": "handle_request", + "src": "" + }, + Power + "file": "/tmp/p. y", + "line": 17, + "name": "handler", + "src": "raising TeapotError(" + } + ] + } + ] +} +``` +```` + +### 背景任务管理 + +使用 `app 时. dd_task` 方法来创建背景任务。 现在可以通过一个可选的 `name` 关键字参数来获取或取消它。 + +```python +app.add_task(dummy, name="dummy_task") +task = app.get_task("dummy_task") + +app.cancel_task("dummy_task") +``` + +### 定义中路由上下文kwargs + +当路由被定义时,您可以添加任何数量的关键字参数与 `ctx_` 前缀。 这些值将被注入到路由 `ctx` 对象中。 + +```python +@app.get("/1", ctx_label="something") +async def handler1(request): + ... + +@appp. et("/2", ctx_label="something") +async def handler2(request): + ... + +@app. et("/99") +async def handler99(request): + ... + +@app. n_request +async def do_something(request): + if request.route.ctx.label == "something": +... +``` + +### 蓝图可以随时注册 + +在先前版本的Sanic中,严格规定蓝图何时可以附加到申请中。 如果你在 `app.blueprint(bp)` \*之前将所有对象附加到蓝图实例中,他们就会被错过。 + +现在,您可以随时附加蓝图,附加到它的所有内容都将在启动时包含。 + +### 噪音异常(强制所有异常日志) + +有一个新的 `NOISY_EXCEPTIONS` 配置值。 当它是 `False` (默认值) 时,萨尼克会尊重任何`SanicException`的安静\` 属性。 这意味着"安静=True"的异常将不会显示到日志输出。 + +然而,当设置 `NOISY_EXCEPTIONS=True` 时,所有异常都将被记录,而不论安静的值如何。 + +在调试时这可能是有帮助的。 + +```python +应用程序.config.NOISY_EXCEPTIS = True +``` + +### 信号事件为 `Enum` + +有一个 `Enum` 包含所有内置的信号值以方便。 + +```python +从sanic.signatures + +@app.signal(Event.HTTP_LIFECYCLE_BEGIN) +async def connection_opened(conn_info): + +``` + +### 自定义环境变量的投影类型 + +默认情况下,当将环境变量应用到 `config` 实例时,Sanic 会将`int`、`float`、`bool`等值转换为`config`。 您可以通过自己的转换器来扩展此功能: + +```python +应用 = Sanic(..., config=Config(converters=[UUID])) +``` + +### 按配置值禁用 `uvloop` + +`uvloop`的用法可以由配置值控制: + +```python +应用程序config.USE_UVLOOP = 错误 +``` + +### 用 TLS 证书运行 Sanic 服务器 + +Sanic 可以使用多个TLS证书运行: + +```python +运行( + ssl=[ + "/etc/letsent/crypt/live/example.com/", + "/etcrypt/live/mysite.example/", + ] +) +``` + +## 新闻 + +### 即将到来:Python Web Development with Sanic + +一本关于Sanic的书即将由一个核心开发者编写,[@ahopkins](https://github.com/ahopkins)。 + +在 [sanicbook.com](https://sanicbook.com)上了解更多信息。 + +> 装备了与Sanic合作的实用知识,以提高您的网络应用程序的性能和可扩展性。 在这样做的时候, 我们将提升您的开发技能,因为您正在学会自定义您的应用程序,以满足不断变化的业务需要,而不必过度设计应用程序。 + +部分账单收入转入萨尼语社区组织,以帮助资助萨尼语的发展和运作。 因此,购买该书是你可以支持Sanic的另一种方式。 + +### 文档的暗色模式 + +如果你还没有注意到,这个Sanic网站现在可以在本机黑暗模式下使用。 您可以切换页面右上角的主题。 + +## 谢谢你 + +Thank you to everyone that participated in this release: :clap: + +[@adarsharegmi](https://github.com/adarsharegmi) +[@ahopkins](https://github.com/ahopkins) +[@ashleysommer](https://github.com/ashleysommer) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@cnicodeme](https://github.com/cnicodeme) +[@kianmeng](https://github.com/kianmeng) +[@meysam81](https://github.com/meysam81) +[@nuxion](https://github.com/nuxion) +[@prryplatypus](https://github.com/prryplatypus) +[@realDragonium](https://github.com/realDragonium) +[@SaidBySolo](https://github.com/SaidBySolo) +[@sjsadowski](https://github.com/sjsadowski) +[@Tronic](https://github.com/tronic) +[@Varriount](https://github.com/Varriount) +[@vltr](https://github.com/vltr) +[@whos4n3](https://github.com/whos4n3) + +并且,特别感谢你[@miss85246](https://github.com/miss85246)和[@ZinkLu](https://github.com/ZinkLu)做了大量工作,使文档同步并翻译成中文。 + +--- + +如果您喜欢这个项目,请考虑捐款。 当然,我们喜欢代码贡献,但我们也喜欢任何形式的贡献。 考虑撰写一些文档,显示关闭的情况,加入对话并让您的声音为人所知,如果您能够:[金融贡献](https://opencollective.com/sanic-org/)。 diff --git a/guide/content/zh/release-notes/2021/v21.3.md b/guide/content/zh/release-notes/2021/v21.3.md new file mode 100644 index 0000000000..53134b7484 --- /dev/null +++ b/guide/content/zh/release-notes/2021/v21.3.md @@ -0,0 +1,272 @@ +--- +title: 版本21.3 +--- + +# 版本21.3 + +.. toc:: + +## 一. 导言 + +现在沙漠化速度更快。 + +好吧,它已经很快了。 但是,随着二十一世纪会议的第一次复会,我们纳入了几个重大里程碑,这些里程碑已经取得了一些具体的改进。 这些建议包含了多年来一直在工作中的一些想法,并最终将其变成了已经发表的版本。 + +.. 警告:打破更改 + +```` +Version 21.3 introduces a lot of new features. But, it also includes some breaking changes. This is why these changes were introduced after the last LTS. If you rely upon something that has been removed, you should continue to use v20.12LTS until you are able to upgrade. + +```bash +pip install "sanic>=20.12,<20.13" +pip freeze > requirements.txt +``` + +For most typical installations, you should be able to upgrade without a problem. +```` + +## 了解什么 + +显著的新功能或破损功能以及升级内容... + +### 仅限Python 3.7+ + +此版本丢弃了 Python 3.6 支持。 20.12LTS版本将继续支持Python 3.6,直至2022年12月EOL和19版。 2LTS将支持它至2021年12月的EOL。 + +阅读更多关于我们的 [LTS 政策] (../project/policies.md#long-term support-v-interim-releases). + +### 作为一等公民串流 + +最大的速度提高是将请求/响应周期统一为单一的流程。 以前,正常周期与流周期之间存在着差异。 这已经简化到了它的位置,尽管API 现在保持相同,因为兼容性。 净养恤金是**所有**请求现在都应看到新的养恤金。 + +阅读更多关于 [串流变化](../advanced/streading.md#response-streaming)。 + +### 路由器全面检查 + +旧的Sanic路由器是以正则表达方式为基础的。 此外,它还遇到了一些使得很难在运行时加以修改的问题,并造成了一些业绩问题。 这种变化已经是多年了,现在[在启动时将路由器转换为编译的树](https://community.sanicframework.org/t/a-快速-new-routter/649/41)。 寻求全年的进一步改进。 + +外置的 API 保持了后向兼容性。 但是,如果你特别访问路由器内的任何东西,你就会注意到一些变化。 例如: + +1. `Router.get()` 有一个新的返回值 +2. `Route` 现在是一个合适的类对象,而不是一个`namedtuple` +3. 如果手动构建路由器,你需要调用 `Router.finalize()` 方法才能使用 +4. 有一个新的 `` 模式可以匹配到您的路由 +5. 您不能在没有定义至少一个路由的情况下启动应用程序 + +路由器现在位于自己的仓库中:[sanic-org/sanic-router](https://github.com/sanic-org/sanic-router),也是自己的[PyPI上的独立包件](https://pypi.org/project/sanic-routing/)。 + +### Signals API ⭐️ + +_BETA 特性:API将在 v21.6_ 中完成 + +新路由器的一个附带好处是,它也可以为[新信号API](https://github.com/sanic-org/sanic/issues/1630)提供双重动力。 此功能正在发布,供公共使用,很可能公共API将不会改变其最终形式。 + +这个特点的核心思想是: + +1. 允许开发者更多地控制和访问插件到服务器和请求生命周期, +2. 提供新的工具,通过您的应用程序同步和发送消息;以及 +3. 最终进一步提高性能。 + +API 引入了三种新方法: + +- `@app.signal(...)` - 定义信号处理器。 它看起来很像一条路线。 每当发出此信号时,此处理程序将被执行。 +- `app.event(...)` - 一个可在应用程序中随时随地用于暂停执行直到事件触发的可待办事宜。 +- `app.signatch(...)` - 触发一个事件并导致信号处理程序被执行。 + +```python +@app.signal("foo.bar.") +async def signal_handler(notes, **kwargs): + print(f)[signal_handler] {thing=}", kwargs) + +async def wait_for_event(app): + while True: + print("> 等待") + 等待应用。 vent("foo.bar. ") + print("> event found\n") + +@app. fter_server_start +async def after _server_start(app, rol): + app. dd_task(wait_for_event(app)) + +@app.get("/") +async def 触发器(request): + 等待app.appailch("foo.bar.baz") + return response.text("完成") +``` + +### 路由名称 + +路由被`route.name`和`route.endpoint`引用. 虽然类似,但它们略有不同。 现在,所有航线都将**前后一致** 命名空间并被引用。 + +```text +。[可选:。] +``` + +这个新的“名称”已分配给属性 "route.name"。 我们正在废弃`route.endpoint`, 并将在 v21.9 中移除该属性。 在此之前,它将是`route.name`的一个别名。 + +此外,为静态、websocket和蓝图路线等事项使用的命名前缀已被删除。 + +### 新装饰符 + +好几个新的方便装饰师帮助IDE自动完成。 + +```python +# 别名 @app.listener("...") +@app.befor_server_start +@app.after_server_stop +@app.prev_server_stop +@app.after _server_stop + +# 别名 @app.midlewarer("...") +@app.on_request +@app.on_response +``` + +### 在航线中取消引用 + +如果你有一条使用非ascii字符的路由,萨尼克将不再能够为你\`取消引用'。 您需要具体告诉路线定义它应该这样做。 + +```python +@app.route("/overload/", methods=["GET"], unquote=True) +async def handler2(request, param): + return text("OK2 " + param) + +request, response = app. est_client.get("/overload/potsertifle") +power response.text == "OK2 etail" +``` + +如果你忘记了这一点,你的文本将保持编码。 + +### 更改 `Request.match_info` + +`match_info` 始终提供匹配路径参数的数据。 您现在有权修改这一点,例如在中间行程中。 + +```python +@app.on_request +def convert_to_snake_case(request): + request.match_info = to_snake(request.match_info) +``` + +### 路径中的版本类型 + +现在路上的 `version` 参数可以是: + +- `str` +- `int` +- `float` + +```python +@app.route("/foo", version="2.1.1") +@app.route("/foo", version=2) +@app.route("/foo", version=2.1) +``` + +### 与身体一起安全处理方法 + +`GET`、`HEAD`、`OPTIONS`和`DELETE`的路由处理程序将不会解码传递给它的任何HTTP体。 您可以覆盖: + +```python +@app.delete(..., ignore_body=False) +``` + +### 应用程序、 蓝图和蓝图组均等性 + +`Sanic` 和 `Blueprint` 两个类有一个共同的基础。 以前,它们重复了许多功能,导致它们之间的执行略有不同。 既然他们都继承了相同的基础类,开发者和插件应该有一个更加一致的API。 + +另外,蓝图组现在也支持常见的 URL 扩展,例如`version` 和 `strict_slashes` 关键字参数。 + +### 从依赖中丢弃`httpx` + +不再依赖`httpx`。 + +### 已删除 `testing` 库 + +Sanic 内部测试客户端已被移除。 它现在位于自己的仓库:[sanic-org/sanic-testing](https://github.com/sanic-org/sanic-testing)并且也是自己的[PyPI上独立的软件包](https://pypi.org/project/sanic-testing/)。 + +如果您已经安装了 `sanic-testing` ,它将和以前一样在你的 `Sanic()` 应用程序上可用和使用。 因此,您需要做的**仅**更改是将`sanic-testing`添加到您的测试套装要求中。 + +### 应用程序和连接级别环境(`ctx`) 对象 + +版本19.9 [添加](https://github.com/sanic-org/sanic/pull/1666/files) the `request.ctx` API。 这个有用的构建可以轻松地将属性和数据附加到请求对象 (例如) 在中间, 并在别处重新使用他所申请的信息。 + +同样,这一概念正在两个地方扩大: + +1. 申请实例和 +2. a 运输连接。 + +#### 应用程序上下文: + +一个常见的用例是将属性附加到应用实例。 为了一致性并避免名称与 Sanic 属性碰撞的问题,`ctx` 对象现在存在于 `Sanic` 实例中。 + +```python +@app.before_server_startup +async def startup_db(app, _): + # WRONG + app.db = 等待connect_to_db() + + # CORRECT + app.ctx.db = 等待connect_to_db() +``` + +#### 连接环境 + +当客户端发送一个保持生命的头部时,Sanic 会尝试保持传送套接字[打开一段时间](../deplement/configuration.md#keep-live-timeout)。 这个传输对象现在有一个 `ctx` 对象可供使用。 这实际上意味着单个客户端的多次请求(在运输层被重新使用的情况下)可能会共享状态。 + +```python +@app.on_request +async def increment_foo(request): + if not hasattr(request.conn_info.ctx, "foo"): + request.conn_info.ctx.foo = 0 + request.conn_info.ctx.foo += 1 + +@app.get("/") +async def count_foo(request): + return text(f"request.conn_info.ctx.foo={request.conn_info.ctx.foo}") +``` + +```bash +$ curl localhost:8000 localhost:8000 localhost:8000 localhost:8000 +request.conn_info.ctx.foo=1 +request.conn_info.ctx.foo=2 +request.conn_info.ctx.fo=3 +``` + +.. 警告:: + +``` +连接级别环境是一个实验功能,应在 v21.6 中最后确定。 +``` + +## 新闻 + +### 新的首页 🎉 + +我们将文件分成两部分。 代码库内的码头仍将继续构建到 ReadTheDocs 的 Sphinx 文档。 然而,它将仅限于API文档。 新的首页将存放“Sanic 用户指南”。 + +新站点运行于Vuepress。 欢迎提供捐助。 我们还请各方帮助翻译这些文件。 + +作为其中的一部分,我们还刷新了RTD文档并将其更改为仅API文档。 + +### 聊天已移动到Discord + +Gitter chatroom已经朝着逐步淘汰的方向迈出了一步。 在它的位置,我们打开了一个[Discord服务器](https://discord.gg/RARQzAEMAA)。 + +### 开放集团 + +萨尼克社区组织[打开了一个开放的集体页面](https://opencollective.com/sanic-org),以使任何想要资助萨尼克发展的人都能够这样做。 + +### 2021 发布管理器 + +感谢您的 @sjsadowski 和 @yunstanford 担任2019和2020年的释放管理员。 今年的发行经理是 @ahopkins 和 @vltr。 + +## 谢谢你 + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) [@akshgpt7](https://github.com/akshgpt7) [@artcg](https://github.com/artcg) [@ashleysommer](https://github.com/ashleysommer)[@elis-k](https://github.com/elis-k) [@harshanarayana](https://github.com/harshanarayana) [@sjsadowski](https://github.com/sjsadowski) [@tronic](https://github.com/tranic) [@vltr](https://github.com/vltr), + +[@ConnorZhang](https://github.com/miss85246)和[@ZinkLu](https://github.com/ZinkLu)将我们的文件翻译成中文。 + +--- + +确保检查更新日志以获取所有PR等链接。 diff --git a/guide/content/zh/release-notes/2021/v21.6.md b/guide/content/zh/release-notes/2021/v21.6.md new file mode 100644 index 0000000000..f923124dce --- /dev/null +++ b/guide/content/zh/release-notes/2021/v21.6.md @@ -0,0 +1,352 @@ +--- +title: 版本21.6 +--- + +# 版本21.6 + +.. toc:: + +## 一. 导言 + +这是第二次版本的 21 [发行周期] (../project/policies.md#release-schedule)。 9月份还会有一个版本,然后再在12月份的长期支持版本中“定稿”。 有一件用户可能从21.3开始注意到,路由器被移动到自己的包裹上:[`sanic-routing`](https://pypi.org/project/sanic-routing)。 这种变化现在可能会持续下去。 从发布开始,最低要求版本为 0.7.0。 + +## 了解什么 + +更多详细信息在 [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html)。 显著的新功能或破损功能以及升级内容... + +### 弃用 `StreamingHTTPResponse` + +`StreamingHTTPResponse`的使用已被废弃,并将在21.12版本中删除。 这影响到`sanic.response.stream` 和 `sanic.response.file_stream`, 这两者都是在hood instantiate `StreamingHTTPResponse`下发生的。 + +虽然确切的迁移路径尚待确定,但`sanic.response.stream`和`sanic.response.file_stream`将以某种形式作为方便操作者继续存在。 在今年夏天全天寻找更多细节,因为我们希望在9月份公布之前完成这项工作。 + +### 弃用 `ComppositionView` + +`CompositionView`的用法已被废弃,将在 21.12中删除。 + +### 废弃路径参数类型:`string` 和 `number` + +今后你应该使用 `str` 和 `float` 作为路径参数类型,而不是 `string` 和 `number` 。 + +```python +@app.get("//") +async def handler(request, foo: str, bar: float): + ... +``` + +现有的 `string` 和 `num` 类型都是别名并将继续工作,但将在 v21.12中移除。 + +### 版本0.7路由器升级 + +这包括一些错误修复,并且比v0.6更宽松地处理更多的边缘案例。 如果您遇到任何不被支持的模式,[请报告它们](https://github.com/sanic-org/sanic-routing/issues)。 您可以在 `sanic-routing` [发行笔记](https://github.com/sanic-org/sanic-routing/releases)上看到一些问题已经解决。 + +### 内联 `eof()` + +第21.3版包括[如何处理流媒体的重大变化](https://sanic.dev/en/guide/release-notes/v21.3.html#what to-know)。 采用的模式将成为缺省模式(见下文)。 为方便起见,列入了一个新的`response.eof()`方法。 一旦最终数据被推送到客户端,它应被调用: + +```python +@app.route("/") +async def test(request): + response = request.reply (content_type="text/csv") + 等待应答.send("foo") + 等待应答.send("bar") + 等待应答.eof() + 返回 +``` + +### 新路径参数类型:`slug` + +您现在可以指定一个动态路径段为 `slug` ,并且匹配: + +```python +@app.get("/articles/") +异步文章(请求, article_slug: str): +... +``` + +Slugs必须由小写字母或数字组成。 它们可能含有连字符(`-`),但不能是第一个字符。 + +``` +This-a-slug +与 123-is-also a slug +111-at-is-a-slug +NOT-a-slug +-NOT-a-slug +``` + +### 更严格的应用程序和蓝图名称以及弃用 + +您的应用程序和“蓝图”实例必须符合一套更严格的要求: + +1. 只包含字母数字字符 +2. 可能包含连线(`-`) 或下划线(`_`) +3. 必须以字母开头(大写或小写) + +命名协议类似于Python变量命名协议,并增加了允许连字符串(`-`)。 + +放松标准已经被拆除。 从21.12开始,不符合同将是启动时间错误。 + +### `Route`对象上的新访问权: `route.uri` + +v21.3中的`路由`对象不再具有`uri`属性。 相反,你可以得到的关卡是“route.path”。 然而,由于`sanic-routing`是如何工作的,因此`path`财产没有`/`的领先地位。 这个问题已经纠正,现在有一个 `route.uri` 带有一个顶部斜线: + +```python +route.uri == f"/{route.path}" +``` + +### 在 `Request` 对象上的一个新存取器影响IPs + +要访问传入请求的 IP 地址,Sanic 在请求对象上有一个便捷访问器:`request.ip`。 这不是新的,来自一个提供开启HTTP连接详细信息的底层对象:\`request.conn_info'。 + +当前版本为`conn_info`对象添加了一个新的`client_ip`访问器。 对于IPv4,您不会发现差异。 然而,对于IPv6应用,新的访问器将提供地址的“卸载”版本。 请考虑以下示例: + +```python +@app.get("/") +async def handler(request): + return json( + { + "request.ip": request.ip, + "request.conn_info.client": request.conn_info.client, + "request.conn_info.client_ip": request.conn_info.client_ip, + } + ) + +app.run(sock=my_ipv6_sock) +``` + +```bash +$ curl http://\[:1\]:8000 +v. + "request.ip": ":1", + "request.conn_info.client": "[::1]", + "request.conn_info.client_ip": ":1" +} + +``` + +### 备用的 `Config` 和 `Sanic.ctx` 对象 + +您现在可以将自己的配置和上下文对象传递到您的 Sanic 应用程序。 自定义配置文件 _应该_ 为子类 `sanic.config.Config` 。 上下文对象可以是你想要的任何东西,没有任何限制。 + +```python +class CustomConfig(Config): + ... + +config= CustomConfig() +app = Sanic("custom", config=config) +configinstance(app.config, CustomConfig) +``` + +是... + +```python +class CustomContext: + ... + +ctx = CustomContext() +app = Sanic("custom", ctx=ctx) +significance(app.ctx, CustomContext) +``` + +### Sanic CLI 改进 + +1. 现有功能的新标志:`--auto-reload` +2. 现有参数的一些新的简写标记 +3. 新功能:`--factory` +4. 新功能:`--simple` +5. 新功能:`--reload-dir` + +#### 工厂应用 + +对于遵循出厂模式的应用程序(函数返回“卫生”函数)。 您现在可以使用 "--factory" 标志从 Sanic CLI 启动您的应用程序。 + +```python +from sanic import Blueprint, Sanic, text + +bp = Blueprint(__file__) + +@bp.get("/") +async def handler(request): + return text("😎") + +def create_app() -> Sanic: + app = Sanic(__file__) + app.blueprint(bp) + return app +``` + +您现在可以运行它: + +```bash +$ sanic path.to:create_app --facture +``` + +#### Sanic 简单服务器 + +Sanic CLI 现在包含一个简单的模式,作为一个网络服务器的目录。 它将在目录根目录寻找一个 `index.html` 。 + +```bash +$ sanic ./path/to/dir --imple +``` + +.. 警告:: + +``` +此功能仍处于早期的 *beta* 模式。它的范围可能会改变。 +``` + +#### 附加重新加载目录 + +当使用 `debug` 或 `auto-reload` 时,您可以为Sanic 添加额外的目录以监视新文件。 + +```bash +sanic ... --reload-dir=/path/to/foo --reload-dir=/path/to/bar +``` + +.. tip:: + +``` +您的确*不*需要将此内容包含在您的应用程序目录中。当您的应用程序中任何Python文件更改时,Sanic会自动重新加载。 当静态文件更新时,您应该使用 "reload-dir" 参数来监听和更新您的应用程序。 +``` + +### 版本前缀 + +当添加 `version` 时,您的路由前缀为 `/v`。 这将永远处于道路的开始。 这并不是新鲜事。 + +```python +# /v1/my/path +app.route("/my/path", version=1) +``` + +现在,您可以更改前缀(因此在\*版本之前添加路径段)。 + +```python +# /api/v1/my/path +app.route("/my/path", version=1, version_prefix="/api/v") +``` + +`version_prefix`参数可以定义于: + +- `app.route` 和 `bp.route` 装饰符 (也包括所有方便装饰师) +- `Blueprint` 实例 +- `Blueprint.group` 构造函数 +- `BlueprintGroup` 实例 +- `app.bluprint` 注册 + +### 信号事件自动注册 + +设置`config.EVENT_AUTOREGISTER`为`True`,将允许您等待任何信号事件,即使它以前没有一个信号处理器来定义。 + +```python +@app.signal("do.something.start") +async def signal_handler(): + 等待do_something() + 等待app.appailch("do.something.complete") + +# 在您的应用中其他一些: +等待app.event("do.something.complete") +``` + +### 无限可复用和可嵌套的 `Blueprint` 和 `BlueprintGroup` + +单个的 `Blueprint` 不能分配给多个组。 这些团体本身也可以通过无限期地嵌入一个或多个其他团体。 这允许无限的组合。 + +### HTTP 方法作为 `Enum` + +Sanic 现在有 `sanic.HTTPMethod`, 它是 `Enum`。 它可以与字符串互换使用: + +```python +从 sanic import Sanic, HTTPMethow + +@app.route("/", methods=["post", "PUT", HTTPMethod.pATCH]) +async def handler(...): +... +``` + +### 扩展`HTTPMethodView` + +基于班级的视图现在可以通过以下三种方式之一: + +**选项1 - 退出** + +```python +class DummyView(HTTPMethodView): + ... + +app.add_route(DummyView.as_view(), "/dummy") +``` + +**选项2 - 从 `attach` 方法** + +```python +DummyView(HTTPMethodView): + ... + +DummyView.attach(app, "/") +``` + +**Option 3 - From class definition at `__init_subclass__`** + +```python +DummyView(HTTPMethodView, attach=app, uri="/"): +... +``` + +如果您的CBV位于另一个文件中,选项2和3是有用的: + +```python +从 Sanic 导入 Sanic, HTTPMethodView + +类DummyView(HTTPMethodView, attach=Sanic.get_app(), uri="/"): + +``` + +## 新闻 + +### Discord 和支持论坛 + +如果你还没有加入我们的社区,你可以通过加入 [Discord 服务器] (https://discord.gg/RARQzAEMAA) 和 [Community Forums](https://community.sanicframework.org/) 来成为一个组成部分。 另外,在Twitter上关注[@sanicframework](https://twitter.com/sanicframework)。 + +### 上海合作组织2022年选举 + +夏季的 🏝️/Winter ❄️ (选择你的Hemisphere) 已经到达我们了。 这意味着我们将举行上海合作组织的选举。 今年,我们将有以下职位要填补: + +- 指导委员会成员(任期两年) +- 指导委员会成员(任期两年) +- 指导委员会成员(任期一年) +- 发行管理器 v22 +- 发行管理器 v22 + +[@vltr](https://github.com/vltr)将继续在指导委员会完成他的第二年。 + +如果您有兴趣了解更多信息,您可以阅读上海合作组织[角色和责任](../project/scope.md#roles-and-responsibilities),或Adam Hopkins on Discord。 + +提名将于9月1日开始。 更多详细信息将在论坛上我们更加接近时提供。 + +### 新项目正在进行中 + +我们在上海合作组织总括中增加了一个新项目:[`sanic-ext`](https://github.com/sanic-org/sanic-ext)。 它尚未释放,正在积极发展。 该项目的目标最终将是替换[`sanic-openapi`](https://github)。 提供更多功能的 web 应用程序开发者的 om/sanic-org/sanic-openapi ,包括输入验证、CORS 处理和HTTP 自动方法处理器。 如果您有兴趣帮助您,请在Discord上告诉我们。 在九月份发布之前的某个时候(希望是)寻找这个项目的初步版本。 + +## 谢谢你 + +Thank you to everyone that participated in this release: :clap: + +[@aaugustin](https://github.com/aaugustin) +[@ahopkins](https://github.com/ahopkins) +[@ajaygupta2790](https://github.com/ajaygupta2790) +[@ashleysommer](https://github.com/ashleysommer) +[@ENT8R](https://github.com/ent8r) +[@fredlllll](https://github.com/fredlllll) +[@graingert](https://github.com/graingert) +[@harshanarayana](https://github.com/harshanarayana) +[@jdraymon](https://github.com/jdraymon) +[@Kyle-Verhoog](https://github.com/kyle-verhoog) +[@sanjeevanahilan](https://github.com/sanjeevanahilan) +[@sjsadowski](https://github.com/sjsadowski) +[@Tronic](https://github.com/tronic) +[@vltr](https://github.com/vltr) +[@ZinkLu](https://github.com/zinklu) + +--- + +如果您喜欢这个项目,请考虑捐款。 当然,我们喜欢代码贡献,但我们也喜欢任何形式的贡献。 考虑撰写一些文档,显示关闭的情况,加入对话并让您的声音为人所知,如果您能够,[财务贡献](https://opencollective.com/sanic-org/)。 diff --git a/guide/content/zh/release-notes/2021/v21.9.md b/guide/content/zh/release-notes/2021/v21.9.md new file mode 100644 index 0000000000..877253bca6 --- /dev/null +++ b/guide/content/zh/release-notes/2021/v21.9.md @@ -0,0 +1,240 @@ +--- +title: 版本21.9 +--- + +# 版本21.9 + +.. toc:: + +## 一. 导言 + +这是版本21的第三次版本[发行周期](../../org/policies.md#release-schedule)。 版本21将在12月份长期支持版本发布时“最后完成”。 + +## 了解什么 + +更多详细信息在 [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html)。 显著的新功能或破损功能以及升级内容... + +### 移除配置值:`WEBSOCKET_READ_LIMIT`、`WEBSOCKET_WRITE_LIMITED `和`WEBSOCKET_MAX_QUEUE` + +随着Web套接字实现的全面整修,这些配置值已被删除。 目前没有计划替换它们。 + +### 废弃`FALLBACK_ERROR_FORMAT` 的默认值 + +当没有错误处理程序被附加时,Sanic使用了“html”作为备用格式类型。 这已被弃用,并将从 v22.3 开始变为`text`。 虽然这个值已经更改为 `auto`,但它仍将继续使用 HTML 作为最后的推力v21.12LTS 才能改变。 + +### `ErrorHandler.lookup`签名已弃用 + +`ErrorHandler.lookup`现在**需要** 两个位置参数: + +```python +def 查找(自己,例外情况,route_name: 可选[str]): +``` + +不符合同的方法会导致蓝图特定的异常处理程序无法正确附加。 + +### 即将清除的提醒 + +作为提醒,下列项目已经被废弃,并将在21.12LTS版本中删除 + +- `CompositionView` +- `load_env` (使用 `env_prefix` 代替) +- Sanic objects (application instance, blueprints, and routes) must by alphamumeric conforming to `^[a-zA-Z][a-zA-Z0-9_\-]*$` +- 任意将对象分配到应用程序和蓝图实例(代之以使用 `ctx` ;将其移除已从21.9移至21.12) + +### 重写网上套接字 + +对网络套接字连接的处理进行了大修。 多亏了[@aaugustin](https://github.com/aaaugustin)[websockets`](https://websockets.readthedocs.io/en/stable/index.html)现在有了一个新的安装程序,允许Sanic自行处理Websocket连接的 I/O 。 因此,Sanic已将最小版本变成`websockets>=10.0\` 。 + +这种变化对开发者来说基本上是不明显的,只是一些环绕Sanic网络套接字处理器的困难已经得到纠正。 例如,当有人断开连接时,您现在应该能够抓到自己的 "取消错误": + +```python +@app.websocket("/") +async def 处理器(请求, w): + 尝试: + while True: + 等待asyncio.sleep(0.25) + 但asyncio.cancelled错误除外: + print("用户闭合连接") +``` + +### 内置信号 + +Version [21.3](./v21.3.md) introduced [signals](../advanced/signals.md). 现在,Sanic 调度事件**来自codebase** 本身。 这意味着开发人员现在有能力将请求/响应周期绑定到比以往更接近的水平。 + +之前, 如果你想要注入一些逻辑, 你被限制在中间。 把集成信号看作是_super_-middleware。 现在发出的事件包括: + +- `http.lifecycle.begin` +- `http.lifecycle.complete` +- `http.lifecycle.excition` +- `http.lifecycle.handle` +- `http.lifecycle.read_body` +- `http.lifecycle.read_head` +- `http.lifecycle.request` +- `http.lifecycle.response` +- `http.lifecycle.send` +- `http.midleware.after ` +- `http.midleware.before` +- `http.routing.after ` +- `http.routing.before` +- `server.init.after ` +- `server.init.before` +- `server.shutdown.after ` +- `server.shutdown.before` + +.. 注: + +``` +服务器信号与四(4)服务器监听器事件相同。 事实上,这些听众本身现在只是表示执行情况的方便。 +``` + +### Smarter `auto`异常格式 + +Sanic现在将试图根据端点和客户端以适当的例外格式进行回应。 例如,如果您的端点总是返回 `sanic.response.json` 对象,那么任何异常都会自动在 JSON 格式化。 `text`和`html`响应也是如此。 + +此外,您现在可以使用 _explicitly_ 控制哪个格式化器在路由基础上使用路由定义: + +```python +@app.route("/", error_format="json") +async def handler(request): + pass +``` + +### 蓝图复制 + +蓝图可以复制到新的实例。 这将传送附加到它的所有东西,如路线、中间距等。 + +```python +v1 = Blueprint("Version1", version=1) + +@v1.route("/something") +def something(request): + passe + +v2 = v1.copy("Version2", version=2) + +app.bluprint(v1) +app.luprint(v2) +``` + +``` +/v1/some +/v2/some +``` + +### 蓝图组方便方法 + +蓝图组现在应该拥有与普通蓝图相同的方法。 这样,蓝图连同蓝图复制,现在应该是非常合成和灵活的。 + +### 接受页眉解析 + +Sanic `Request` 对象可以解析 `Acept` 标题,提供客户端内容类型首选项的顺序列表。 您可以作为配件访问它: + +```python +打印(request.accept) +# ["*/*"] +``` + +它还能够处理通配符。 例如,假定收到的请求包括: + +``` +接受:*/* +``` + +然后,以下是`True`: + +```python +在请求中接受“文本/平原” +``` + +### 默认异常消息 + +来自`SanicExcution`的任何异常现在都可以定义默认异常消息。 这使得在多个地方重新使用相同的异常更方便和更容易操作,而不会在异常所提供的消息中进入DRY问题。 + +```python +class TeaError(SanicException): + message = "Tempest in a teapot" + +raw TeaErrors +``` + +### 输入批注便利 + +现在可以使用 Python 类型注释来控制路径参数类型。 而不是这样做: + +```python +@app.route("///") +def handler(request: Request, one: int, two: float, three: UUID): + ... +``` + +您现在可以简单地这样做: + +```python +@app.route("///") +def handler(request: Request, one: int, two: float, three: UUID): + ... +``` + +这两个例子都将导致适用相同的路由原则。 + +### 明确静态资源类型 + +您现在可以明确告诉一个 `static` 端点,它是否应该将资源视为一个文件或一个目录: + +```python +static("/", "/path/to/some/file", resource_type="file")) +``` + +## 新闻 + +### 释放`sanic-ext`和废弃`sanic-openapi` + +萨尼克的核心原则之一是,它是一种工具,而不是独裁者。 该网站的首页称: + +> 构建你想要构建的方式而不让你的工具约束你。 + +这意味着在`sanic` 仓库中不存在很多使用的常见功能(具体来说是由 Web API 开发者使用)。 这是有充分理由的。 不作决定就提供了开发者的自由和灵活性。 + +但是,有时你们不想要建造和重建同样的东西。 迄今为止,Sanic确实依靠社区的大力支持来弥补插件的缺口。 + +从最初几天起,就有一个官方的`sanic-openapi`软件包,能够根据您的应用程序创建 OpenAPI 文档。 但是,这个项目多年来一直受到困扰,没有像主要项目那样受到高度重视。 + +从发布v21.9开始,上海合作组织正在废弃`sanic-openapi`软件包,并将其移动到维护模式。 这意味着它将继续得到必要的更新,以便为当前的未来保持这种状态。 但它将不会收到任何新的功能强化。 + +一个叫做`sanic-ext`的新项目正在取代它。 这个包不仅提供了构建OAS3文档的能力。 但可以填补API开发者在他们的应用程序中可能需要的许多空白。 例如,它将从方框中设置CORS, 并在需要时自动启用 `HEAD` 和 `OPTIONS` 。 它还能够使用标准库基准数或Pydantic模型验证收到的数据。 + +山羊清单包括: + +- CORS 保护 +- 传入请求验证 +- 使用 Redoc 或 Swagger UI 自动使用 OAS3 文档 +- auto `HEAD`、`OPTIONS`和`TRACE`的回应 +- 依赖输入 +- 响应序列化 + +此项目现在仍然处于"alpha" 模式,并且可能会被更改。 虽然它被认为是生产能力,但在我们继续添加特性时,可能需要更改API。 + +签出 [documentation](../../plugins/sanic-ext/getting-started.md) 以了解更多详情。 + +## 谢谢你 + +Thank you to everyone that participated in this release: :clap: + +[@aaugustin](https://github.com/aaugustin) +[@ahopkins](https://github.com/ahopkins) +[@ashleysommer](https://github.com/ashleysommer) +[@cansarigol3megawatt](https://github.com/cansarigol3megawatt) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@gluhar2006](https://github.com/gluhar2006) +[@komar007](https://github.com/komar007) +[@ombe1229](https://github.com/ombe1229) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@Tronic](https://github.com/tronic) +[@vltr](https://github.com/vltr) + +并且,特别感谢你[@miss85246](https://github.com/miss85246)和[@ZinkLu](https://github.com/ZinkLu)做了大量工作,使文档同步并翻译成中文。 + +--- + +如果您喜欢这个项目,请考虑捐款。 当然,我们喜欢代码贡献,但我们也喜欢任何形式的贡献。 考虑撰写一些文档,显示关闭的情况,加入对话并让您的声音为人所知,如果您能够,[财务贡献](https://opencollective.com/sanic-org/)。 diff --git a/guide/content/zh/release-notes/2022/v22.12.md b/guide/content/zh/release-notes/2022/v22.12.md new file mode 100644 index 0000000000..1f5163fbb3 --- /dev/null +++ b/guide/content/zh/release-notes/2022/v22.12.md @@ -0,0 +1,190 @@ +--- +title: 第22.12版 (LTS) +--- + +# 第22.12版 (LTS) + +.. toc:: + +## 一. 导言 + +这是版本22 的最后版本[发行周期](../../org/policies.md#release-schedule)。 因此,这是一个 **长期支持** 发布,并将按照 [policies]的说明提供支持(../../org/policies.md#long-term support-v-interim-releases)。 + +## 了解什么 + +更多详细信息在 [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html)。 显著的新功能或破损功能以及升级内容... + +### 🚨 _BREAKING CHANGE_ - Sanic Inspector 现在是一个 HTTP 服务器 + +Sanic v22.9 引入了 [Inspector](./v22.9.md#视察员),以便对运行中的Sanic 实例进行实时检查。 此功能依赖于打开 TCP 套接字和通过自定义协议进行通信。 这个基本TCP协议已被丢弃,转而在它的位置上运行一个完整的HTTP服务。 [了解更多关于检查员的信息](../deplement/spector.md)。 + +当前版本介绍了一个新的 HTTP 服务器和刷新的 CLI 体验。 这将使这里突出显示的几个新功能。 然而,最重要的变化也许是将检查员的所有命令转移到CLI实例的子解析器。 + +``` +$ sanic inspect --help + + ▄███ █████ ██ ▄█▄ ██ █ █ ▄██████████ + ██ █ █ █ ██ █ █ ██ + ▀███████ ███▄ ▀ █ █ ██ ▄ █ ██ + ██ █████████ █ ██ █ █ ▄▄ + ████ ████████▀ █ █ █ ██ █ ▀██ ███████ + +Optional +======== + General: + -h, --help show this help message and exit + --host HOST, -H HOST Inspector host address [default 127.0.0.1] + --port PORT, -p PORT Inspector port [default 6457] + --secure, -s Whether to access the Inspector via TLS encryption + --api-key API_KEY, -k API_KEY Inspector authentication key + --raw Whether to output the raw response information + + Subcommands: + Run one or none of the below subcommands. Using inspect without a subcommand will fetch general information about the state of the application instance. + + Or, you can optionally follow inspect with a subcommand. If you have created a custom Inspector instance, then you can run custom commands. See https://sanic.dev/en/guide/deployment/inspector.html for more details. + + {reload,shutdown,scale,} + reload Trigger a reload of the server workers + shutdown Shutdown the application and all processes + scale Scale the number of workers + Run a custom command +``` + +#### CLI 远程访问现在可用 + +如上文所示,检查员的`主机`和港口\`现在已经在CLI上明确暴露。 此前在v22.9中,根据申请实例推定。 由于这一变化, 更可能的做法是让检查专员了解现场生产实例和远程安装CLI的访问。 + +例如,您可以从本地开发机器检查您正在运行的生产部署。 + +``` +$ sanic inspection --host=1.2.3.4 +``` + +.. 警告:: + +``` +在 **production** 实例中,请确保您正在使用 _using TLS and authentification_ 描述如下。 +``` + +#### TLS 加密现在可用 + +您可以通过提供 TLS 证书来加密网络流量来保护您的远程检查员访问权限。 + +```python +app.config.INSPECTOR_TLS_CERT = "/path/to/cert.pem" +app.config.INSPECTOR_TLS_KEY = "/path/to/key.pem" +``` + +要通过 CLI 访问加密安装,请使用 "--secure" 标记。 + +``` +$ sanic inspection --secure +``` + +#### 身份验证现在可用 + +要控制远程检查器的访问权限,您可以使用 API 密钥保护终点。 + +```python +app.config.INSPECTOR_API_KEY = "超级秘书200" +``` + +要通过 CLI 访问受保护的安装,请使用 "--api-key" 标记。 + +``` +$ sanic inspection --api-key=Super-Secret-200 +``` + +This is equivalent to the header: `Authorization: Bearer `. + +``` +$ curl http://localhost:6457-H "Authorization: Morer Super-Secret-200" +``` + +### 缩放运行服务器员数 + +检查专员现在能够缩减工人工序的数量。 例如,若要缩放到三个仿真品,请使用以下命令: + +``` +美元桑色体检查比例 3 +``` + +### 使用自定义命令扩展检查器 + +检查员现在可以充分扩展,可以将自定义命令添加到CLI。 欲了解更多信息,请查看[自定义命令](../deplement/检查员.md#custom-commands)。 + +``` +$ sanic inspection foo --bar +``` + +### 工人在失败时提早退出 + +带有v22.9的流程经理的启动时间非常短。 这是为了防止僵局。 增加到30秒, 而且,如果工人进程启动时发生崩溃,新机制已经增加到早期失败。 + +### 引入`JSONResponse` 并方便更新JSON响应机构 + +`sanic.response.json`方便方法现在返回一个新的子类的 `HTTPResponse`:`JSONResponse`。 这种新类型有一些方便的方法来处理一个响应机构创建后的更改。 + +```python +resp = json({"foot": "bar"}) +resp.update({"another": "value"}) +``` + +[return JSON Data](../basics/response.md#returning-json-data) 获取更多信息。 + +### 下游需求更新:`uvloop` 和 `websockets` + +最小的 `uvloop` 设置为 `0.15.0` 。 已添加更改,使Sanic 兼容`websockets`11.0\`版本。 + +### 强制退出 2nd `ctrl+c` + +在支持操作系统时,现有的行为是 Sanic 服务器试图在点击 `ctrl+c` 时执行宽松的关机。 这个新版本将在最初关闭后立即关闭 ctrl+c。 + +### 废弃和移除 + +1. _DEPRECATED_ - 在 v22.9 中引入的 `--checkt*` 命令已被一个新的子命令解析器替换为 `expect` 。 旗帜版本将继续运行到 v23.3。 鼓励您使用替换。 虽然这一短暂的废弃期偏离了标准的两周期,但我们希望这种变化能够尽量减少干扰。 + ``` + OLD sanic ... --inspect + new sanic ... Inspection + + OLD sanic . .. --inspect-raw + New sanic ... inspection --raw + + OLD sanic . .. --inspect-reload + 新的sanic... 检查重新加载 + + OLD sanic... --inspect-shutdown + 新的sanic... 检查关机 + ``` + +## 新闻 + +萨尼克社区组织将由一个新的指导委员会领导,任期为2023年。 有两名返回的成员和两名新成员。 + +[@ahopkins](https://github.com/ahopkins) _returning_ +[@prryplatypus](https://github.com/prryplatypus) _returning_ \ +[@sjsadowski](https://github.com/sjsadowski) _NEW_ +[@Tronic](https://github.com/Tronic) _NEW_ + +2023年版本管理员是:[@ahopkins](https://github.com/ahopkins)和[@sjsadowski](https://github.com/sjsadowski)。 + +如果您有兴趣更多地参与Sanic,请在[Discord服务器](https://discord.gg/RARQzAEMAA)联系我们。 + +## 谢谢你 + +Thank you to everyone that participated in this release: :clap: + +[@aaugustin](https://github.com/aaaugustin) +[@ahopkins](https://github.com/ahopkins) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@kijk2869](https://github.com/kijk2869) +[@LiraNuna](https://github.com/LiraNuna) +[@prplatryypus](https://github.com/prryplatypus) +[@sjsadowski](https://github.com/sjsadowski) +[@todddialpad](https://github.com/todddialpad) +[@ + +--- + +如果您喜欢这个项目,请考虑捐款。 当然,我们喜欢代码贡献,但我们也喜欢任何形式的贡献。 考虑撰写一些文档,显示关闭的情况,加入对话并让您的声音为人所知,如果您能够:[金融贡献](https://opencollective.com/sanic-org/)。 diff --git a/guide/content/zh/release-notes/2022/v22.3.md b/guide/content/zh/release-notes/2022/v22.3.md new file mode 100644 index 0000000000..96ac53b94a --- /dev/null +++ b/guide/content/zh/release-notes/2022/v22.3.md @@ -0,0 +1,230 @@ +--- +title: 22.3 版本 +--- + +# 22.3 版本 + +.. toc:: + +## 一. 导言 + +这是版本22 [发行周期](../../org/policies.md#release-schedule)的第一次版本。 所有标准的上海合作组织图书馆现在都进入了相同的发布周期,并且将遵循相同的版本模式。 这些一揽子计划是: + +- [`sanic-routing`](https://github.com/sanic-org/sanic-routing) +- [`sanic-testing`](https://github.com/sanic-org/sanic-testing) +- [`sanic-ext`](https://github.com/sanic-org/sanic-ext) + +## 了解什么 + +更多详细信息在 [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html)。 显著的新功能或破损功能以及升级内容... + +### 应用程序多服务 + +Sanic 服务器现在有一个 API ,允许您在同一过程中并肩运行多个应用程序。 这是通过在一个或多个应用程序实例上调用 `app.preparere(...)` 做到的,一次或多次。 每次它都应受独特的东道国/港口组合的约束。 然后,你开始使用`Sanic.serve()`来为应用程序服务。 + +```python +App = Sanic("One") +app2 = Sanic("Two") + +app.preparre(port=99999) +app.preparre(port=9998) +app.preparre(port=9997) +app2.preparre(port=888888) +app2.preparre(port=8887) + +Sanic.serve() +``` + +在上面的代码片段,有两个应用程序将同时运行并绑定到多个端口。 此功能在 CLI 中_不支持_。 + +这个模式是要替代运行 `app.run(...)` 。 应该注意到,`app.run`现在只是上述模式的一个短语,仍然得到完全支持。 + +### 👶 _BETA FEATURE_ - 新路径参数类型: 文件扩展 + +一个非常常见的模式是创建一个动态生成文件的路由。 端点是指在文件上与扩展名匹配。 有一个匹配文件的新路径参数:``。 + +```python +@app.get("/path/to/") +async def 处理(请求, 文件名, ext): +... +``` + +这将捕获任何以文件扩展名结尾的模式。 然而,您可能想要通过指定哪个扩展名以及使用文件名称的其他路径参数类型来展开这个扩展。 + +例如,如果你想要抓取一个只是数字的 `.jpg` 文件: + +```python +@app.get("/path/to/") +async def 处理(请求, 文件名, ext): +... +``` + +一些潜在的例子: + +| 定义 | 示例 | 文件名 | 扩展 | +| ------------------------------------------------------------------------------------ | ----------------------------------------------------------- | -------- | --------------------------- | +| \ | 页次 | `"page"` | `"txt"` | +| \ | jpg | `"cat"` | \`"jpg"" | +| \ | jpg | `"cat"` | \`"jpg"" | +| | 123.txt | `123` | `"txt"` | +| | 123.svg | `123` | `"svg"` | +| | 3.14.tar.gz | `3.14` | \`"tar.gz"" | + +### 🚨 _Breaking变换_ - 非空字符串路径参数 + +动态路径参数只匹配非空字符串。 + +之前一个带有动态字符串参数的路由(`/` 或 `/`) 将匹配任何字符串,包括空字符串。 现在只匹配一个非空字符串。 要保留旧的行为,您应该使用新的参数类型:`/`。 + +```python +@app.get("/path/to/") +async def 处理器(请求,foo) +... +``` + +### 🚨 _Breaking变换_ - `sanic.worker.GunicornWorker` 已删除 + +“GunicornWorker”背离了我们通常的废弃政策,被移除,作为升级Sanic服务器以包括多服务的过程的一部分。 作出这一决定的部分原因是,即使存在这种决定,也不是部署萨尼克的最佳战略。 + +如果你想使用“gunicorn”来部署Sanic,那么你应该使用[uvicorn`执行的策略](https://www.uvicorn.org/#running-with-gunicorn)来部署Sanic。 这将通过`uvicorn'来有效运行 Sanic。 您可以通过安装 `uvicorn` 来升级到此模式: + +``` +pip install uvicorn +``` + +然后,你应该能够以这样的模式运行它: + +``` +gunicorn path.to.sanic:app -k uvicorn.workers.UvicornWorker +``` + +### 授权头正在解析 + +`Authorization`标题已经部分解析了一段时间。 您可以使用 `request.token` 来获取以下两种形式之一的标题: + +``` +Authorization: Token +Authorization: Bearer +``` + +Sanic 现在可以解析更多的凭据类型,如`BASIC`: + +``` +授权: 基本Z2lsLWJhdGVzOnBhc3N3b3JkMTIz +``` + +现在可以以 `request.credentials` 的形式访问: + +```python +print(request.credentials) +# Credentials(auth_type='Basic', token='Z2lsLWJhdGVzOnBhc3N3b3JkMTIz', _username='gil-bates', _password='password123') +``` + +### CLI 参数可选注入到应用程序出厂中 + +如果你正在使用解析的 CLI 参数,Sanic 现在会尝试将解析的 CLI 参数注入到工厂。 + +```python +def create_app(args): + app = Sanic("MyApp") + print(args) + return app +``` + +``` +$sanic p:create_app --factory +Namespace(module='p:create_app', factory=True, simple=False, host='127.0.0.1', port=8000, unix='', cert=None, key=None, tls=None, tlshost=False, workers=1, fast=False, access_log=False, debug=False, auto_reload=False, path=None, dev=False, motd=True, verbosity=None, noisy_exceptions=False) +``` + +如果你使用 `--factory` 来运行CLI ,你也可以选择将任意参数传递给命令。 它将被注入到参数 `namespace` 。 + +``` +sanic p:create_app --factor --foo=bar +Namespace(module='p:create_app', factory=True, simple=False, host='127.0.0.0 ', port=8000, unix='', cert=None, key=None, tlshost=False, workers=1, fast=False, access_log=False, debug=False, auto_reload=False, path=None, dev=False, motd=True, verbosity=None, noisy_exceptions=False, foo='bar') +``` + +### 新的阅读器进程监听器事件 + +当运行带有自动重新加载的Sanic服务器时,有两个新事件会触发读取器\*仅在读取器过程中触发: + +- `reload_process_start` +- `reload_process_stop` + +只有当读取器正在运行时才触发这些。 + +```python +@app.reload_process_start +async def reload_start(*_): + print(">>>>>>>>> reload_start <<<<<<<<") + +@app.reload_process_stop +async def reload_stop(*_): + print(">>>>>>>>> reload_stop <<<<<") +``` + +### 事件循环不再是监听器的必填参数 + +你可以忽略监听器的 "循环" 参数。 这两个例子都按预期那样起作用: + +```python +@app.before_server_start +async def without(app): + ... + +@app.before_server_start +async def with(app, loop): +... +``` + +### 删除 - 调试模式不会自动启动读取器 + +当与 `--debug` 或 `debug=True`运行时,Sanic 服务器将不会自动启动自动重新加载器。 这个在调试时同时执行的功能在 v21 中被废弃,并且在这个版本中被删除。 如果你想要_同时_调试模式和自动重新加载,你可以使用 "--dev" 或 "dev=True" 。 + +**dev = 调试模式 + 自动重新加载器** + +### 废弃-加载小写环境变量 + +Sanic 负载预固定环境变量作为配置值。 只要前缀匹配,它就没有区分大写和小写。 然而,钥匙应当居于首位的始终是公约。 这已被弃用,如果值不是大写,您将收到警告。 在 v22.9 中,只能加载大写和预定的密钥。 + +## 新闻 + +### Packt 出版关于Sanic web 开发的新书 + +.. 列: + +``` +在**Python Web Development 与 Sanic** 上有一本由[@ahopkins](https://github.com/ahopkins)编写的新书。 该书得到上海合作组织的认可,所有销售收入的一部分直接送到上海合作组织,以进一步发展萨尼克。 + +您可以在 [sanicbook.com](https://sanicbook.com/) 学习更多 +``` + +.. 列: + +``` +![Python Web Development with Sanic](https://sanicbook.com/images/SanicCoverFinal.png) +``` + +## 谢谢你 + +Thank you to everyone that participated in this release: :clap: + +[@aericson](https://github.com/aericson) +[@ahankinson](https://github.com/ahankinson) +[@ahopkins](https://github.com/ahopkins) +[@ariebovenberg](https://github.com/ariebovenberg) +[@ashleysommer](https://github.com/ashleysommer) +[@Bluenix2](https://github.com/Bluenix2) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@dotlambda](https://github.com/dotlambda) +[@eric-spitler](https://github.com/eric-spitler) +[@howzitcdf](https://github.com/howzitcdf) +[@jonra1993](https://github.com/jonra1993) +[@prryplatypus](https://github.com/prryplatypus) +[@raphaelauv](https://github.com/raphaelauv) +[@SaidBySolo](https://github.com/SaidBySolo) +[@SerGeRybakov](https://github.com/SerGeRybakov) +[@Tronic](https://github.com/Tronic) + +--- + +如果您喜欢这个项目,请考虑捐款。 当然,我们喜欢代码贡献,但我们也喜欢任何形式的贡献。 考虑撰写一些文档,显示关闭的情况,加入对话并让您的声音为人所知,如果您能够:[金融贡献](https://opencollective.com/sanic-org/)。 diff --git a/guide/content/zh/release-notes/2022/v22.6.md b/guide/content/zh/release-notes/2022/v22.6.md new file mode 100644 index 0000000000..fc0d94fb16 --- /dev/null +++ b/guide/content/zh/release-notes/2022/v22.6.md @@ -0,0 +1,173 @@ +--- +title: 22.6 版本 +--- + +# 22.6 版本 + +.. toc:: + +## 一. 导言 + +这是第二次版本的 22 [发行周期] (../../org/policies.md#release-schedule)。 版本22将在12月份的长期支持版本发布时“最后完成”。 + +## 了解什么 + +更多详细信息在 [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html)。 显著的新功能或破损功能以及升级内容... + +### `DEBUG`模式下自动设置TLS + +Sanic 服务器可以使用 [mkcert](https://github.com/FiloSottile/mkcert) 或 [trustme](https://github.com/python-trio/trustme) 自动设置一个 TLS 证书。 此证书将启用本地开发环境的`https://localhost`(或另一个本地地址)。 您必须自己安装 `mkcert` 或 `trustme` 才能正常工作。 + +.. 列: + +```` +``` +$ sanic path.to.server:app --auto-tls --debug +``` +```` + +.. 列: + +```` +```python +app.run(debug=True, auto_tls=True) + +```` + +在`ASGI`模式或`PRODUCTION`模式下运行时,此功能不可用。 当在生产中运行 Sanic时,您应该使用真正的TLS 证书,要么通过合法的供应商购买,要么使用[我们的加密](https://letsenccrypt.org/)。 + +### HTTP/3 Server :ro火箭: + +2022年6月,国际电子交易日志最后确定并出版了HTTP/3规格[RFC 9114](https://www.rfc-editor.org/rfc/rfc9114.html)。 简而言之,HTTP/3 是一个**所有**不同于HTTP/1.1和HTTP-2的协议,因为它使用 UDP而不是TCP 新的 HTTP 协议保证更快的网页加载时间和解决旧标准的一些问题。 你被鼓励[阅读更多关于](https://http3-explanined.haxx.se/)这个新的网页技术。 您可能需要安装一个[能够的客户端](https://curl.se/docs/http3.html),因为传统的工具将无法工作。 + +Sanic 服务器使用 [aioquic](https://github.com/aiortc/aioquic) 提供 HTTP/3 支持。 此 \*\*must \*\* 已安装: + +``` +pip install sanic aioquic +``` + +``` +pip install sanic[http3] +``` + +要启动 HTTP/3,您必须在运行应用程序时明确请求它。 + +.. 列: + +```` +`` +$ sanic path.to.server:app --http=3 +``` + +``` +$ sanic path.to.server:app -3 +``` +```` + +.. 列: + +```` +```python +app.run(version=3) +``` +```` + +要同时运行一个 HTTP/3 和 HTTP/1.1 服务器,您可以使用 v22.3 引入的 [application multiserve](./v22.3.html#application-multi-servve)。 + +.. 列: + +```` +`` +$ sanic path.to.server:app --http=3 --http=1 +``` + +``` +$ sanic path.to.server:app -3 -1 +``` +```` + +.. 列: + +```` +```python +app.prepre(version=3) +app.pres(version=1) +Sanic.serve() +``` +```` + +因为HTTP 3 需要 TLS,您不能在没有TLS 证书的情况下启动 HTTP/3 服务器。 你应该[自己设置它](../how-to/tls.html),或者在 `DEBUG` 模式下使用 `mkcert` 。 目前,HTTP/3 的自动TLS设置与 `trustme` 不兼容。 + +\*\*👶 此功能正在发布为 _EARLY RELEASE FEATURE_. \* 它是 **没有** 完全符合HTTP/3 规格,缺少一些功能,如 [websockets](https://websockets)。 pet.whatwg.org/, [webtransport](https://w3c.github.io/webtransport/), 和 [推送回复](https://http3-explanined.haxx.se/en/h3/h3-pub). 相反,此版本的意图是使现有的 HTTP 请求/响应周期与HTTP/3实现地物对等。 在以后的几个版本中,将会添加更多的 HTTP/3 功能,并且它的 API 已经完成。 + +### 前后一致的异常名称 + +有些Sanic异常已更名为更符合标准HTTP响应名称。 + +- `InvalidUsage` >> `BadRequest` +- `MethodNotSupped` >> `MethodNotalled` +- `ContentRangeError` >> `RangeNotSatisfiable` + +所有旧名字都已被别名,并将保持向后兼容。 + +### 当前请求获取 + +类似于访问应用程序的 API (`Sanic.get_app()`),在请求处理程序之外有一个新方法来检索当前请求。 + +```python +从 sanic 导入请求 + +Request.get_current() +``` + +### 改进了 API 支持设置缓存控制头部 + +`file`响应助手有一些额外的参数,使它更容易处理设置 [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) header。 + +```python +文件( + ..., + last_modified=..., + max_age=...., + no_store=...., +) +``` + +### 自定义 `loads` 函数 + +就像Sanic支持全局设置一个自定义 `dumps`,您现在可以设置一个全局自定义 \`loads'。 + +```python +从 orjson 导入负载 + +应用 = Sanic("测试", loads=loads) +``` + +### 废弃和移除 + +1. _REMOVED_——申请者不得再选择退出申请登记册 +2. _REMOVED_ - 自定义异常处理程序在发送部分异常后将不再运行 +3. _REMOVED_ - Fallback 错误格式不能设置在 `ErrorHandler` 上,并且必须**仅** 在 `Config` 中设置 +4. _REMOVED_ - 不再允许为启动设置自定义 `LOGO` +5. _REMOVED_ - 旧的 `stream` 响应方便方法已被删除 +6. _REMOVED_ - `AsyncServer.init` 已被删除,不再是 `AsyncServer.app.state.is_started` 的别名 + +## 谢谢你 + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@amitay87](https://github.com/amitay87) +[@ashleysommer](https://github.com/ashleysommer) +[@azimovMichael](https://github.com/azimovMichael) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@kijk2869](https://github.com/kijk2869) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@sjsadowski](https://github.com/sjsadowski) +[@timmo001](https://github.com/timmo001) +[@zozzz](https://github.com/zozzz) + +--- + +如果您喜欢这个项目,请考虑捐款。 当然,我们喜欢代码贡献,但我们也喜欢任何形式的贡献。 考虑撰写一些文档,显示关闭的情况,加入对话并让您的声音为人所知,如果您能够:[金融贡献](https://opencollective.com/sanic-org/)。 diff --git a/guide/content/zh/release-notes/2022/v22.9.md b/guide/content/zh/release-notes/2022/v22.9.md new file mode 100644 index 0000000000..023e3598ba --- /dev/null +++ b/guide/content/zh/release-notes/2022/v22.9.md @@ -0,0 +1,349 @@ +--- +title: 22.9 版本 +--- + +# 22.9 版本 + +.. toc:: + +## 一. 导言 + +这是版本22 的第三次版本[发行周期](../../org/policies.md#release-schedule)。 版本22将在12月份的长期支持版本发布时“最后完成”。 + +## 了解什么 + +更多详细信息在 [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html)。 显著的新功能或破损功能以及升级内容... + +### ⚠️ _IMPORTANT_ - 新的工人管理器 :ro火箭: + +对Sanic服务器进行了全面检查,以便使其运作方式更加一致和灵活。 更多关于动机的详细信息载于[PR #2499](https://github.com/sanic-org/sanic/pull/2499),并在直播中讨论[YouTube上的讨论](https://youtu.be/m8HCO8NK7HE)。 + +这个**不适用于ASGI 模式的 Sanic** + +#### 变化概述 + +- 工人服务器将**总是**在子进程中运行。 + - 以前,这种情况可能会根据一个工人相对于许多工人的情况以及重新装载器的使用情况而改变。 这应导致更加可预测的发展环境,使其与生产对应方更加吻合。 +- Windows\*\*现在支持多个工作者。 + - 这是不可能的,因为Sanic依靠使用 "fork" 的 "multiprocessing" 模块,而这个模块在Windows上不可用。 + - 现在,Sanic将总是使用 `spawn` 。 这确实有一些明显的差异,特别是如果你正在全球范围内用 \`app.run' 运行 Sanic (见下面:升级问题)。 +- 应用程序实例现在有一个新的 `multiplexer` 对象,可以用来重启一个或多个工人。 例如,这可能是请求所触发的。 +- 有一个新的检查员可以提供关于您服务器状态的详细信息。 +- 卫生工作者管理人员可以任意进行工作。 + - 这允许开发者添加他们想要的 Sanic 内部的任何进程。 + - 可能的使用案例: + - 健康监视器,见Sanic Extensions + - 日志队列,见Sanic Extensions + - 分隔进程中的背景工作者队列 + - 正在运行另一个应用程序,就像一个bot +- 有一个新的监听器叫做`main_process_ready`。 确实只能用它来为萨尼克增加武断的过程。 +- 在员工之间传递共享对象。 + - Python 确实允许某些类型的对象在进程之间分享状态,不管是通过共享内存、管道等。 + - Sanic 现在允许在`app.shared_ctx`对象上共享这些类型的对象。 + - 因为此功能依靠Pythons `multiprocessing`库, 它显然只能在同一执行实例中的萨尼克工人之间分享状态。 这不是 \* 提供一个 API 用于多台机器之间的水平缩放。 + +#### 添加共享上下文对象 + +要在工作流程之间共享一个对象,它_MUST_将被分配到 `main_process_start` 监听器。 + +```python +从多处理导入队列 + +@app.main_process_start +async def main_process_start(app): + app.shared_ctx.queue = Queue() +``` + +"shared_ctx" 上的所有对象现在都可以在每个工作流程中使用。 + +```python +@app.before_server_starts +async def before_server_starts(app): + assert isinstance(app.shared_ctx.queue, Queue) + +@app.on_request +async def on_request(request): + assert isinstance(request.app.shared_ctx.queue, Queue) + +@app.get("/") +async def handler(request): + assert isinstance(request.app.shared_ctx.queue, Queue) +``` + +_注意:Sanic不会阻止您注册一个不安全的物品,但可能会警告您。 请注意不要仅仅添加一个普通列表对象,并期望它能起作用。 您应该了解如何分享进程之间的状态。_ + +#### 正在运行任意进程 + +Sanic可以为你任意运行任何过程。 它应该能够被 `SIGINT` 或 `SIGTERM` OS 信号拦截。 + +这些进程应该在 "main_process_ready" 监听器中注册。 + +```python +@app.main_process_ready +async def ready(app: Sanic, _): + app.manager.manager.management("MyProcess", my_process, {"foo": "bar"}) +# app.manager.manager.management(, , ) +``` + +#### 检查员 + +具有可选检查员的Sanic船。 这是一个特殊过程,允许CLI检查应用程序的运行状态和发布命令。 它目前只有当CLI运行于与Sanic实例相同的机器时才会起作用。 + +``` +sanic path.to:app --inspection +``` + +![Sanic 查看器](https://user-images.githubusercontent.com/166269/190099384-2f2f3fae-22d5-4529-b279-8446f6b5f9bd.png) + +新的 CLI 命令是: + +``` + --inspect 检查运行实例的状态 人类可读的 + --查看运行实例的状态 JSON 输出 + --触发器重新加载触发器进程以重新加载 + --触发器关闭所有进程 +``` + +默认情况下未启用此功能。 要有它,您必须选择: + +```python +app.config.INSPECTOR = True +``` + +\*注意:Sanic Extensions 提供了一个 [自定义请求](../basics/app.md#custom-requests) 类,它将添加一个请求对应到服务器状态。 + +#### 应用程序多路程序器 + +许多相同的信息和功能都可以在应用程序实例本身上获得。 应用程序实例上有一个新的 `multiplexer` 对象,它能够重启一个或多个工人。 并获取当前状态的信息。 + +您可以以 `app.multiplexer` 的身份访问它,或更可能通过它的短别名`app.m`访问它。 + +```python +@app.on_request +async def print_state(request: Request): + print(request.app.m.state) +``` + +#### 可能的升级问题 + +因为“fork”切换到“spawn”。如果您尝试在全局范围内运行服务器,您将收到一个错误。 如果你看到这样的东西: + +``` +sanic.exceptions.ServerError: Sanic server could not start: [Errno 98] Address already in use. +This may have happened if you are running Sanic in the global scope and not inside of a `if __name__ == "__main__"` block. +``` + +... 然后变化就很简单了。 请确认 `app.run` 是在方块内。 + +```python +if __name__ == "__main__": + app.run(port=9999, dev=True) +``` + +#### 选择退出新功能 + +如果你想要在没有新的进程管理器的情况下运行 Sanic,你可以轻松地使用旧版的运行器。 请注意支持他们**将来会被删除** 日期尚未确定,但有可能在2023年某个时候确定。 + +若要退出新服务器并使用遗产,根据您如何运行 Sanic,选择适当的方法: + +.. 列: + +``` +如果您使用CLI... +``` + +.. 列: + +```` +``` +sanic path.to:app --legacy +``` +```` + +.. 列: + +``` +如果您使用 `app.run`... +``` + +.. 列: + +```` +`` +app.run(..., legacy=True) +``` +```` + +.. 列: + +``` +如果您`app.prepare`... +``` + +.. 列: + +```` +`` +app.preparre(...) +Sanic.serve_legacy() +``` +```` + +同样,你可以强制Sanic在一个过程中运行。 然而,这意味着将无法访问自动重新加载器。 + +.. 列: + +``` +如果您使用CLI... +``` + +.. 列: + +```` +``` +sanic path.to:app --single-process +``` +```` + +.. 列: + +``` +如果您使用 `app.run`... +``` + +.. 列: + +```` +`` +app.run(..., sinle_process=True) +``` +```` + +.. 列: + +``` +如果您`app.prepare`... +``` + +.. 列: + +```` +`` +app.preparre(...) +Sanic.serve_single() +``` +```` + +### 中间件优先级 + +中间件是根据定义时的顺序执行的。 请求中间件按顺序执行,而中间件则对应。 如果您的订单严格基于导入订单,比如全局变量,这可能会产生不幸的影响。 + +增加的一个新内容是打破严格的结构,并允许将优先权分配给一个中间体。 中间件定义的数字越高,执行顺序就越早。 这适用于**既有**请求也有中途响应。 + +```python +@app.on_request +async def low_priority(_): + ... + +@app.on_request(priority=10) +async def hig_priority(_): +... +``` + +在上面的例子中,即使首先定义了 `low_priority` ,但是`high_priority` 将先运行。 + +### 自定义 `loads` 函数 + +Sanic 支持在实例化应用程序时添加 [自定义 `dumps` 函数] (https://sanic.readthedocs.io/en/stable/sanic/api/app.html#sanic.app.Sanic)。 同一功能已扩展到 `loads`, 当反序列化时将使用它。 + +```python +从 json 导入负载 + +Sanic("测试", loads=loads) +``` + +### Websocket 对象现在是可以迭代的 + +你不能在 Websocket`对象上调用`recv`方法,而是可以在`for\` 循环中迭代。 + +```python +从 sanic 导入请求, Websocket + +@app.websocket("/ws") +async def ws_echo_handler(request: request, ws: Websocket): + async for msg in w: + request ws.send(msg) +``` + +### 以304个静态文件作出适当响应 + +当服务于静态文件时,Sanic 服务器可以使用 `If-Modified-Since` 响应而不是重置一个文件。 + +### 两个新的信号来包装处理器执行 + +添加了两个新的 [signals](../advanced/signals.md),将执行请求处理器。 + +- `http.handler.befor` - 在请求中间件后但在路由处理程序之前运行 +- `http.handler.after ` - 在路由处理程序后运行 + - 在_大多数_情况下,这也意味着它将在中间反应之前运行。 然而,如果你从路由处理器内部调用 `request.respond` ,那么你的中间件将会先来到去。 + +### HTTP 方法信息的新请求属性 + +HTTP规格界定了哪种HTTP方法:安全、易感染性和可缓存。 添加了新的属性,以响应布尔标识来帮助根据方法识别请求属性。 + +```python +is_safe +request.is_idempotent +request.is_cacheable +``` + +### 🚨 _Breaking变换_ - 改进的取消请求异常 + +在先前版本的 Sanic 中,如果抓到一个 `取消的错误` ,它可能会泡沫化并导致服务器以 `503` 响应. 这并不总是期望的结果,它防止了在其他情况下使用这一错误。 因此,Sanic现在将使用一个子类的 `CancelledError` ,叫做`RequestCancelled`。 除非你明确依赖旧的行为,否则它可能不会产生什么影响。 + +欲了解这些属性的细节详情,请签出[MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTPMethods)。 + +### 新的废弃警告过滤器 + +您可以使用[标准库警告过滤值](https://docs.python.org/3/library/warnings.html#the-warnings-filter)控制来自Sanic的废弃警告的级别。 默认为“一次”\`。 + +```python +app.config.DEPRECATION_FILTER = "忽略" +``` + +### 废弃和移除 + +1. _删除_ - 重复的路由名称已被弃用,并将在 v23.3 中删除。 +2. _DEPRECATED_-注册重复的异常处理程序已被弃用,并将在 v23.3 中删除。 +3. _REMOVED_ - `route.ctx` 没有由 Sanic 设置,因此是用户的空白对象... + - `route.ctx.ignore_body` >> `route.extra.ignore_body` + - `route.ctx.stream` >> `route.extra.stream` + - `route.ctx.hosts` >> `route.extra.hosts` + - `route.ctx.static` >> `route.extra.static` + - `route.ctx.error_格式` >> `route.extra.error_格式` + - `route.ctx.websocket` >> `route.extra.websocket` +4. _REMOVED_ - 只有`app.debug` 是 READ-唯一的 +5. _REMOVED_ - `app.is_running` 已移除 +6. _REMOVED_ - `app.is_stopping` 已删除 +7. _REMOVED_ - `Sanic._uvloop_setting` 已删除 +8. _REMOVED_ - 如果不是大写,预固定环境变量将被忽略 + +## 谢谢你 + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@azimovMichael](https://github.com/azimovMichael) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@huntzhan](https://github.com/huntzhan) +[@monosans](https://github.com/monosans) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@seemethere](https://github.com/seemethere) +[@sjsadowski](https://github.com/sjsadowski) +[@timgates42](https://github.com/timgates42) +[@Tronic](https://github.com/Tronic) + +--- + +如果您喜欢这个项目,请考虑捐款。 当然,我们喜欢代码贡献,但我们也喜欢任何形式的贡献。 考虑撰写一些文档,显示关闭的情况,加入对话并让您的声音为人所知,如果您能够:[金融贡献](https://opencollective.com/sanic-org/)。 diff --git a/guide/content/zh/release-notes/2023/v23.12.md b/guide/content/zh/release-notes/2023/v23.12.md new file mode 100644 index 0000000000..7741fac421 --- /dev/null +++ b/guide/content/zh/release-notes/2023/v23.12.md @@ -0,0 +1,185 @@ +--- +title: 第23.12版 +--- + +# 第23.12版 (LTS) + +.. toc:: + +## 一. 导言 + +这是第23版的最后版本[发行周期](../../organization/policies.md#release-schedule)。 它被指定为**长期支持("LTS") release**, 这意味着它将按照支持政策的规定得到两年的支持。 如果您遇到任何问题,请在 [GitHub](https://github.com/sanic-org/sanic/issues/new/seloe.) + +## 了解什么 + +更多详细信息在 [Changelog](../changelog.html)。 显著的新功能或破损功能以及升级内容: + +### :party_poper: 文档现在使用 {span:has-text-primary:Sanic} + +![](http://127.0.0.1:8000/assets/images/sanic-framework-logo-circle-128x128.png) + +您可以阅读[所有关于如何](/en/build-with-sanic.html),但我们将此文档网站转换为使用 SHH 🤫 stack。 + +- [Sanic](https://sanic.dev) +- [html5tagger](https://github.com/sanic-org/html5tagger) +- [HTMX](https://htmx.org/) + +### 👶 _BETA_ 欢迎使用 Sanic 交互式控制台 + +这是正确的,现在萨尼克带着REPL! + +![](/assets/images/repli.png) + +当使用 Sanic CLI 时,你可以通过 "--repli" 参数来自动运行一个交互式控制台。 这在开发时非常有用,允许您访问应用程序实例。 还有一个内置客户端已启用,可以将 HTTP 请求发送到运行实例。 + +如果您使用 "--dev" 标志,此功能默认是准启用的。 它将不会彻底运行REPL, 它将启动进程,并允许您随时在键盘上点击\`'。 + +_这仍然处于BETA模式。 我们希望你让我们了解任何升级请求或问题。_ + +### Python 3.12 支持 + +我们已经将 Python 3.12 添加到支持的版本。 + +### 启动并重启任意进程 + +使用 [multiplexer](../../guide/running/manager.md#access-to-the-multiplexer),您现在可以开始并重启任意或原有的过程。 这使得多路程序和工人管理员的运作方式具有以下新功能: + +1. `multiplexer.restt("")` 现在将重新启动一个目标的单一进程 +2. `multiplexer.manag(...)` 是一种完全类似于`manager.management(...)` 的新方法 +3. `manag` 方法现在有额外的关键字参数: + - `tracked` - 进程完成后是否跟踪进程状态 + - `restable` - 是否允许重启进程 + - `auto_start` - 是否在创建后立即启动此进程 + +```python +def task(n: int = 10, **kwargs): + print("TASK STARTED", kwargs) + for i in range(n): + print(f"Running task - Step {i+1} of {n}") + sleep(1) + +@app.get("/restart") +async def restart_handler(request: Request): + request.app.m.restart("Sanic-TEST-0") + return json({"foo": request.app.m.name}) + + +@app.get("/start") +async def start_handler(request: Request): + request.app.m.manage("NEW", task, kwargs={"n": 7}, workers=2) + return json({"foo": request.app.m.name}) + +@app.main_process_ready +def start_process(app: Sanic): + app.manager.manage("TEST", task, kwargs={"n": 3}, restartable=True) +``` + +### 已排序的听众和信号 + +在 [v22.9](../2022/v22.9.md) 中,Sanic 增加了中间件的优先顺序,以允许任意排序中间件。 这一概念现已扩大到听众和信号。 这将允许在创建时指定一个优先级编号,这将覆盖其在执行时间段中的默认位置。 + +```python +@app.before_server_start(priority=3) +异步采样(app): +... +``` + +数字越高,它将获得越高的优先级。 总体上,决定执行顺序的规则如下: + +1. 按降序排序 +2. 蓝图监听器之前的应用程序监听器 +3. 注册订单 + +_记住,一些监听器被反向执行_ + +### Websocket 信号 + +我们为websocket添加了三个新信号: + +1. `websocket.handler.before` +2. `websocket.handler.after ` +3. `websocket.handler.exception` + +```python +@app.signal("websocket.handler.before") +async def ws_before(request: Request, websocket: Websocket): + + +@app.signal("websocket.handler.after ") +async def ws_after (request: Request, websocket: Websocket): + . + +@app.signal("websocket.handler. xception") +async def ws_exception( + request: Request, websocket: Websocket, exception: Exception +): +... +``` + +![](https://camo.githubusercontent.com/ea2894c88bedf37a4f12f1296569e8fd14bfceaa36d4452c7b7a1869d2f1cdb18/687477733a2f2f7a692f77732d73696e616c732e706e67) + +### 简化信号 + +“Sanic”总是强制执行三部分命名标记协议:`one e.tw.three`。 然而,现在你可以创建更简单的名字,这只是一个部件。 + +```python +@app.signal("foo") +async def foo(): + ... +``` + +你可以让这个部件与正常信号和路由一样动态: + +```python +@app.signal("") +async def 处理器(**kwargs): + print("收到foobar 信号") + print(kwargs) + + +@app. oute("/") +异步测试(请求: 请求): + 等待request.appailch("foobar") + return json({"hello": "world"}) +``` + +如果您需要有多个动态信号,那么您应该使用较长的三部分格式。 + +### `event` 方法已被更新 + +对 `app.event()` 和 `bluprint.event()`都做了一些修改。 + +- `condition` 和 `exclusive` 是控制匹配条件的关键字 (类似于“signal()\` 方法) +- 您可以通过 `str` 或 `Enum` (就像`signal()`) +- 返回传递到调度()方法的上下文副本 + +### 重新加载触发器文件已更改 + +由读取器更改的文件现在被注入到侦听器。 这将允许触发器在知道那些更改过的文件的情况下做一些事情。 + +```python +@app.after_reload_tend +async def after _reload_trigger(_, 更改): + 打印(更改) +``` + +## 谢谢你 + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@freddiewanah](https://github.com/freddiewanah) +[@gluhar2006](https://github.com/gluhar2006) +[@iAndriy](https://github.com/iAndriy) +[@MichaelHinrichs](https://github.com/MichaelHinrichs) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@sjsadowski](https://github.com/sjsadowski) +[@talljosh](https://github.com/talljosh) +[@tjni](https://github.com/tjni) +[@Tronic](https://github.com/Tronic) + +--- + +如果您喜欢这个项目,请考虑捐款。 当然,我们喜欢代码贡献,但我们也喜欢任何形式的贡献。 考虑撰写一些文档,显示关闭的情况,加入对话并让您的声音为人所知,如果您能够:[金融贡献](https://opencollective.com/sanic-org/)。 diff --git a/guide/content/zh/release-notes/2023/v23.3.md b/guide/content/zh/release-notes/2023/v23.3.md new file mode 100644 index 0000000000..4c5c559112 --- /dev/null +++ b/guide/content/zh/release-notes/2023/v23.3.md @@ -0,0 +1,421 @@ +--- +title: 23.3 版本 +--- + +# 23.3 版本 + +.. toc:: + +## 一. 导言 + +这是第23版的第一次版本[发行周期](../../org/policies.md#release-schedule)。 因此含有一些废弃的偏见,希望有些_小_断裂的修改。 如果您遇到任何问题,请在 [GitHub](https://github.com/sanic-org/sanic/issues/new/selecte) 上提出一个问题。 + +## 了解什么 + +更多详细信息在 [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html)。 显著的新功能或破损功能以及升级内容... + +### 较好的追踪格式 + +上合组织在GitHub上通过了两个项目,将其纳入Sanic命名空间: [tracerite](https://github.com/sanic-org/tracerite) 和 [html5tagger](https://github.com/sanic-org/html5tagger)。 这些项目团队可以提供和令人难以置信的新错误页面,并提供更多详细信息来帮助调试过程。 + +这是由方框提供的,并将调整以只显示相关信息,不管是在DEBUG 更多还是PROD 模式。 + +.. 列: + +``` +**Using PROD mode** +![image](/assets/images/error-html-no-debug.png) +``` + +.. 列: + +``` +**Using DEBUG mode** +![image](/assets/images/error-html-debug.png) +``` + +浅暗模式的 HTML 页面可用,将被隐含使用。 + +### 目录上的基本文件浏览器 + +当使用静态处理器的目录时,Sanic可以被配置为显示基本文件浏览器,而不是使用 `directory_view=True`。 + +.. 列: + +```` +```python +app.static("/uploads/", "/path/to/dir/", directory_view=True) +``` +```` + +.. 列: + +``` +![image](/assets/images/directory-view.png) +``` + +浅暗模式的 HTML 页面可用,将被隐含使用。 + +### 使用 Python 的 HTML 模板 + +因为Sanic正在使用 [html5tagger](https://github)。 om/sanic-org/html5tagger,用于渲染[新的错误页面](#nicer-traceback-forming), 您现在有可用的软件包可以轻松生成Python代码的 HTML 页面: + +.. 列: + +```` +```python +from html5tagger import Document +from sanic import Request, Sanic, html + +app = Sanic("测试应用") + +@app. et("/") +async def 处理器(请求: 请求): + doc = Document("我的网站") + doc. 1 ("你好,世界。") + 与doc. able(id="data "): + doc.tr.th("First").th("Second"). h("Third") + doc.tr.td(1).td(2).td(3) + doc. (tclass_="text")("A paragraph with ") + doc.ahhref="/files")("a link")(" and ").em("格式化") + return html(doc) +``` +```` + +.. 列: + +```` +```html + + +我的网站 +

Hello, world。

+ + + +
First + Second + Third +
1 + 2 + 3 +
+

+ A paragraph with a link and forming +``` +```` + +### 自动索引服务在静态处理程序上可用 + +Sanic 现在可以配置为静态目录时的索引文件。 + +```python +app.static("/assets/", "/path/to/some/dir", index="index.html") +``` + +当使用上述内容时,`http://example.com/assets/`将自动为位于该目录的`index.html`文件服务。 + +### 简单的 CLI 目标 + +Sanic 应用程序通常使用变量`app`作为应用程序实例。 由于这个原因, CLI 应用程序目标 (`sanic` CLI 命令的第二值) 现在试图根据目标推断应用程序实例。 如果目标是包含一个`app`变量的模块,它将使用它。 + +现在有四种可能的方式从地方倡议启动一个Sanic应用程序。 + +#### 1. 应用程序实例 + +正常情况下,为模块和应用程序实例提供路径将按预期工作。 + +```sh +sanic path.to.module:app # 全局应用实例 +``` + +#### 2. 应用工厂: + +以前,为了服务出厂模式,你需要使用 "--factory" 旗帜。 现在可以省略。 + +```sh +sanic path.to.module:create_app # 出厂模式 +``` + +#### 3. 启动Sanic 简单服务器路径 + +同样,要启动 Sanic 简单服务器 (服务静态目录),您以前需要使用 "--simple" 旗帜。 现在这可以省略,而只是提供到目录的路径。 + +```sh +sanic ./path/to/directory/ # 简单服务 +``` + +#### 4. 包含一个`app`变量的 Python 模块 + +如上所述,如果目标是包含一个`app`变量的模块, 它将使用那个(假设`app`变量是一个 `Sanic` 实例)。 + +```sh +带有应用程序实例的 sanic path.to.module # 模块 +``` + +### 更方便的 cookie 设置和删除 + +旧的 cookie 模式非常糟糕,很难找。 它看起来不像普通的 Python ,因为那个尺寸下“魔法”。 + +.. 列: + +``` +:fac_screaming_in_fear: 这不是直观的,对新来者来说是混淆不清的。 +``` + +.. 列: + +```` +```python +response = text("在这个响应中有一个 cookie ") +response.cookies["test"] = "它正常工作!" +响应。 ookies["test"]["domain"] = ".yummy-yummy-cookie.com" +response.cookies["test"]["httuponly"] = True +``` +```` + +现在已经有了新的方法(以及完全全面的`Cookie`和CookieJar\`对象)来使这个过程更加方便。 + +.. 列: + +``` +😌 Ahh... 非常好。 +``` + +.. 列: + +```` +```python +response = text("在这个响应中有cookie") +response。 dd_cookie( + "test", + "it 工作!", + domain=". ummy-yummy-cookie.com", + httponly=True +) +``` +```` + +### 更好的 cookie 兼容性 + +Sanic 增加了对 [cookie 前缀](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#cookie_prefixes)的支持,使读取和写下这些值看起来变得越来越简单。 + +设置cookie时... + +```py +响应.cookies.add_cookie("foo", "bar", host_prefix=True) +``` + +这将创建预设的 cookie: `__Host-foo`。 然而,当访问传入请求时您可以在不知道头部存在的情况下访问cookie。 + +```py +cookie.get("foo") +``` + +还应该注意到,cookie可以作为属性访问,如 [headers](#access-any-header-as a property)。 + +```python +Cookie.foo +``` + +而且,cookie类似于“request.args”和“request.form”对象,因为可以使用 `getlist` 获取多个值。 + +```py +cookie.getlist("foo") +``` + +还添加了支持创建 [分区的 cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#partitioned_cookie)。 + +```py +响应.cookies.add_cookie(...,分区=True) +``` + +### 🚨 _Breaking变换_ - 更加一致和强大的 `SanicException` + +Sanic已经有一段时间将`SanicException`作为基本类别的例外。 可将其扩展到添加`status_code`等(详见http://localhost:8080/en/guide/best practices/exceptions.html)。 + +**NOW**,使用所有不同的例外已变得更加容易。 通常使用的异常可以直接从 root 级模块导入。 + +```python +从 NotFound, 未经授权, BadRequest, ServerError +``` + +此外,所有这些参数都可以作为关键词参数用于每个异常类型: + +| 参数 | 类型 | 描述 | +| --------- | ------ | -------------------------- | +| 安静\` | `bool` | 从日志中取消跟踪 | +| `context` | `dict` | 错误页面_总是显示额外信息_ | +| `extra` | `dict` | 在 \*DEBUG 模式下的错误页面中显示的额外信息 | +| `headers` | `dict` | 响应中发送的更多信头 | + +所有这些本身都不是新的特征。 然而,它们在如何使用它们方面更加一致,从而创造了一种直接控制错误响应的强大方式。 + +```py +升起服务器错误(headers={"foo": "bar"}) +``` + +这种变化的一个重要部分是,有些原先的立场论点现在只是关键词。 + +鼓励您查看[API 文档](https://sanic.readthedocs.io/en/stable/sanic/api/exceptions.html#module-sanic.exceptions)中每个错误的具体实现。 + +### 🚨 _Breaking变换_ - 刷新`Request.accept` 功能以提高性能和投影兼容性 + +解析`接受`标题'到 `Request.accept` 访问器已经改进。 如果你正在使用这个财产并依靠它的平等操作,这种情况已经改变。 您可能应该转换为 `request.accept.match()` 方法。 + +### 以属性访问任何页眉内容 + +为了简化对页眉的访问权限,您可以访问原始(未解析的)页眉版本作为属性。 标题的名称是所有小写字母中的属性的名称, 并切换任何连字符(`-`) 到下划线 (`_`). + +例如: + +.. 列: + +```` +``` +GET /foot/bar HTTP/1.1 +主机:localhost +User-Agent: curl/7.88.1 +X-Request-ID: 123ABC +``` +```` + +.. 列: + +```` +```py +request.headers.host +request.headers.user_agent +request.headers.x_request_id +``` +```` + +### 默认消耗`DELETE` 体 + +默认情况下,一个 `DELETE` 请求的正文将会被消耗并读取到 `Request` 对象。 这将使`body`像`POST`、`PUT`和`PATCH`请求一样无需采取任何进一步行动。 + +### 自定义 `CertLoader` 用于直接控制创建 `SSLContext` + +有时,您可能想要创建自己的 `SSLContext` 对象。 要做到这一点,你可以创建你自己的 `CertLoader` 子类,生成你所需的上下文对象。 + +```python +从 sanic.worker.loader 导入 CertLoader + +类 MyCertLoader(CertLoader): + def load(self, app: Sanic) -> SSLContext: + ... + +app = Sanic(...certloader_class=MyCertLoader) +``` + +### 废弃和移除 + +1. _DEECATED_-Dict-样式 cookie 设置 +2. _DEECATED_ - 使用 JSON 数据请求使用JSON 错误格式化一个因素时使用 JSON 数据 +3. _REMOVED_ - Remove deprecated `__blueprintname__` property +4. _删除_ - 重复路由名称 +5. _REMOVED_ - 重复异常处理器定义 +6. _REMOVED_ - 带着旗帜的检查器 CLI +7. _REMOVED_ - 旧服务器(包括`sanic.server.serve_single` 和 `sanic.server.serve_multiple`) +8. _REMOVED_ - 使用字节字符串的目录 +9. _REMOVED_ - `Request.request_middleware_started` +10. _REMOVED_ - `Websocket.connection` + +#### 不再允许重复的路由名称 + +在第22.9版中,Sanic宣布说,V23.3不允许使用重复名称注册路线。 如果你看到以下错误,它是因为这种改变: + +> 异常。服务器错误:检测到重复的路由名称:有人App.some_handler。 您应该使用 "name" 参数来明确重命名其中一个或多个, 或更改来自类和函数名称的隐含名称。 欲了解更多详情,请访问https://sanic.dev/en/guide/release-notes/v23.3.html#carbated-route-names-are-no longer-allowed + +如果你看到这一点,你应该选择为你的路线使用明确的名称。 + +.. 列: + +```` +**BAD** +```python +app = Sanic("有人应用") + +@app.get("/") +@app.get("/foo") +async def handler(请求): +``` +```` + +.. 列: + +```` +**GOOD** +```python +app = Sanic("有人应用") + +@app.get("/", name="root") +@app.get("/foo", name="foo") +async def handler(请求): +``` +```` + +#### 响应 cookie + +响应 cookie 仅为了兼容起作用的 `dict` 。 在版本24.3中,所有的`dict`方法将被删除,应答的 cookie 将仅为对象。 + +因此,如果您使用此模式来设置 cookie 属性,您需要在版本 24.3 之前升级它。 + +```python +resp = HTTPResponse() +resp.cookies["foo"] = "bar" +resp.cookies["foo"]["httponly"] = True +``` + +相反,您应该使用 "add_cookie" 方法: + +```python +resp = HTTPResponse() +resp.add_cookie("foo", "bar", httponly=True) +``` + +#### 请求 cookie + +Sanic增加了对阅读重复的 cookie 密钥的支持,以便更加符合RFC 规格。 To retain backwards compatibility, accessing a cookie value using `__getitem__` will continue to work to fetch the first value sent. 因此,在第23.3版和以前的版本中,这将是“True”。 + +```python +cookie["foo"] == "bar" +坚持request.cookies.get("foo") == "bar" +``` + +版本23.3添加了 `getlist` + +```python +cookies.getlist("foo") == ["bar"] +``` + +如上文所述,`get` 和 `getlist` 两个方法都是可用的,类似于它们在其他请求属性上的存在方式(`request.args`, `request.form`, 等等)。 Starting in v24.3, the `__getitem__` method for cookies will work exactly like those properties. This means that `__getitem__` will return a list of values. + +因此,如果你只能依靠此功能返回一个值,你应该在v24.3之前升级到以下模式。 + +```python +cookies["foo"] == ["bar"] +坚持request.cookies.get("fo") == "bar" +signing request.cookies.getlist("foo") == ["bar"] +``` + +## 谢谢你 + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@deounix](https://github.com/deounix) +[@Kludex](https://github.com/Kludex) +[@mbendiksen](https://github.com/mbendiksen) +[@prryplatypus](https://github.com/prryplatypus) +[@r0x0d](https://github.com/r0x0d) +[@SaidBySolo](https://github.com/SaidBySolo) +[@sjsadowski](https://github.com/sjsadowski) +[@stricaud](https://github.com/stricaud) +[@Tracyca209](https://github.com/Tracyca209) +[@Tronic](https://github.com/Tronic) + +--- + +如果您喜欢这个项目,请考虑捐款。 当然,我们喜欢代码贡献,但我们也喜欢任何形式的贡献。 考虑撰写一些文档,显示关闭的情况,加入对话并让您的声音为人所知,如果您能够:[金融贡献](https://opencollective.com/sanic-org/)。 diff --git a/guide/content/zh/release-notes/2023/v23.6.md b/guide/content/zh/release-notes/2023/v23.6.md new file mode 100644 index 0000000000..ea2e4de4c2 --- /dev/null +++ b/guide/content/zh/release-notes/2023/v23.6.md @@ -0,0 +1,199 @@ +--- +title: 第23.6版 +--- + +# 第23.6版 + +.. toc:: + +## 一. 导言 + +这是第二次版本的 23 [发行周期] (../../org/policies.md#release-schedule)。 如果您遇到任何问题,请在 [GitHub](https://github.com/sanic-org/sanic/issues/new/selecte) 上提出一个问题。 + +## 了解什么 + +更多详细信息在 [Changelog](https://sanic.readthedocs.io/en/stable/sanic/changelog.html)。 显著的新功能或破损功能以及升级内容... + +### 移除 Python 3.7 支持 + +Python 3.7将于2023-06-27年到达预定的上游报废。 Sanic 正在放弃对 Python 3.7的支持,需要 Python 3.8或更高版本。 + +见[No. 27777](https://github.com/sanic-org/sanic/pull/2777)。 + +### 解决pypy 兼容性问题 + +在 `os` 模块中添加了一个小补丁,让Sanic 再次与 PyPy 一起运行。 此操作将缺失的 `readlink` 函数(PyPy `os` 模块中缺失) 替换为 `os.path.realpath` 函数,这个函数具有相同的目的。 + +见[No. 2782](https://github.com/sanic-org/sanic/pull/2782)。 + +### 添加自定义输入到配置和 ctx 对象 + +`sanic.Sanic`和`sanic.Request`的对象已经成为通用类型,使完全输入`config`和`ctx`对象更方便。 + +在最简单的形式中,`Sanic`对象被输入为: + +```python +from sanic import Sanic +app = Sanic("test") +reveal_type(app) # N: Revealed type is "sanic.app.Sanic[sanic.config.Config, types.SimpleNamespace]" +``` + +.. 提示:备注 + +``` +应该注意到,有*否*要求使用通用类型。默认类型是 `sanic.config.Config` 和 `types.SimpleNamespace` 。 这个新功能只是那些想要使用它的人的一个选项,并且“应用:Sanic”和“request:Request”应该正常工作。 +``` + +现在可以有一个全型的 `app.config`, `app.ctx`, 和 `request.ctx` 对象,尽管它们是普通的。 这样可以更好地结合开发者体验的自动完成工具。 + +```python +从 Sanic 导入请求, Sanic +来自Sanic。 onfig importing config + +class CustomConfig(Config): + pass + +class Foo: + pass + +class RequestContext: + foo: Foo + +class CustomRequest(Request[Sanic[CustomConfig, Foo], RequestContext]): + @static方法 + def make_context() -> RequestContext: + ctx = RequestContext() + ctx. oo = Foo() + return ctx + +app = Sanic( + "test", config=CustomConfig(), ctx=Foo(), request_class=CustomRequest +) + +@app. et("/") +async def 处理器(请求:自定义请求): +... +``` + +作为一个附带效果,现在`request.ctx`已经启动,这会在未使用 `request.ctx`时减少一些间接费用。 + +您在上述片段中可能注意到的另一个变化是 `make_context` 方法。 这个新方法可以用于自定义 `Request` 类型来注入不同于 `SimpleNamespace` 的对象,类似于Sanic 允许自定义应用程序上下文对象一段时间。 + +关于更详细的讨论,见[自定义的应用程序](../basics/app.md#custom-typed应用程序)和[自定义的请求](../basics/app.md#custom-typedrequest)。 + +见[第2785号](https://github.com/sanic-org/sanic/pull/2785)。 + +### 通用异常信号 + +在服务器运行时为 **ALL** 添加了一个新的异常信号:`server.exception.reporting`。 这是一个普遍的信号,将因提出的任何例外而发出,并作为其自身的任务予以发出。 这意味着它将_不_阻止请求处理器,并且将_不_受到任何中间件的影响。 + +这对于捕捉请求处理程序之外可能出现的异常(例如信号中的异常)非常有用, 它旨在为用户创建一个一致的错误处理体验。 + +```python +从sanic.signatures + +@app.signal(Event.SERVER_LIFECYCLE_EXCEPTION) +async def catch_any_exception(app: Sanic, exception: + app.ctx.my_error_reporter_utility.error(异常) +``` + +这种模式可以通过新的装饰符`@app.report_exception`来简化: + +```python +@app.report_exception +async def catch_any_exception(app: Sanic, exception): + print("捕捉异常:", 异常) +``` + +应该指出的是,这种情况是在一个后台任务中发生的,并且因为错误响应被操纵而**无**。 仅用于报告、记录或其他应用发生错误时应触发的目的。 + +见[第2724](https://github.com/sanic-org/sanic/pull/2724)和[第2792](https://github.com/sanic-org/sanic/pull/2792)。 + +### 将名称前缀添加到 BP 组 + +Sanic对重复的路线名称提出警告已有一段时间,并开始在 [v23.3](https://sanic.dev/en/guide/release-notes/v23.3.html#disposations-and-removals)强制执行路线名称唯一性。 这使蓝图组成复杂化。 + +为了缓解这个问题,添加了蓝图组的新名称前缀参数。 它允许嵌套蓝图和组使它们可以合成的。 + +添加是这个代码段中显示的新的 `name_prefix` 参数。 + +```python +bp1 = Bluprint("bp1", url_prefix="/bp1") +bp2 = Bluprint("bp2", url_prefix="/bp2") + +bp1.add_route(lambda _..., "/"name="route1") +bp2.add_route(Lambda _..., "/", name="route2") + +group_a = 蓝图。 roup( + bp1, bp2, url_prefix="/group-a", name_prefix="group-a" +) +group_b = 蓝图。 roup( + bp1, bp2, url_prefix="/group-b", name_prefix="group-b" +) + +app = Sanic("TestApp") +app.blueprint(group_a) +app.bluprint(group_b) +``` + +建造的路线将命名如下: + +- `TestApp.group-a_bp1.route1` +- `TestApp.group-a_bp2.route2` +- `TestApp.group-b_bp1.route1` +- `TestApp.group-b_bp2.route2` + +见[第2727号](https://github.com/sanic-org/sanic/pull/2727)。 + +### 添加 `request.client_ip` + +Sanic 已经引入了 `request.client_ip` ,这是一个新的访问器,从本地和代理数据中提供客户端的 IP 地址。 它允许直接在互联网上或代理后面运行该应用程序。 这相当于`request.remote_addr 或 request.ip`, 提供客户端IP, 无论应用程序是如何部署的。 + +见[第2790号](https://github.com/sanic-org/sanic/pull/2790)。 + +### 将 `KEEP_ALIVE_TIMEOUT` 默认值增加到120秒 + +默认的`KEEP_ALIVE_TIMEOUT`值从5秒变为120秒。 它当然仍然是可配置的,但这种变化将提高长期延迟连接的性能, 在重新连接费用较高的地方,更适合用户流浏览页面长度超过5秒。 + +Sanic历来使用5次超时来迅速关闭闲置连接。 选择的 **120 秒** 的值确实大于 Nginx 默认值75,且与 Caddy 服务器默认值相同。 + +Related to [#2531](https://github.com/sanic-org/sanic/issues/2531) and +[#2681](https://github.com/sanic-org/sanic/issues/2681). + +见[No. 2670](https://github.com/sanic-org/sanic/pull/2670)。 + +### 尽早设置多处理开始方法 + +由于Python如何处理 `multiprocessing` ,它可能会使一些用户混淆如何正确创建同步的初级读物。 这是因为Sanic是如何创建多处理环境的。 此更改及早设置起始方法,以使创建的任何原始都能正确地附加到正确的上下文中。 + +对大多数用户来说,这不应明显或具有影响。 但是,它应该使这种创造变得更加容易,并且能够如预期的那样发挥作用。 + +```python +从多处理导入队列 + +@app.main_process_start +async def main_process_start(app): + app.shared_ctx.queue = Queue() +``` + +见[No. 2776](https://github.com/sanic-org/sanic/pull/2776)。 + +## 谢谢你 + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@chuckds](https://github.com/chuckds) +[@deounix](https://github.com/deounix) +[@guacs](https://github.com/guacs) +[@liamcoatman](https://github.com/liamcoatman) +[@moshe742](https://github.com/moshe742) +[@prryplatypus](https://github.com/prryplatypus) +[@SaidBySolo](https://github.com/SaidBySolo) +[@Thirumalai](https://github.com/Thirumalai) +[@Tronic](https://github.com/Tronic) + +--- + +如果您喜欢这个项目,请考虑捐款。 当然,我们喜欢代码贡献,但我们也喜欢任何形式的贡献。 考虑撰写一些文档,显示关闭的情况,加入对话并让您的声音为人所知,如果您能够:[金融贡献](https://opencollective.com/sanic-org/)。 diff --git a/guide/content/zh/release-notes/2023/v23.9.md b/guide/content/zh/release-notes/2023/v23.9.md new file mode 100644 index 0000000000..630da3625b --- /dev/null +++ b/guide/content/zh/release-notes/2023/v23.9.md @@ -0,0 +1,7 @@ +--- +title: Version 23.9 +--- + +# Version 23.9 + +_Due to circumstances at the time, v.23.9 was skipped._ diff --git a/guide/content/zh/release-notes/2024/v24.12.md b/guide/content/zh/release-notes/2024/v24.12.md new file mode 100644 index 0000000000..8fe38230cf --- /dev/null +++ b/guide/content/zh/release-notes/2024/v24.12.md @@ -0,0 +1,80 @@ +--- +title: 24.12 版本 +--- + +# 24.12 版本 + +.. toc:: + +## 一. 导言 + +这是版本 24 [发行周期] (../../organization/policies.md#release-schedule) 的第一次版本。 与过去几年相比,释放24人的队伍可能略有改变。 请确保在Discord服务器上更新最新信息。 如果您遇到任何问题,请在 [GitHub](https://github.com/sanic-org/sanic/issues/new/selecte) 上提出一个问题。 + +## 了解什么 + +更多详细信息在 [Changelog](../changelog.html)。 显著的新功能或破损功能以及升级内容: + +### 👶 _BETA_自定义 CLI 命令 + +现在`sanic` CLI 实用程序允许调用自定义命令。 命令可以使用下面的装饰符语法添加。 + +```python +@app.command +async def foo(one, two: str, three: str = "..."): + logger.info(f"FOO {one=} {two=} {three=}") + + +@app.command +def bar(): + logger.info("BAR") + + +@app.command(name="qqq") +async def baz(): + logger.info("BAZ") +``` + +这些命令使用下面的`exec`命令。 + +```sh +sanic server:app exec [--arg=value] +``` + +函数签名中的任何参数都将作为参数添加。 例如: + +```sh +sanic server:app exec command --one=1 --two=2 --three=3 +``` + +.. 警告:: + +``` +This is in **BETA** and the functionality is subject to change in upcoming versions. +``` + +### 添加 Python 3.13 支持 + +我们已经将 Python 3.13 添加到支持的版本。 + +### 删除 Python 3.8 支持 + +Python 3.8寿命终结。 Sanic 正在放弃对 Python 3.8的支持,需要 Python 3.9 或更高版本。 + +### 旧响应 cookie 存取器已删除 + +在 v23 之前,在 "Response" 对象上的 cookie 被设置为字典对象并被访问。 在 v23.3 中添加了新的 [方便方法] (../2023/v23.3.html#more-facilent-methods-for-setting-deleting-cookies)时,这种做法被废弃。 旧模式已被移除。 + +## 谢谢你 + +Thank you to everyone that participated in this release: :clap: + +[@ahopkins](https://github.com/ahopkins) +[@C5H12O5](https://github.com/C5H12O5) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@HyperKiko](https://github.com/HyperKiko) +[@imnotjames](https://github.com/imnotjames) +[@pygeek](https://github.com/pygeek) + +--- + +如果您喜欢这个项目,请考虑捐款。 当然,我们喜欢代码贡献,但我们也喜欢任何形式的贡献。 考虑撰写一些文档,显示关闭的情况,加入对话并让您的声音为人所知,如果您能够:[金融贡献](https://opencollective.com/sanic-org/)。 diff --git a/guide/content/zh/release-notes/2024/v24.6.md b/guide/content/zh/release-notes/2024/v24.6.md new file mode 100644 index 0000000000..57b0933ce8 --- /dev/null +++ b/guide/content/zh/release-notes/2024/v24.6.md @@ -0,0 +1,135 @@ +--- +title: 版本24.6 +--- + +# 版本24.6 + +.. toc:: + +## 一. 导言 + +这是版本 24 [发行周期] (../../organization/policies.md#release-schedule) 的首个版本。 v24 的发布节奏可能与过去几年略有不同。 请确保你已经加入了Discord服务器来跟进最新信息。 如果您遇到任何问题,请在 [GitHub](https://github.com/sanic-org/sanic/issues/new/selecte) 上提出问题。 + +## 需要了解的内容 + +更多详细信息在 [Changelog](../changelog.html) 可供查阅。 引人注目的新功能或破坏性更新以及升级内容: + +### 日志改进 + +默认日志格式已得到清理,因此当从控制台会话阅览日志时,对开发者会更加友好。 这包括:使用颜色进行高亮和减少冗余内容。 + +Sanic 将根据您的服务器是否处于 DEBUG 模式在两种细微变化之间进行选择。 您始终可以选择使用以下方法去除日志打印的颜色: + +```python +app.config.NO_COLOR = True +``` + +颜色将自动从非 TTY 终端的日志中去除。 + +在使用`sanic.logging.formatter.AutoFormatter`和`sanic.logging.formatter.AutoAccessFormatter`时,Sanic会自动根据 DEBUG 和 PROD 模式进行日志格式的切换。 当然,你可以使用适当命名的格式化程序强制使用一个版本或另一个版本 + +#### 在 DEBUG 模式 + +```python +sanic.logging.formatter.DebugFormatter +sanic.logging.formatter.DebugAccessFormatter +``` + +![](/assets/images/logging-dev.png) + +#### 在PROD模式 + +```python +sanic.logging.formatter.ProdFormatter +sanic.logging.formatter.ProdAccessFormatter +``` + +![](/assets/images/logging-prod.png) + +#### 遗留问题 + +如果您更喜欢旧式的日志记录,这些日志格式化程序也为你保留了这类格式:`sanic.logging.formatter.LegacyFormatter`和`sanic.logging.formatter.LegacyAccessFormatter`。 + +实现这些格式化的一种方式: + +```python +from sanic.log import LOGGING_CONFIG_DEFAULTS + +LOGGING_CONFIG_DEFAULTS["formatters"] = { + "generic": { + "class": "sanic.logging.formatter.LegacyFormatter" + }, + "access": { + "class": "sanic.logging.formatter.LegacyAccessFormatter" + }, +} +``` + +#### 新 JSON formatter + +还有一个新的 JSON 日志格式化器(Formatter),它将以JSON 格式输出日志以便与其他第三方日志平台集成。 + +```python +from sanic.log import LOGGING_CONFIG_DEFAULTS + +LOGGING_CONFIG_DEFAULTS["formatters"] = { + "generic": { + "class": "sanic.logging.formatter.JSONFormatter" + }, + "access": { + "class": "sanic.logging.formatter.JSONAccessFormatter" + }, +} +``` + +### 在 unix 套接字中使用Path + +在为服务器创建 unix 套接字时,现在可以通过传递`pathlib.Path`对象(而不是仅基于字符串的路径)来执行此操作。 + +### 自定义路由名称 + +您可以在自定义 `Sanic` 或 `Blueprint` 上覆盖`generate_name` 方法。 这将允许您随意修改路由名称。 + +```python +from sanic import Sanic, text, + +class Custom(Sanic): + def generate_name(self, *objects): + existing = self._generate_name(*objects) + return existing.upper() + +app = Sanic("Foo") + +@app.get("/") +async def handler(request): + return text(request.name) # FOO.HANDLER + + +return app +``` + +### 🚨 破坏性更改 + +1. `Request.cookies.getlist` 总是返回 `list` 。 这意味着,如果没有 `key` 的 cookie 时,它将是空的 `list` 而不是 `None'。 使用 `Request.cookies.getlist("something", None)\` 来保留现有的行为。 + +## 特别鸣谢 + +感谢参与到本次迭代的所有人::clap: + +[@ahopkins](https://github.com/ahopkins) +[@ashleysommer](https://github.com/ashleysommer) +[@ChihweiLHBird](https://github.com/ChihweiLHBird) +[@DeJayDev](https://github.com/DeJayDev) +[@ekzhang](https://github.com/ekzhang) +[@Huy-Ngo](https://github.com/Huy-Ngo) +[@iAndriy](https://github.com/iAndriy) +[@jakkaz](https://github.com/jakkaz) +[@Nano112](https://github.com/Nano112) +[@prryplatypus](https://github.com/prryplatypus) +[@razodactyl](https://github.com/razodactyl) +[@Tronic](https://github.com/Tronic) +[@wieczorek1990](https://github.com/wieczorek1990) + +--- + +如果您喜欢这个项目,请考虑参与贡献。 当然,我们喜欢代码贡献,但我们也喜欢任何形式的贡献。 考虑撰写一些文档,提供一些案例,加入交流并让您的声音为人所知,如果您能够:[财务捐款](https://opencollective.com/sanic-org/),那再好不过了。 diff --git a/guide/content/zh/release-notes/changelog.md b/guide/content/zh/release-notes/changelog.md new file mode 100644 index 0000000000..9b7b87a7b1 --- /dev/null +++ b/guide/content/zh/release-notes/changelog.md @@ -0,0 +1,1597 @@ +--- +content_class: 更新日志 +--- + +# 更新日志 + +:larg_orange_diamond: Current release +:larg_blu_diamond: In support LTS release + +## 版本24.12.0 :larg_orange_diamond::larg_blu_diamond: + +_当前版本_ + +### Features + +- [#3019](https://github.com/sanic-org/sanic/pull/3019) 在 `sanic` CLI 中添加自定义命令 + +### Bugfixes + +- [#2992](https://github.com/sanic-org/sanic/pull/2992) 修复`mixins.startup.serve` UnboundLocalError +- [#3000](https://github.com/sanic-org/sanic/pull/3000) +- [#3009](https://github.com/sanic-org/sanic/pull/3009) Fix `SanicException.quet` 属性处理设置为 `False` +- [#3014](https://github.com/sanic-org/sanic/pull/3014) 清理一些输入 +- [#3015](https://github.com/sanic-org/sanic/pull/3015) 如果适用,则杀死整个流程组 +- [#3016](https://github.com/sanic-org/sanic/pull/3016) 修复在 HTTPMethodView 类中获取方法的不兼容类型注释 + +### Deprecations and Removals + +- [#3020](https://github.com/sanic-org/sanic/pull/3020) 删除 Python 3.8 支持 + +### 开发者基础设施 + +- [#3017](https://github.com/sanic-org/sanic/pull/3017) Cleanup setup.cfg + +### Improved Documentation + +- [#3007](https://github.com/sanic-org/sanic/pull/3007) 修复`sanic-ext`的文档类型 + +## 版本24.6.0 + +### Features + +- [#2838](https://github.com/sanic-org/sanic/pull/2838) 简化请求 cookie `getlist` +- [#2850](https://github.com/sanic-org/sanic/pull/2850) Unix sockets 现在可以使用 `pathlib.Path` +- [#2931](https://github.com/sanic-org/sanic/pull/2931)[#2958](https://github.com/sanic-org/sanic/pull/2958) 日志改进 +- [#2947](https://github.com/sanic-org/sanic/pull/2947) 使异常的 .message 字段非空 +- [#2961](https://github.com/sanic-org/sanic/pull/2961) [#2964](https://github.com/sanic-org/pull/2964) 允许自定义名称生成 + +### Bugfixes + +- [#2919](https://github.com/sanic-org/sanic/pull/2919) 移除websockets中的废弃通知 +- [#2937](https://github.com/sanic-org/sanic/pull/2937) 在ASGI 模式中解决流媒体响应错误 +- [#2959](https://github.com/sanic-org/sanic/pull/2959) 解析Python 3.12废弃噪音 +- [#2960](https://github.com/sanic-org/sanic/pull/2960) +- [#2970](https://github.com/sanic-org/sanic/pull/2970)[#2978](https://github.com/sanic-org/sanic/pull/2978) 修复缺少的3.12依赖关系。 +- [#2971](https://github.com/sanic-org/sanic/pull/2971) 修复在未发现的路径上的中间件异常,中间件中出现错误 +- [#2973](https://github.com/sanic-org/sanic/pull/2973) 解析`transport.close`和`transport.abort` +- [#2976](https://github.com/sanic-org/sanic/pull/2976) 修复删除由 `secure=False` 创建的 cookie +- [#2979](https://github.com/sanic-org/sanic/pull/2979) 抛出错误的身体长度 +- [#2980](https://github.com/sanic-org/sanic/pull/2980) 丢弃错误的物体编码错误 + +### 废弃和移除 + +- [#2899](https://github.com/sanic-org/sanic/pull/2899) 从REPL中删除错误的行,从而影响没有HTTPX的环境 +- [#2962](https://github.com/sanic-org/sanic/pull/2962) 合并实体头部删除 + +### 开发者基础设施 + +- [#2882](https://github.com/sanic-org/sanic/pull/2882) [#2896](https://github.com/sanic-org/sanic/pull/2896) 应用动态端口固定来改进端口选择测试 +- [#2887](https://github.com/sanic-org/sanic/pull/2887) 更新到码头图像版本 +- [#2932](https://github.com/sanic-org/sanic/pull/2932) 用Ruff 清理代码库 + +### Improved Documentation + +- [#2924](https://github.com/sanic-org/sanic/pull/2924) cleanup markdown on html5tagger page +- [#2930](https://github.com/sanic-org/sanic/pull/2930) Sanic Extension README.md +- [#2934](https://github.com/sanic-org/sanic/pull/2934) +- [#2936](https://github.com/sanic-org/sanic/pull/2936) +- [#2955](https://github.com/sanic-org/sanic/pull/2955) 修复了`request.md`中错误的格式化。 + +## 版本23.12.0 :larg_blu_diamond: + +### Features + +- [#2775](https://github.com/sanic-org/sanic/pull/2775) Start and restart arbitrary processes +- [#2811](https://github.com/sanic-org/sanic/pull/2811) Cleaner process management in shutdown +- [#2812](https://github.com/sanic-org/sanic/pull/2812) Suppress task cancel traceback on open websocket +- [#2822](https://github.com/sanic-org/sanic/pull/2822) Listener and signal prioritization +- [#2831](https://github.com/sanic-org/sanic/pull/2831) Reduce memory consumption +- [#2837](https://github.com/sanic-org/sanic/pull/2837) Accept bare cookies +- [#2841](https://github.com/sanic-org/sanic/pull/2841) Add `websocket.handler.` signals +- [#2805](https://github.com/sanic-org/sanic/pull/2805) Add changed files to reload trigger listeners +- [#2813](https://github.com/sanic-org/sanic/pull/2813) Allow for simple signals +- [#2827](https://github.com/sanic-org/sanic/pull/2827) Improve functionality and consistency of `Sanic.event()` +- [#2851](https://github.com/sanic-org/sanic/pull/2851) Allow range requests for a single byte +- [#2854](https://github.com/sanic-org/sanic/pull/2854) Better `Request.scheme` for websocket requests +- [#2858](https://github.com/sanic-org/sanic/pull/2858) Convert Sanic `Request` to a Websockets `Request` for handshake +- [#2859](https://github.com/sanic-org/sanic/pull/2859) Add a REPL to the `sanic` CLI +- [#2870](https://github.com/sanic-org/sanic/pull/2870) Add Python 3.12 support +- [#2875](https://github.com/sanic-org/sanic/pull/2875) Better exception on multiprocessing context conflicts + +### Bugfixes + +- [#2803](https://github.com/sanic-org/sanic/pull/2803) Fix MOTD display for extra data + +### Developer infrastructure + +- [#2796](https://github.com/sanic-org/sanic/pull/2796) Refactor unit test cases +- [#2801](https://github.com/sanic-org/sanic/pull/2801) Fix `test_fast` when there is only one CPU +- [#2807](https://github.com/sanic-org/sanic/pull/2807) Add constraint for autodocsum (lint issue in old package version) +- [#2808](https://github.com/sanic-org/sanic/pull/2808) Refactor GitHub Actions +- [#2814](https://github.com/sanic-org/sanic/pull/2814) Run CI pipeline on git push +- [#2846](https://github.com/sanic-org/sanic/pull/2846) Drop old performance tests/benchmarks +- [#2848](https://github.com/sanic-org/sanic/pull/2848) Makefile cleanup +- [#2865](https://github.com/sanic-org/sanic/pull/2865) + [#2869](https://github.com/sanic-org/sanic/pull/2869) + [#2872](https://github.com/sanic-org/sanic/pull/2872) + [#2879](https://github.com/sanic-org/sanic/pull/2879) + Add ruff to toolchain +- [#2866](https://github.com/sanic-org/sanic/pull/2866) Fix the alt svc test to run locally with explicit buffer nbytes +- [#2877](https://github.com/sanic-org/sanic/pull/2877) Use Python's trusted publisher in deployments +- [#2882](https://github.com/sanic-org/sanic/pull/2882) Introduce dynamic port fixture in targeted locations in the test suite + +### Improved Documentation + +- [#2781](https://github.com/sanic-org/sanic/pull/2781) + [#2821](https://github.com/sanic-org/sanic/pull/2821) + [#2861](https://github.com/sanic-org/sanic/pull/2861) + [#2863](https://github.com/sanic-org/sanic/pull/2863) + Conversion of User Guide to the SHH (Sanic, html5tagger, HTMX) stack +- [#2810](https://github.com/sanic-org/sanic/pull/2810) Update README +- [#2855](https://github.com/sanic-org/sanic/pull/2855) Edit Discord badge +- [#2864](https://github.com/sanic-org/sanic/pull/2864) Adjust documentation for using state properties within http/https redirection document + +## Version 23.9.0 + +_Due to circumstances at the time, v.23.9 was skipped._ + +## Version 23.6.0 + +### Features + +- [#2670](https://github.com/sanic-org/sanic/pull/2670) Increase `KEEP_ALIVE_TIMEOUT` default to 120 +- [#2716](https://github.com/sanic-org/sanic/pull/2716) Adding allow route overwrite option in blueprint +- [#2724](https://github.com/sanic-org/sanic/pull/2724) and [#2792](https://github.com/sanic-org/sanic/pull/2792) Add a new exception signal for ALL exceptions raised anywhere in application +- [#2727](https://github.com/sanic-org/sanic/pull/2727) Add name prefixing to BP groups +- [#2754](https://github.com/sanic-org/sanic/pull/2754) Update request type on middleware types +- [#2770](https://github.com/sanic-org/sanic/pull/2770) Better exception message on startup time application induced import error +- [#2776](https://github.com/sanic-org/sanic/pull/2776) Set multiprocessing start method early +- [#2785](https://github.com/sanic-org/sanic/pull/2785) Add custom typing to config and ctx objects +- [#2790](https://github.com/sanic-org/sanic/pull/2790) Add `request.client_ip` + +### Bugfixes + +- [#2728](https://github.com/sanic-org/sanic/pull/2728) Fix traversals for intended results +- [#2729](https://github.com/sanic-org/sanic/pull/2729) Handle case when headers argument of ResponseStream constructor is None +- [#2737](https://github.com/sanic-org/sanic/pull/2737) Fix type annotation for `JSONREsponse` default content type +- [#2740](https://github.com/sanic-org/sanic/pull/2740) Use Sanic's serializer for JSON responses in the Inspector +- [#2760](https://github.com/sanic-org/sanic/pull/2760) Support for `Request.get_current` in ASGI mode +- [#2773](https://github.com/sanic-org/sanic/pull/2773) Alow Blueprint routes to explicitly define error_format +- [#2774](https://github.com/sanic-org/sanic/pull/2774) Resolve headers on different renderers +- [#2782](https://github.com/sanic-org/sanic/pull/2782) Resolve pypy compatibility issues + +### 废弃和移除 + +- [#2777](https://github.com/sanic-org/sanic/pull/2777) Remove Python 3.7 support + +### Developer infrastructure + +- [#2766](https://github.com/sanic-org/sanic/pull/2766) Unpin setuptools version +- [#2779](https://github.com/sanic-org/sanic/pull/2779) Run keep alive tests in loop to get available port + +### Improved Documentation + +- [#2741](https://github.com/sanic-org/sanic/pull/2741) Better documentation examples about running Sanic + From that list, the items to highlight in the release notes: + +## Version 23.3.0 + +### Features + +- [#2545](https://github.com/sanic-org/sanic/pull/2545) Standardize init of exceptions for more consistent control of HTTP responses using exceptions +- [#2606](https://github.com/sanic-org/sanic/pull/2606) Decode headers as UTF-8 also in ASGI +- [#2646](https://github.com/sanic-org/sanic/pull/2646) Separate ASGI request and lifespan callables +- [#2659](https://github.com/sanic-org/sanic/pull/2659) Use `FALLBACK_ERROR_FORMAT` for handlers that return `empty()` +- [#2662](https://github.com/sanic-org/sanic/pull/2662) Add basic file browser (HTML page) and auto-index serving +- [#2667](https://github.com/sanic-org/sanic/pull/2667) Nicer traceback formatting (HTML page) +- [#2668](https://github.com/sanic-org/sanic/pull/2668) Smarter error page rendering format selection; more reliant upon header and "common sense" defaults +- [#2680](https://github.com/sanic-org/sanic/pull/2680) Check the status of socket before shutting down with `SHUT_RDWR` +- [#2687](https://github.com/sanic-org/sanic/pull/2687) Refresh `Request.accept` functionality to be more performant and spec-compliant +- [#2696](https://github.com/sanic-org/sanic/pull/2696) Add header accessors as properties + ``` + Example-Field: Foo, Bar + Example-Field: Baz + ``` + ```python + request.headers.example_field == "Foo, Bar,Baz" + ``` +- [#2700](https://github.com/sanic-org/sanic/pull/2700) Simpler CLI targets + + ```sh + $ sanic path.to.module:app # global app instance + $ sanic path.to.module:create_app # factory pattern + $ sanic ./path/to/directory/ # simple serve + ``` +- [#2701](https://github.com/sanic-org/sanic/pull/2701) API to define a number of workers in managed processes +- [#2704](https://github.com/sanic-org/sanic/pull/2704) Add convenience for dynamic changes to routing +- [#2706](https://github.com/sanic-org/sanic/pull/2706) Add convenience methods for cookie creation and deletion + + ```python + response = text("...") + response.add_cookie("test", "It worked!", domain=".yummy-yummy-cookie.com") + ``` +- [#2707](https://github.com/sanic-org/sanic/pull/2707) Simplified `parse_content_header` escaping to be RFC-compliant and remove outdated FF hack +- [#2710](https://github.com/sanic-org/sanic/pull/2710) Stricter charset handling and escaping of request URLs +- [#2711](https://github.com/sanic-org/sanic/pull/2711) Consume body on `DELETE` by default +- [#2719](https://github.com/sanic-org/sanic/pull/2719) Allow `password` to be passed to TLS context +- [#2720](https://github.com/sanic-org/sanic/pull/2720) Skip middleware on `RequestCancelled` +- [#2721](https://github.com/sanic-org/sanic/pull/2721) Change access logging format to `%s` +- [#2722](https://github.com/sanic-org/sanic/pull/2722) Add `CertLoader` as application option for directly controlling `SSLContext` objects +- [#2725](https://github.com/sanic-org/sanic/pull/2725) Worker sync state tolerance on race condition + +### Bugfixes + +- [#2651](https://github.com/sanic-org/sanic/pull/2651) ASGI websocket to pass thru bytes as is +- [#2697](https://github.com/sanic-org/sanic/pull/2697) Fix comparison between datetime aware and naive in `file` when using `If-Modified-Since` + +### Deprecations and Removals + +- [#2666](https://github.com/sanic-org/sanic/pull/2666) Remove deprecated `__blueprintname__` property + +### Improved Documentation + +- [#2712](https://github.com/sanic-org/sanic/pull/2712) Improved example using `'https'` to create the redirect + +## 版本22.12.0 + +_当前LTS版本_ + +### Features + +- [#2569](https://github.com/sanic-org/sanic/pull/2569) Add `JSONResponse` class with some convenient methods when updating a response object +- [#2598](https://github.com/sanic-org/sanic/pull/2598) Change `uvloop` requirement to `>=0.15.0` +- [#2609](https://github.com/sanic-org/sanic/pull/2609) Add compatibility with `websockets` v11.0 +- [#2610](https://github.com/sanic-org/sanic/pull/2610) Kill server early on worker error + - Raise deadlock timeout to 30s +- [#2617](https://github.com/sanic-org/sanic/pull/2617) Scale number of running server workers +- [#2621](https://github.com/sanic-org/sanic/pull/2621) [#2634](https://github.com/sanic-org/sanic/pull/2634) Send `SIGKILL` on subsequent `ctrl+c` to force worker exit +- [#2622](https://github.com/sanic-org/sanic/pull/2622) Add API to restart all workers from the multiplexer +- [#2624](https://github.com/sanic-org/sanic/pull/2624) Default to `spawn` for all subprocesses unless specifically set: + ```python + from sanic import Sanic + + Sanic.start_method = "fork" + ``` +- [#2625](https://github.com/sanic-org/sanic/pull/2625) Filename normalisation of form-data/multipart file uploads +- [#2626](https://github.com/sanic-org/sanic/pull/2626) Move to HTTP Inspector: + - Remote access to inspect running Sanic instances + - TLS support for encrypted calls to Inspector + - Authentication to Inspector with API key + - Ability to extend Inspector with custom commands +- [#2632](https://github.com/sanic-org/sanic/pull/2632) Control order of restart operations +- [#2633](https://github.com/sanic-org/sanic/pull/2633) Move reload interval to class variable +- [#2636](https://github.com/sanic-org/sanic/pull/2636) Add `priority` to `register_middleware` method +- [#2639](https://github.com/sanic-org/sanic/pull/2639) Add `unquote` to `add_route` method +- [#2640](https://github.com/sanic-org/sanic/pull/2640) ASGI websockets to receive `text` or `bytes` + +### 错误修正 + +- [#2607](https://github.com/sanic-org/sanic/pull/2607) Force socket shutdown before close to allow rebinding +- [#2590](https://github.com/sanic-org/sanic/pull/2590) Use actual `StrEnum` in Python 3.11+ +- [#2615](https://github.com/sanic-org/sanic/pull/2615) Ensure middleware executes only once per request timeout +- [#2627](https://github.com/sanic-org/sanic/pull/2627) Crash ASGI application on lifespan failure +- [#2635](https://github.com/sanic-org/sanic/pull/2635) Resolve error with low-level server creation on Windows + +### 废弃和移除 + +- [#2608](https://github.com/sanic-org/sanic/pull/2608) [#2630](https://github.com/sanic-org/sanic/pull/2630) Signal conditions and triggers saved on `signal.extra` +- [#2626](https://github.com/sanic-org/sanic/pull/2626) Move to HTTP Inspector + - 🚨 _BREAKING CHANGE_: Moves the Inspector to a Sanic app from a simple TCP socket with a custom protocol + - _DEPRECATE_: The `--inspect*` commands have been deprecated in favor of `inspect ...` commands +- [#2628](https://github.com/sanic-org/sanic/pull/2628) Replace deprecated `distutils.strtobool` + +### Developer infrastructure + +- [#2612](https://github.com/sanic-org/sanic/pull/2612) Add CI testing for Python 3.11 + +## Version 22.9.1 + +### Features + +- [#2585](https://github.com/sanic-org/sanic/pull/2585) Improved error message when no applications have been registered + +### Bugfixes + +- [#2578](https://github.com/sanic-org/sanic/pull/2578) Add certificate loader for in process certificate creation +- [#2591](https://github.com/sanic-org/sanic/pull/2591) Do not use sentinel identity for `spawn` compatibility +- [#2592](https://github.com/sanic-org/sanic/pull/2592) Fix properties in nested blueprint groups +- [#2595](https://github.com/sanic-org/sanic/pull/2595) Introduce sleep interval on new worker reloader + +### Deprecations and Removals + +### Developer infrastructure + +- [#2588](https://github.com/sanic-org/sanic/pull/2588) Markdown templates on issue forms + +### Improved Documentation + +- [#2556](https://github.com/sanic-org/sanic/pull/2556) v22.9 documentation +- [#2582](https://github.com/sanic-org/sanic/pull/2582) Cleanup documentation on Windows support + +## Version 22.9.0 + +### Features + +- [#2445](https://github.com/sanic-org/sanic/pull/2445) Add custom loads function +- [#2490](https://github.com/sanic-org/sanic/pull/2490) Make `WebsocketImplProtocol` async iterable +- [#2499](https://github.com/sanic-org/sanic/pull/2499) Sanic Server WorkerManager refactor +- [#2506](https://github.com/sanic-org/sanic/pull/2506) Use `pathlib` for path resolution (for static file serving) +- [#2508](https://github.com/sanic-org/sanic/pull/2508) Use `path.parts` instead of `match` (for static file serving) +- [#2513](https://github.com/sanic-org/sanic/pull/2513) Better request cancel handling +- [#2516](https://github.com/sanic-org/sanic/pull/2516) Add request properties for HTTP method info: + - `request.is_safe` + - `request.is_identient` + - `request.is_cache` + - _See_ [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) _for more information about when these apply_ +- [#2522](https://github.com/sanic-org/sanic/pull/2522) Always show server location in ASGI +- [#2526](https://github.com/sanic-org/sanic/pull/2526) Cache control support for static files for returning 304 when appropriate +- [#2533](https://github.com/sanic-org/sanic/pull/2533) Refactor `_static_request_handler` +- [#2540](https://github.com/sanic-org/sanic/pull/2540) Add signals before and after handler execution + - `http.handler.befor` + - `http.handler.after ` +- [#2542](https://github.com/sanic-org/sanic/pull/2542) Add _[redacted]_ to CLI :) +- [#2546](https://github.com/sanic-org/sanic/pull/2546) Add deprecation warning filter +- [#2550](https://github.com/sanic-org/sanic/pull/2550) Middleware priority and performance enhancements + +### Bugfixes + +- [#2495](https://github.com/sanic-org/sanic/pull/2495) Prevent directory traversion with static files +- [#2515](https://github.com/sanic-org/sanic/pull/2515) Do not apply double slash to paths in certain static dirs in Blueprints + +### Deprecations and Removals + +- [#2525](https://github.com/sanic-org/sanic/pull/2525) Warn on duplicate route names, will be prevented outright in v23.3 +- [#2537](https://github.com/sanic-org/sanic/pull/2537) Raise warning and deprecation notice on duplicate exceptions, will be prevented outright in v23.3 + +### Developer infrastructure + +- [#2504](https://github.com/sanic-org/sanic/pull/2504) Cleanup test suite +- [#2505](https://github.com/sanic-org/sanic/pull/2505) Replace Unsupported Python Version Number from the Contributing Doc +- [#2530](https://github.com/sanic-org/sanic/pull/2530) Do not include tests folder in installed package resolver + +### Improved Documentation + +- [#2502](https://github.com/sanic-org/sanic/pull/2502) Fix a few typos +- [#2517](https://github.com/sanic-org/sanic/pull/2517) [#2536](https://github.com/sanic-org/sanic/pull/2536) Add some type hints + +## Version 22.6.2 + +### 错误修正 + +- [#2522](https://github.com/sanic-org/sanic/pull/2522) Always show server location in ASGI + +## Version 22.6.1 + +### 错误修正 + +- [#2477](https://github.com/sanic-org/sanic/pull/2477) Sanic static directory fails when folder name ends with ".." + +## Version 22.6.0 + +### Features + +- [#2378](https://github.com/sanic-org/sanic/pull/2378) Introduce HTTP/3 and autogeneration of TLS certificates in `DEBUG` mode + - 👶 _EARLY RELEASE FEATURE_: Serving Sanic over HTTP/3 is an early release feature. It does not yet fully cover the HTTP/3 spec, but instead aims for feature parity with Sanic's existing HTTP/1.1 server. Websockets, WebTransport, push responses are examples of some features not yet implemented. + - 📦 _EXTRA REQUIREMENT_: Not all HTTP clients are capable of interfacing with HTTP/3 servers. You may need to install a [HTTP/3 capable client](https://curl.se/docs/http3.html). + - 📦 _EXTRA REQUIREMENT_: In order to use TLS autogeneration, you must install either [mkcert](https://github.com/FiloSottile/mkcert) or [trustme](https://github.com/python-trio/trustme). +- [#2416](https://github.com/sanic-org/sanic/pull/2416) Add message to `task.cancel` +- [#2420](https://github.com/sanic-org/sanic/pull/2420) Add exception aliases for more consistent naming with standard HTTP response types (`BadRequest`, `MethodNotAllowed`, `RangeNotSatisfiable`) +- [#2432](https://github.com/sanic-org/sanic/pull/2432) Expose ASGI `scope` as a property on the `Request` object +- [#2438](https://github.com/sanic-org/sanic/pull/2438) Easier access to websocket class for annotation: `from sanic import Websocket` +- [#2439](https://github.com/sanic-org/sanic/pull/2439) New API for reading form values with options: `Request.get_form` +- [#2445](https://github.com/sanic-org/sanic/pull/2445) Add custom `loads` function +- [#2447](https://github.com/sanic-org/sanic/pull/2447), [#2486](https://github.com/sanic-org/sanic/pull/2486) Improved API to support setting cache control headers +- [#2453](https://github.com/sanic-org/sanic/pull/2453) Move verbosity filtering to logger +- [#2475](https://github.com/sanic-org/sanic/pull/2475) Expose getter for current request using `Request.get_current()` + +### Bugfixes + +- [#2448](https://github.com/sanic-org/sanic/pull/2448) Fix to allow running with `pythonw.exe` or places where there is no `sys.stdout` +- [#2451](https://github.com/sanic-org/sanic/pull/2451) Trigger `http.lifecycle.request` signal in ASGI mode +- [#2455](https://github.com/sanic-org/sanic/pull/2455) Resolve typing of stacked route definitions +- [#2463](https://github.com/sanic-org/sanic/pull/2463) Properly catch websocket CancelledError in websocket handler in Python 3.7 + +### Deprecations and Removals + +- [#2487](https://github.com/sanic-org/sanic/pull/2487) v22.6 deprecations and changes + 1. Optional application registry + 2. Execution of custom handlers after some part of response was sent + 3. Configuring fallback handlers on the `ErrorHandler` + 4. Custom `LOGO` setting + 5. `sanic.response.stream` + 6. `AsyncioServer.init` + +### Developer infrastructure + +- [#2449](https://github.com/sanic-org/sanic/pull/2449) Clean up `black` and `isort` config +- [#2479](https://github.com/sanic-org/sanic/pull/2479) Fix some flappy tests + +### Improved Documentation + +- [#2461](https://github.com/sanic-org/sanic/pull/2461) Update example to match current application naming standards +- [#2466](https://github.com/sanic-org/sanic/pull/2466) Better type annotation for `Extend` +- [#2485](https://github.com/sanic-org/sanic/pull/2485) Improved help messages in CLI + +## Version 22.3.0 + +### Features + +- [#2347](https://github.com/sanic-org/sanic/pull/2347) API for multi-application server + - 🚨 _BREAKING CHANGE_: The old `sanic.worker.GunicornWorker` has been **removed**. To run Sanic with `gunicorn`, you should use it thru `uvicorn` [as described in their docs](https://www.uvicorn.org/#running-with-gunicorn). + - 🧁 _SIDE EFFECT_: Named background tasks are now supported, even in Python 3.7 +- [#2357](https://github.com/sanic-org/sanic/pull/2357) Parse `Authorization` header as `Request.credentials` +- [#2361](https://github.com/sanic-org/sanic/pull/2361) Add config option to skip `Touchup` step in application startup +- [#2372](https://github.com/sanic-org/sanic/pull/2372) Updates to CLI help messaging +- [#2382](https://github.com/sanic-org/sanic/pull/2382) Downgrade warnings to backwater debug messages +- [#2396](https://github.com/sanic-org/sanic/pull/2396) Allow for `multidict` v0.6 +- [#2401](https://github.com/sanic-org/sanic/pull/2401) Upgrade CLI catching for alternative application run types +- [#2402](https://github.com/sanic-org/sanic/pull/2402) Conditionally inject CLI arguments into factory +- [#2413](https://github.com/sanic-org/sanic/pull/2413) Add new start and stop event listeners to reloader process +- [#2414](https://github.com/sanic-org/sanic/pull/2414) Remove loop as required listener arg +- [#2415](https://github.com/sanic-org/sanic/pull/2415) Better exception for bad URL parsing +- [sanic-routing#47](https://github.com/sanic-org/sanic-routing/pull/47) Add a new extention parameter type: ``, ``, ``, ``, ``, `` + - 👶 _BETA FEATURE_: This feature will not work with `path` type matching, and is being released as a beta feature only. +- [sanic-routing#57](https://github.com/sanic-org/sanic-routing/pull/57) Change `register_pattern` to accept a `str` or `Pattern` +- [sanic-routing#58](https://github.com/sanic-org/sanic-routing/pull/58) Default matching on non-empty strings only, and new `strorempty` pattern type + - 🚨 _BREAKING CHANGE_: Previously a route with a dynamic string parameter (`/` or `/`) would match on any string, including empty strings. It will now **only** match a non-empty string. To retain the old behavior, you should use the new parameter type: `/`. + +### Bugfixes + +- [#2373](https://github.com/sanic-org/sanic/pull/2373) Remove `error_logger` on websockets +- [#2381](https://github.com/sanic-org/sanic/pull/2381) Fix newly assigned `None` in task registry +- [sanic-routing#52](https://github.com/sanic-org/sanic-routing/pull/52) Add type casting to regex route matching +- [sanic-routing#60](https://github.com/sanic-org/sanic-routing/pull/60) Add requirements check on regex routes (this resolves, for example, multiple static directories with differing `host` values) + +### Deprecations and Removals + +- [#2362](https://github.com/sanic-org/sanic/pull/2362) 22.3 Deprecations and changes + 1. `debug=True` and `--debug` do _NOT_ automatically run `auto_reload` + 2. Default error render is with plain text (browsers still get HTML by default because `auto` looks at headers) + 3. `config` is required for `ErrorHandler.finalize` + 4. `ErrorHandler.lookup` requires two positional args + 5. Unused websocket protocol args removed +- [#2344](https://github.com/sanic-org/sanic/pull/2344) Deprecate loading of lowercase environment variables + +### Developer infrastructure + +- [#2363](https://github.com/sanic-org/sanic/pull/2363) Revert code coverage back to Codecov +- [#2405](https://github.com/sanic-org/sanic/pull/2405) Upgrade tests for `sanic-routing` changes +- [sanic-testing#35](https://github.com/sanic-org/sanic-testing/pull/35) Allow for httpx v0.22 + +### 改进文档 + +- [#2350](https://github.com/sanic-org/sanic/pull/2350) Fix link in README for ASGI +- [#2398](https://github.com/sanic-org/sanic/pull/2398) Document middleware on_request and on_response +- [#2409](https://github.com/sanic-org/sanic/pull/2409) Add missing documentation for `Request.respond` + +### Miscellaneous + +- [#2376](https://github.com/sanic-org/sanic/pull/2376) Fix typing for `ListenerMixin.listener` +- [#2383](https://github.com/sanic-org/sanic/pull/2383) Clear deprecation warning in `asyncio.wait` +- [#2387](https://github.com/sanic-org/sanic/pull/2387) Cleanup `__slots__` implementations +- [#2390](https://github.com/sanic-org/sanic/pull/2390) Clear deprecation warning in `asyncio.get_event_loop` + +## Version 21.12.1 + +- [#2349](https://github.com/sanic-org/sanic/pull/2349) Only display MOTD on startup +- [#2354](https://github.com/sanic-org/sanic/pull/2354) Ignore name argument in Python 3.7 +- [#2355](https://github.com/sanic-org/sanic/pull/2355) Add config.update support for all config values + +## Version 21.12.0 + +### 功能 + +- [#2260](https://github.com/sanic-org/sanic/pull/2260) Allow early Blueprint registrations to still apply later added objects +- [#2262](https://github.com/sanic-org/sanic/pull/2262) Noisy exceptions - force logging of all exceptions +- [#2264](https://github.com/sanic-org/sanic/pull/2264) Optional `uvloop` by configuration +- [#2270](https://github.com/sanic-org/sanic/pull/2270) Vhost support using multiple TLS certificates +- [#2277](https://github.com/sanic-org/sanic/pull/2277) Change signal routing for increased consistency + - _BREAKING CHANGE_: If you were manually routing signals there is a breaking change. The signal router's `get` is no longer 100% determinative. There is now an additional step to loop thru the returned signals for proper matching on the requirements. If signals are being dispatched using `app.dispatch` or `bp.dispatch`, there is no change. +- [#2290](https://github.com/sanic-org/sanic/pull/2290) Add contextual exceptions +- [#2291](https://github.com/sanic-org/sanic/pull/2291) Increase join concat performance +- [#2295](https://github.com/sanic-org/sanic/pull/2295), [#2316](https://github.com/sanic-org/sanic/pull/2316), [#2331](https://github.com/sanic-org/sanic/pull/2331) Restructure of CLI and application state with new displays and more command parity with `app.run` +- [#2302](https://github.com/sanic-org/sanic/pull/2302) Add route context at definition time +- [#2304](https://github.com/sanic-org/sanic/pull/2304) Named tasks and new API for managing background tasks +- [#2307](https://github.com/sanic-org/sanic/pull/2307) On app auto-reload, provide insight of changed files +- [#2308](https://github.com/sanic-org/sanic/pull/2308) Auto extend application with [Sanic Extensions](https://sanicframework.org/en/plugins/sanic-ext/getting-started.html) if it is installed, and provide first class support for accessing the extensions +- [#2309](https://github.com/sanic-org/sanic/pull/2309) Builtin signals changed to `Enum` +- [#2313](https://github.com/sanic-org/sanic/pull/2313) Support additional config implementation use case +- [#2321](https://github.com/sanic-org/sanic/pull/2321) Refactor environment variable hydration logic +- [#2327](https://github.com/sanic-org/sanic/pull/2327) Prevent sending multiple or mixed responses on a single request +- [#2330](https://github.com/sanic-org/sanic/pull/2330) Custom type casting on environment variables +- [#2332](https://github.com/sanic-org/sanic/pull/2332) Make all deprecation notices consistent +- [#2335](https://github.com/sanic-org/sanic/pull/2335) Allow underscore to start instance names + +### 错误修正 + +- [#2273](https://github.com/sanic-org/sanic/pull/2273) Replace assignation by typing for `websocket_handshake` +- [#2285](https://github.com/sanic-org/sanic/pull/2285) Fix IPv6 display in startup logs +- [#2299](https://github.com/sanic-org/sanic/pull/2299) Dispatch `http.lifecyle.response` from exception handler + +### 废弃和移除 + +- [#2306](https://github.com/sanic-org/sanic/pull/2306) Removal of deprecated items + - `Sanic` and `Blueprint` may no longer have arbitrary properties attached to them + - `Sanic` and `Blueprint` forced to have compliant names + - alphanumeric + `_` + `-` + - must start with letter or `_` + - `load_env` keyword argument of `Sanic` + - `sanic.exceptions.abort` + - `sanic.views.CompositionView` + - `sanic.response.StreamingHTTPResponse` + - _NOTE:_ the `stream()` response method (where you pass a callable streaming function) has been deprecated and will be removed in v22.6. You should upgrade all streaming responses to the new style: https://sanicframework.org/en/guide/advanced/streaming.html#response-streaming +- [#2320](https://github.com/sanic-org/sanic/pull/2320) Remove app instance from Config for error handler setting + +### 开发者基础设施 + +- [#2251](https://github.com/sanic-org/sanic/pull/2251) Change dev install command +- [#2286](https://github.com/sanic-org/sanic/pull/2286) Change codeclimate complexity threshold from 5 to 10 +- [#2287](https://github.com/sanic-org/sanic/pull/2287) Update host test function names so they are not overwritten +- [#2292](https://github.com/sanic-org/sanic/pull/2292) Fail CI on error +- [#2311](https://github.com/sanic-org/sanic/pull/2311), [#2324](https://github.com/sanic-org/sanic/pull/2324) Do not run tests for draft PRs +- [#2336](https://github.com/sanic-org/sanic/pull/2336) Remove paths from coverage checks +- [#2338](https://github.com/sanic-org/sanic/pull/2338) Cleanup ports on tests + +### 改进文档 + +- [#2269](https://github.com/sanic-org/sanic/pull/2269), [#2329](https://github.com/sanic-org/sanic/pull/2329), [#2333](https://github.com/sanic-org/sanic/pull/2333) Cleanup typos and fix language + +### Miscellaneous + +- [#2257](https://github.com/sanic-org/sanic/pull/2257), [#2294](https://github.com/sanic-org/sanic/pull/2294), [#2341](https://github.com/sanic-org/sanic/pull/2341) Add Python 3.10 support +- [#2279](https://github.com/sanic-org/sanic/pull/2279), [#2317](https://github.com/sanic-org/sanic/pull/2317), [#2322](https://github.com/sanic-org/sanic/pull/2322) Add/correct missing type annotations +- [#2305](https://github.com/sanic-org/sanic/pull/2305) Fix examples to use modern implementations + +## Version 21.9.3 + +_Rerelease of v21.9.2 with some cleanup_ + +## Version 21.9.2 + +- [#2268](https://github.com/sanic-org/sanic/pull/2268) Make HTTP connections start in IDLE stage, avoiding delays and error messages +- [#2310](https://github.com/sanic-org/sanic/pull/2310) More consistent config setting with post-FALLBACK_ERROR_FORMAT apply + +## Version 21.9.1 + +- [#2259](https://github.com/sanic-org/sanic/pull/2259) Allow non-conforming ErrorHandlers + +## Version 21.9.0 + +### 功能 + +- [#2158](https://github.com/sanic-org/sanic/pull/2158), [#2248](https://github.com/sanic-org/sanic/pull/2248) Complete overhaul of I/O to websockets +- [#2160](https://github.com/sanic-org/sanic/pull/2160) Add new 17 signals into server and request lifecycles +- [#2162](https://github.com/sanic-org/sanic/pull/2162) Smarter `auto` fallback formatting upon exception +- [#2184](https://github.com/sanic-org/sanic/pull/2184) Introduce implementation for copying a Blueprint +- [#2200](https://github.com/sanic-org/sanic/pull/2200) Accept header parsing +- [#2207](https://github.com/sanic-org/sanic/pull/2207) Log remote address if available +- [#2209](https://github.com/sanic-org/sanic/pull/2209) Add convenience methods to BP groups +- [#2216](https://github.com/sanic-org/sanic/pull/2216) Add default messages to SanicExceptions +- [#2225](https://github.com/sanic-org/sanic/pull/2225) Type annotation convenience for annotated handlers with path parameters +- [#2236](https://github.com/sanic-org/sanic/pull/2236) Allow Falsey (but not-None) responses from route handlers +- [#2238](https://github.com/sanic-org/sanic/pull/2238) Add `exception` decorator to Blueprint Groups +- [#2244](https://github.com/sanic-org/sanic/pull/2244) Explicit static directive for serving file or dir (ex: `static(..., resource_type="file")`) +- [#2245](https://github.com/sanic-org/sanic/pull/2245) Close HTTP loop when connection task cancelled + +### 错误修正 + +- [#2188](https://github.com/sanic-org/sanic/pull/2188) Fix the handling of the end of a chunked request +- [#2195](https://github.com/sanic-org/sanic/pull/2195) Resolve unexpected error handling on static requests +- [#2208](https://github.com/sanic-org/sanic/pull/2208) Make blueprint-based exceptions attach and trigger in a more intuitive manner +- [#2211](https://github.com/sanic-org/sanic/pull/2211) Fixed for handling exceptions of asgi app call +- [#2213](https://github.com/sanic-org/sanic/pull/2213) Fix bug where ws exceptions not being logged +- [#2231](https://github.com/sanic-org/sanic/pull/2231) Cleaner closing of tasks by using `abort()` in strategic places to avoid dangling sockets +- [#2247](https://github.com/sanic-org/sanic/pull/2247) Fix logging of auto-reload status in debug mode +- [#2246](https://github.com/sanic-org/sanic/pull/2246) Account for BP with exception handler but no routes + +### 开发者基础设施 + +- [#2194](https://github.com/sanic-org/sanic/pull/2194) HTTP unit tests with raw client +- [#2199](https://github.com/sanic-org/sanic/pull/2199) Switch to codeclimate +- [#2214](https://github.com/sanic-org/sanic/pull/2214) Try Reopening Windows Tests +- [#2229](https://github.com/sanic-org/sanic/pull/2229) Refactor `HttpProtocol` into a base class +- [#2230](https://github.com/sanic-org/sanic/pull/2230) Refactor `server.py` into multi-file module + +### Miscellaneous + +- [#2173](https://github.com/sanic-org/sanic/pull/2173) Remove Duplicated Dependencies and PEP 517 Support +- [#2193](https://github.com/sanic-org/sanic/pull/2193), [#2196](https://github.com/sanic-org/sanic/pull/2196), [#2217](https://github.com/sanic-org/sanic/pull/2217) Type annotation changes + +## Version 21.6.1 + +**Bugfixes** + +- [#2178](https://github.com/sanic-org/sanic/pull/2178) Update + sanic-routing to allow for better splitting of complex URI + templates +- [#2183](https://github.com/sanic-org/sanic/pull/2183) Proper + handling of chunked request bodies to resolve phantom 503 in logs +- [#2181](https://github.com/sanic-org/sanic/pull/2181) Resolve + regression in exception logging +- [#2201](https://github.com/sanic-org/sanic/pull/2201) Cleanup + request info in pipelined requests + +## Version 21.6.0 + +**Features** + +- [#2094](https://github.com/sanic-org/sanic/pull/2094) Add + `response.eof()` method for closing a stream in a handler + +- [#2097](https://github.com/sanic-org/sanic/pull/2097) Allow + case-insensitive HTTP Upgrade header + +- [#2104](https://github.com/sanic-org/sanic/pull/2104) Explicit + usage of CIMultiDict getters + +- [#2109](https://github.com/sanic-org/sanic/pull/2109) Consistent + use of error loggers + +- [#2114](https://github.com/sanic-org/sanic/pull/2114) New + `client_ip` access of connection info instance + +- [#2119](https://github.com/sanic-org/sanic/pull/2119) Alternatate + classes on instantiation for `Config` and `Sanic.ctx` + +- [#2133](https://github.com/sanic-org/sanic/pull/2133) Implement + new version of AST router + + - Proper differentiation between `alpha` and `string` param + types + - Adds a `slug` param type, example: `` + - Deprecates `` in favor of `` + - Deprecates `` in favor of `` + - Adds a `route.uri` accessor + +- [#2136](https://github.com/sanic-org/sanic/pull/2136) CLI + improvements with new optional params + +- [#2137](https://github.com/sanic-org/sanic/pull/2137) Add + `version_prefix` to URL builders + +- [#2140](https://github.com/sanic-org/sanic/pull/2140) Event + autoregistration with `EVENT_AUTOREGISTER` + +- [#2146](https://github.com/sanic-org/sanic/pull/2146), + [#2147](https://github.com/sanic-org/sanic/pull/2147) Require + stricter names on `Sanic()` and `Blueprint()` + +- [#2150](https://github.com/sanic-org/sanic/pull/2150) Infinitely + reusable and nestable `Blueprint` and `BlueprintGroup` + +- [#2154](https://github.com/sanic-org/sanic/pull/2154) Upgrade + `websockets` dependency to min version + +- [#2155](https://github.com/sanic-org/sanic/pull/2155) Allow for + maximum header sizes to be increased: `REQUEST_MAX_HEADER_SIZE` + +- [#2157](https://github.com/sanic-org/sanic/pull/2157) Allow app + factory pattern in CLI + +- [#2165](https://github.com/sanic-org/sanic/pull/2165) Change HTTP + methods to enums + +- [#2167](https://github.com/sanic-org/sanic/pull/2167) Allow + auto-reloading on additional directories + +- [#2168](https://github.com/sanic-org/sanic/pull/2168) Add simple + HTTP server to CLI + +- [#2170](https://github.com/sanic-org/sanic/pull/2170) Additional + methods for attaching `HTTPMethodView` + +**Bugfixes** + +- [#2091](https://github.com/sanic-org/sanic/pull/2091) Fix + `UserWarning` in ASGI mode for missing `__slots__` +- [#2099](https://github.com/sanic-org/sanic/pull/2099) Fix static + request handler logging exception on 404 +- [#2110](https://github.com/sanic-org/sanic/pull/2110) Fix + request.args.pop removes parameters inconsistently +- [#2107](https://github.com/sanic-org/sanic/pull/2107) Fix type + hinting for load_env +- [#2127](https://github.com/sanic-org/sanic/pull/2127) Make sure + ASGI ws subprotocols is a list +- [#2128](https://github.com/sanic-org/sanic/pull/2128) Fix issue + where Blueprint exception handlers do not consistently route to + proper handler + +**Deprecations and Removals** + +- [#2156](https://github.com/sanic-org/sanic/pull/2156) Remove + config value `REQUEST_BUFFER_QUEUE_SIZE` +- [#2170](https://github.com/sanic-org/sanic/pull/2170) + `CompositionView` deprecated and marked for removal in 21.12 +- [#2172](https://github.com/sanic-org/sanic/pull/2170) Deprecate + StreamingHTTPResponse + +**Developer infrastructure** + +- [#2149](https://github.com/sanic-org/sanic/pull/2149) Remove + Travis CI in favor of GitHub Actions + +**Improved Documentation** + +- [#2164](https://github.com/sanic-org/sanic/pull/2164) Fix typo in + documentation +- [#2100](https://github.com/sanic-org/sanic/pull/2100) Remove + documentation for non-existent arguments + +## Version 21.3.2 + +**Bugfixes** + +- [#2081](https://github.com/sanic-org/sanic/pull/2081) Disable + response timeout on websocket connections +- [#2085](https://github.com/sanic-org/sanic/pull/2085) Make sure + that blueprints with no slash is maintained when applied + +## Version 21.3.1 + +**Bugfixes** + +- [#2076](https://github.com/sanic-org/sanic/pull/2076) Static files + inside subfolders are not accessible (404) + +## Version 21.3.0 + +Release +Notes + +**Features** + +- [#1876](https://github.com/sanic-org/sanic/pull/1876) Unified + streaming server +- [#2005](https://github.com/sanic-org/sanic/pull/2005) New + `Request.id` property +- [#2008](https://github.com/sanic-org/sanic/pull/2008) Allow + Pathlib Path objects to be passed to `app.static()` helper +- [#2010](https://github.com/sanic-org/sanic/pull/2010), + [#2031](https://github.com/sanic-org/sanic/pull/2031) New + startup-optimized router +- [#2018](https://github.com/sanic-org/sanic/pull/2018) + [#2064](https://github.com/sanic-org/sanic/pull/2064) Listeners + for main server process +- [#2032](https://github.com/sanic-org/sanic/pull/2032) Add raw + header info to request object +- [#2042](https://github.com/sanic-org/sanic/pull/2042) + [#2060](https://github.com/sanic-org/sanic/pull/2060) + [#2061](https://github.com/sanic-org/sanic/pull/2061) Introduce + Signals API +- [#2043](https://github.com/sanic-org/sanic/pull/2043) Add + `__str__` and `__repr__` to Sanic and Blueprint +- [#2047](https://github.com/sanic-org/sanic/pull/2047) Enable + versioning and strict slash on BlueprintGroup +- [#2053](https://github.com/sanic-org/sanic/pull/2053) Make + `get_app` name argument optional +- [#2055](https://github.com/sanic-org/sanic/pull/2055) JSON encoder + change via app +- [#2063](https://github.com/sanic-org/sanic/pull/2063) App and + connection level context objects + +**Bugfixes** + +- Resolve [#1420](https://github.com/sanic-org/sanic/pull/1420) + `url_for` where `strict_slashes` are on for a path ending in `/` +- Resolve [#1525](https://github.com/sanic-org/sanic/pull/1525) + Routing is incorrect with some special characters +- Resolve [#1653](https://github.com/sanic-org/sanic/pull/1653) ASGI + headers in body +- Resolve [#1722](https://github.com/sanic-org/sanic/pull/1722) + Using curl in chunk mode +- Resolve [#1730](https://github.com/sanic-org/sanic/pull/1730) + Extra content in ASGI streaming response +- Resolve [#1749](https://github.com/sanic-org/sanic/pull/1749) + Restore broken middleware edge cases +- Resolve [#1785](https://github.com/sanic-org/sanic/pull/1785) + [#1804](https://github.com/sanic-org/sanic/pull/1804) Synchronous + error handlers +- Resolve [#1790](https://github.com/sanic-org/sanic/pull/1790) + Protocol errors did not support async error handlers #1790 +- Resolve [#1824](https://github.com/sanic-org/sanic/pull/1824) + Timeout on specific methods +- Resolve [#1875](https://github.com/sanic-org/sanic/pull/1875) + Response timeout error from all routes after returning several + timeouts from a specific route +- Resolve [#1988](https://github.com/sanic-org/sanic/pull/1988) + Handling of safe methods with body +- [#2001](https://github.com/sanic-org/sanic/pull/2001) Raise + ValueError when cookie max-age is not an integer + +**Deprecations and Removals** + +- [#2007](https://github.com/sanic-org/sanic/pull/2007) \* Config + using `from_envvar` \* Config using `from_pyfile` \* Config using + `from_object` +- [#2009](https://github.com/sanic-org/sanic/pull/2009) Remove Sanic + test client to its own package +- [#2036](https://github.com/sanic-org/sanic/pull/2036), + [#2037](https://github.com/sanic-org/sanic/pull/2037) Drop Python + 3.6 support +- `Request.endpoint` deprecated in favor of `Request.name` +- handler type name prefixes removed (static, websocket, etc) + +**Developer infrastructure** + +- [#1995](https://github.com/sanic-org/sanic/pull/1995) Create + FUNDING.yml +- [#2013](https://github.com/sanic-org/sanic/pull/2013) Add codeql + to CI pipeline +- [#2038](https://github.com/sanic-org/sanic/pull/2038) Codecov + configuration updates +- [#2049](https://github.com/sanic-org/sanic/pull/2049) Updated + setup.py to use `find_packages` + +**Improved Documentation** + +- [#1218](https://github.com/sanic-org/sanic/pull/1218) + Documentation for sanic.log.\* is missing +- [#1608](https://github.com/sanic-org/sanic/pull/1608) Add + documentation on calver and LTS +- [#1731](https://github.com/sanic-org/sanic/pull/1731) Support + mounting application elsewhere than at root path +- [#2006](https://github.com/sanic-org/sanic/pull/2006) Upgraded + type annotations and improved docstrings and API documentation +- [#2052](https://github.com/sanic-org/sanic/pull/2052) Fix some + examples and docs + +**Miscellaneous** + +- `Request.route` property +- Better websocket subprotocols support +- Resolve bug with middleware in Blueprint Group when passed + callable +- Moves common logic between Blueprint and Sanic into mixins +- Route naming changed to be more consistent + - request endpoint is the route name + - route names are fully namespaced +- Some new convenience decorators: + - `@app.main_process_start` + - `@app.main_process_stop` + - `@app.befor_server_start` + - `@app.after _server_start` + - `@app.befor_server_stop` + - `@app.after _server_stop` + - `@app.on_request` + - `@app.on_response` +- Fixes `Allow` header that did not include `HEAD` +- Using \"name\" keyword in `url_for` for a \"static\" route where + name does not exist +- Cannot have multiple `app.static()` without using the named param +- Using \"filename\" keyword in `url_for` on a file route +- `unquote` in route def (not automatic) +- `routes_all` is tuples +- Handler arguments are kwarg only +- `request.match_info` is now a cached (and not computed) property +- Unknown static file mimetype is sent as `application/octet-stream` +- `_host` keyword in `url_for` +- Add charset default to `utf-8` for text and js content types if + not specified +- Version for a route can be str, float, or int +- Route has ctx property +- App has `routes_static`, `routes_dynamic`, `routes_regex` +- [#2044](https://github.com/sanic-org/sanic/pull/2044) Code cleanup + and refactoring +- [#2072](https://github.com/sanic-org/sanic/pull/2072) Remove + `BaseSanic` metaclass +- [#2074](https://github.com/sanic-org/sanic/pull/2074) Performance + adjustments in `handle_request_` + +## Version 20.12.3 + +**Bugfixes** + +- [#2021](https://github.com/sanic-org/sanic/pull/2021) Remove + prefix from websocket handler name + +## Version 20.12.2 + +**Dependencies** + +- [#2026](https://github.com/sanic-org/sanic/pull/2026) Fix uvloop + to 0.14 because 0.15 drops Python 3.6 support +- [#2029](https://github.com/sanic-org/sanic/pull/2029) Remove old + chardet requirement, add in hard multidict requirement + +## Version 19.12.5 + +**Dependencies** + +- [#2025](https://github.com/sanic-org/sanic/pull/2025) Fix uvloop + to 0.14 because 0.15 drops Python 3.6 support +- [#2027](https://github.com/sanic-org/sanic/pull/2027) Remove old + chardet requirement, add in hard multidict requirement + +## Version 20.12.0 + +**Features** + +- [#1993](https://github.com/sanic-org/sanic/pull/1993) Add disable + app registry +- [#1945](https://github.com/sanic-org/sanic/pull/1945) Static route + more verbose if file not found +- [#1954](https://github.com/sanic-org/sanic/pull/1954) Fix static + routes registration on a blueprint +- [#1961](https://github.com/sanic-org/sanic/pull/1961) Add Python + 3.9 support +- [#1962](https://github.com/sanic-org/sanic/pull/1962) Sanic CLI + upgrade +- [#1967](https://github.com/sanic-org/sanic/pull/1967) Update + aiofile version requirements +- [#1969](https://github.com/sanic-org/sanic/pull/1969) Update + multidict version requirements +- [#1970](https://github.com/sanic-org/sanic/pull/1970) Add py.typed + file +- [#1972](https://github.com/sanic-org/sanic/pull/1972) Speed + optimization in request handler +- [#1979](https://github.com/sanic-org/sanic/pull/1979) Add app + registry and Sanic class level app retrieval + +**Bugfixes** + +- [#1965](https://github.com/sanic-org/sanic/pull/1965) Fix Chunked + Transport-Encoding in ASGI streaming response + +**Deprecations and Removals** + +- [#1981](https://github.com/sanic-org/sanic/pull/1981) Cleanup and + remove deprecated code + +**Developer infrastructure** + +- [#1956](https://github.com/sanic-org/sanic/pull/1956) Fix load + module test +- [#1973](https://github.com/sanic-org/sanic/pull/1973) Transition + Travis from .org to .com +- [#1986](https://github.com/sanic-org/sanic/pull/1986) Update tox + requirements + +**Improved Documentation** + +- [#1951](https://github.com/sanic-org/sanic/pull/1951) + Documentation improvements +- [#1983](https://github.com/sanic-org/sanic/pull/1983) Remove + duplicate contents in testing.rst +- [#1984](https://github.com/sanic-org/sanic/pull/1984) Fix typo in + routing.rst + +## Version 20.9.1 + +**Bugfixes** + +- [#1954](https://github.com/sanic-org/sanic/pull/1954) Fix static + route registration on blueprints +- [#1957](https://github.com/sanic-org/sanic/pull/1957) Removes + duplicate headers in ASGI streaming body + +## Version 19.12.3 + +**Bugfixes** + +- [#1959](https://github.com/sanic-org/sanic/pull/1959) Removes + duplicate headers in ASGI streaming body + +## Version 20.9.0 + +**Features** + +- [#1887](https://github.com/sanic-org/sanic/pull/1887) Pass + subprotocols in websockets (both sanic server and ASGI) +- [#1894](https://github.com/sanic-org/sanic/pull/1894) + Automatically set `test_mode` flag on app instance +- [#1903](https://github.com/sanic-org/sanic/pull/1903) Add new + unified method for updating app values +- [#1906](https://github.com/sanic-org/sanic/pull/1906), + [#1909](https://github.com/sanic-org/sanic/pull/1909) Adds + WEBSOCKET_PING_TIMEOUT and WEBSOCKET_PING_INTERVAL configuration + values +- [#1935](https://github.com/sanic-org/sanic/pull/1935) httpx + version dependency updated, it is slated for removal as a + dependency in v20.12 +- [#1937](https://github.com/sanic-org/sanic/pull/1937) Added auto, + text, and json fallback error handlers (in v21.3, the default will + change form html to auto) + +**Bugfixes** + +- [#1897](https://github.com/sanic-org/sanic/pull/1897) Resolves + exception from unread bytes in stream + +**Deprecations and Removals** + +- [#1903](https://github.com/sanic-org/sanic/pull/1903) + config.from_envar, config.from_pyfile, and config.from_object are + deprecated and set to be removed in v21.3 + +**Developer infrastructure** + +- [#1890](https://github.com/sanic-org/sanic/pull/1890), + [#1891](https://github.com/sanic-org/sanic/pull/1891) Update isort + calls to be compatible with new API +- [#1893](https://github.com/sanic-org/sanic/pull/1893) Remove + version section from setup.cfg +- [#1924](https://github.com/sanic-org/sanic/pull/1924) Adding + \--strict-markers for pytest + +**Improved Documentation** + +- [#1922](https://github.com/sanic-org/sanic/pull/1922) Add explicit + ASGI compliance to the README + +## Version 20.6.3 + +**Bugfixes** + +- [#1884](https://github.com/sanic-org/sanic/pull/1884) Revert + change to multiprocessing mode + +## Version 20.6.2 + +**Features** + +- [#1641](https://github.com/sanic-org/sanic/pull/1641) Socket + binding implemented properly for IPv6 and UNIX sockets + +## Version 20.6.1 + +**Features** + +- [#1760](https://github.com/sanic-org/sanic/pull/1760) Add version + parameter to websocket routes +- [#1866](https://github.com/sanic-org/sanic/pull/1866) Add `sanic` + as an entry point command +- [#1880](https://github.com/sanic-org/sanic/pull/1880) Add handler + names for websockets for url_for usage + +**Bugfixes** + +- [#1776](https://github.com/sanic-org/sanic/pull/1776) Bug fix for + host parameter issue with lists +- [#1842](https://github.com/sanic-org/sanic/pull/1842) Fix static + \_handler pickling error +- [#1827](https://github.com/sanic-org/sanic/pull/1827) Fix reloader + on OSX py38 and Windows +- [#1848](https://github.com/sanic-org/sanic/pull/1848) Reverse + named_response_middlware execution order, to match normal response + middleware execution order +- [#1853](https://github.com/sanic-org/sanic/pull/1853) Fix pickle + error when attempting to pickle an application which contains + websocket routes + +**Deprecations and Removals** + +- [#1739](https://github.com/sanic-org/sanic/pull/1739) Deprecate + body_bytes to merge into body + +**Developer infrastructure** + +- [#1852](https://github.com/sanic-org/sanic/pull/1852) Fix naming + of CI test env on Python nightlies +- [#1857](https://github.com/sanic-org/sanic/pull/1857) Adjust + websockets version to setup.py +- [#1869](https://github.com/sanic-org/sanic/pull/1869) Wrap + run()\'s \"protocol\" type annotation in Optional\[\] + +**Improved Documentation** + +- [#1846](https://github.com/sanic-org/sanic/pull/1846) Update docs + to clarify response middleware execution order +- [#1865](https://github.com/sanic-org/sanic/pull/1865) Fixing rst + format issue that was hiding documentation + +## Version 20.6.0 + +_Released, but unintentionally omitting PR #1880, so was replaced by +20.6.1_ + +## Version 20.3.0 + +**Features** + +- [#1762](https://github.com/sanic-org/sanic/pull/1762) Add + `srv.start_serving()` and `srv.serve_forever()` to `AsyncioServer` +- [#1767](https://github.com/sanic-org/sanic/pull/1767) Make Sanic + usable on `hypercorn -k trio myweb.app` +- [#1768](https://github.com/sanic-org/sanic/pull/1768) No + tracebacks on normal errors and prettier error pages +- [#1769](https://github.com/sanic-org/sanic/pull/1769) Code cleanup + in file responses +- [#1793](https://github.com/sanic-org/sanic/pull/1793) and + [#1819](https://github.com/sanic-org/sanic/pull/1819) Upgrade + `str.format()` to f-strings +- [#1798](https://github.com/sanic-org/sanic/pull/1798) Allow + multiple workers on MacOS with Python 3.8 +- [#1820](https://github.com/sanic-org/sanic/pull/1820) Do not set + content-type and content-length headers in exceptions + +**Bugfixes** + +- [#1748](https://github.com/sanic-org/sanic/pull/1748) Remove loop + argument in `asyncio.Event` in Python 3.8 +- [#1764](https://github.com/sanic-org/sanic/pull/1764) Allow route + decorators to stack up again +- [#1789](https://github.com/sanic-org/sanic/pull/1789) Fix tests + using hosts yielding incorrect `url_for` +- [#1808](https://github.com/sanic-org/sanic/pull/1808) Fix Ctrl+C + and tests on Windows + +**Deprecations and Removals** + +- [#1800](https://github.com/sanic-org/sanic/pull/1800) Begin + deprecation in way of first-class streaming, removal of + `body_init`, `body_push`, and `body_finish` +- [#1801](https://github.com/sanic-org/sanic/pull/1801) Complete + deprecation from + [#1666](https://github.com/sanic-org/sanic/pull/1666) of + dictionary context on `request` objects. +- [#1807](https://github.com/sanic-org/sanic/pull/1807) Remove + server config args that can be read directly from app +- [#1818](https://github.com/sanic-org/sanic/pull/1818) Complete + deprecation of `app.remove_route` and `request.raw_args` + +**Dependencies** + +- [#1794](https://github.com/sanic-org/sanic/pull/1794) Bump `httpx` + to 0.11.1 +- [#1806](https://github.com/sanic-org/sanic/pull/1806) Import + `ASGIDispatch` from top-level `httpx` (from third-party + deprecation) + +**Developer infrastructure** + +- [#1833](https://github.com/sanic-org/sanic/pull/1833) Resolve + broken documentation builds + +**Improved Documentation** + +- [#1755](https://github.com/sanic-org/sanic/pull/1755) Usage of + `response.empty()` +- [#1778](https://github.com/sanic-org/sanic/pull/1778) Update + README +- [#1783](https://github.com/sanic-org/sanic/pull/1783) Fix typo +- [#1784](https://github.com/sanic-org/sanic/pull/1784) Corrected + changelog for docs move of MD to RST + ([#1691](https://github.com/sanic-org/sanic/pull/1691)) +- [#1803](https://github.com/sanic-org/sanic/pull/1803) Update + config docs to match DEFAULT_CONFIG +- [#1814](https://github.com/sanic-org/sanic/pull/1814) Update + getting_started.rst +- [#1814](https://github.com/sanic-org/sanic/pull/1814) Update + getting_started.rst +- [#1821](https://github.com/sanic-org/sanic/pull/1821) Update to + deployment +- [#1834](https://github.com/sanic-org/sanic/pull/1834) Order of + listeners + +## Version 19.12.0 + +Version 19.12.0 + +- Fix blueprint middleware application + + Currently, any blueprint middleware registered, irrespective of + which blueprint was used to do so, was being applied to all of the + routes created by the `@app` and `@blueprint` alike. + + As part of this change, the blueprint based middleware application + is enforced based on where they are registered. + + - If you register a middleware via `@blueprint.middleware` then it + will apply only to the routes defined by the blueprint. + - If you register a middleware via `@blueprint.middleware` then it + will apply only to the routes defined by the blueprint. + - If you define a middleware via `@app.middleware` then it will be + applied on all available routes + ([#37](https://github.com/sanic-org/sanic/issues/37)) + +- Fix [url_for]{.title-ref} behavior with missing SERVER_NAME + + If the [SERVER_NAME]{.title-ref} was missing in the + [app.config]{.title-ref} entity, the [url_for]{.title-ref} on the + [request]{.title-ref} and [app]{.title-ref} were failing due to an + [AttributeError]{.title-ref}. This fix makes the availability of + [SERVER_NAME]{.title-ref} on our [app.config]{.title-ref} an + optional behavior. + ([#1707](https://github.com/sanic-org/sanic/issues/1707)) + +**Improved Documentation** + +- Move docs from MD to RST + + Moved all docs from markdown to restructured text like the rest of + the docs to unify the scheme and make it easier in the future to + update documentation. + ([#1691](https://github.com/sanic-org/sanic/issues/1691)) + +- Fix documentation for [get]{.title-ref} and [getlist]{.title-ref} of + the [request.args]{.title-ref} + + Add additional example for showing the usage of + [getlist]{.title-ref} and fix the documentation string for + [request.args]{.title-ref} behavior + ([#1704](https://github.com/sanic-org/sanic/issues/1704)) + +## Version 19.6.3 + +**Features** + +- Enable Towncrier Support + + As part of this feature, [towncrier]{.title-ref} is being introduced + as a mechanism to partially automate the process of generating and + managing change logs as part of each of pull requests. + ([#1631](https://github.com/sanic-org/sanic/issues/1631)) + +**Improved Documentation** + +- Documentation infrastructure changes + - Enable having a single common [CHANGELOG]{.title-ref} file for + both GitHub page and documentation + - Fix Sphinix deprecation warnings + - Fix documentation warnings due to invalid [rst]{.title-ref} + indentation + - Enable common contribution guidelines file across GitHub and + documentation via [CONTRIBUTING.rst]{.title-ref} + ([#1631](https://github.com/sanic-org/sanic/issues/1631)) + +## Version 19.6.2 + +**Features** + +- [#1562](https://github.com/sanic-org/sanic/pull/1562) Remove + `aiohttp` dependency and create new `SanicTestClient` based upon + [requests-async](https://github.com/encode/requests-async) +- [#1475](https://github.com/sanic-org/sanic/pull/1475) Added ASGI + support (Beta) +- [#1436](https://github.com/sanic-org/sanic/pull/1436) Add + Configure support from object string + +**Bugfixes** + +- [#1587](https://github.com/sanic-org/sanic/pull/1587) Add missing + handle for Expect header. +- [#1560](https://github.com/sanic-org/sanic/pull/1560) Allow to + disable Transfer-Encoding: chunked. +- [#1558](https://github.com/sanic-org/sanic/pull/1558) Fix graceful + shutdown. +- [#1594](https://github.com/sanic-org/sanic/pull/1594) Strict + Slashes behavior fix + +**Deprecations and Removals** + +- [#1544](https://github.com/sanic-org/sanic/pull/1544) Drop + dependency on distutil +- [#1562](https://github.com/sanic-org/sanic/pull/1562) Drop support + for Python 3.5 +- [#1568](https://github.com/sanic-org/sanic/pull/1568) Deprecate + route removal. + +.. warning:: Warning + +``` +Sanic will not support Python 3.5 from version 19.6 and forward. +However, version 18.12LTS will have its support period extended thru +December 2020, and therefore passing Python\'s official support version +3.5, which is set to expire in September 2020. +``` + +## Version 19.3 + +**Features** + +- [#1497](https://github.com/sanic-org/sanic/pull/1497) Add support + for zero-length and RFC 5987 encoded filename for + multipart/form-data requests. + +- [#1484](https://github.com/sanic-org/sanic/pull/1484) The type of + `expires` attribute of `sanic.cookies.Cookie` is now enforced to + be of type `datetime`. + +- [#1482](https://github.com/sanic-org/sanic/pull/1482) Add support + for the `stream` parameter of `sanic.Sanic.add_route()` available + to `sanic.Blueprint.add_route()`. + +- [#1481](https://github.com/sanic-org/sanic/pull/1481) Accept + negative values for route parameters with type `int` or `number`. + +- [#1476](https://github.com/sanic-org/sanic/pull/1476) Deprecated + the use of `sanic.request.Request.raw_args` - it has a fundamental + flaw in which is drops repeated query string parameters. Added + `sanic.request.Request.query_args` as a replacement for the + original use-case. + +- [#1472](https://github.com/sanic-org/sanic/pull/1472) Remove an + unwanted `None` check in Request class `repr` implementation. This + changes the default `repr` of a Request from `` to + `` + +- [#1470](https://github.com/sanic-org/sanic/pull/1470) Added 2 new + parameters to `sanic.app.Sanic.create_server`: + + - `return_asyncio_server` - whether to return an + asyncio.Server. + - `asyncio_server_kwargs` - kwargs to pass to + `loop.create_server` for the event loop that sanic is using. + + > + + This is a breaking change. + +- [#1499](https://github.com/sanic-org/sanic/pull/1499) Added a set + of test cases that test and benchmark route resolution. + +- [#1457](https://github.com/sanic-org/sanic/pull/1457) The type of + the `"max-age"` value in a `sanic.cookies.Cookie` is now enforced + to be an integer. Non-integer values are replaced with `0`. + +- [#1445](https://github.com/sanic-org/sanic/pull/1445) Added the + `endpoint` attribute to an incoming `request`, containing the name + of the handler function. + +- [#1423](https://github.com/sanic-org/sanic/pull/1423) Improved + request streaming. `request.stream` is now a bounded-size buffer + instead of an unbounded queue. Callers must now call + `await request.stream.read()` instead of + `await request.stream.get()` to read each portion of the body. + + This is a breaking change. + +This is a breaking change. + +- [#1502](https://github.com/sanic-org/sanic/pull/1502) Sanic was + prefetching `time.time()` and updating it once per second to avoid + excessive `time.time()` calls. The implementation was observed to + cause memory leaks in some cases. The benefit of the prefetch + appeared to negligible, so this has been removed. Fixes + [#1500](https://github.com/sanic-org/sanic/pull/1500) +- [#1501](https://github.com/sanic-org/sanic/pull/1501) Fix a bug in + the auto-reloader when the process was launched as a module i.e. + `python -m init0.mod1` where the sanic server is started in + `init0/mod1.py` with `debug` enabled and imports another module in + `init0`. +- [#1376](https://github.com/sanic-org/sanic/pull/1376) Allow sanic + test client to bind to a random port by specifying `port=None` + when constructing a `SanicTestClient` +- [#1399](https://github.com/sanic-org/sanic/pull/1399) Added the + ability to specify middleware on a blueprint group, so that all + routes produced from the blueprints in the group have the + middleware applied. +- [#1442](https://github.com/sanic-org/sanic/pull/1442) Allow the + the use the `SANIC_ACCESS_LOG` environment variable to + enable/disable the access log when not explicitly passed to + `app.run()`. This allows the access log to be disabled for example + when running via gunicorn. + +**Developer infrastructure** + +- [#1529](https://github.com/sanic-org/sanic/pull/1529) Update + project PyPI credentials +- [#1515](https://github.com/sanic-org/sanic/pull/1515) fix linter + issue causing travis build failures (fix #1514) +- [#1490](https://github.com/sanic-org/sanic/pull/1490) Fix python + version in doc build +- [#1478](https://github.com/sanic-org/sanic/pull/1478) Upgrade + setuptools version and use native docutils in doc build +- [#1464](https://github.com/sanic-org/sanic/pull/1464) Upgrade + pytest, and fix caplog unit tests + +**Improved Documentation** + +- [#1516](https://github.com/sanic-org/sanic/pull/1516) Fix typo at + the exception documentation +- [#1510](https://github.com/sanic-org/sanic/pull/1510) fix typo in + Asyncio example +- [#1486](https://github.com/sanic-org/sanic/pull/1486) + Documentation typo +- [#1477](https://github.com/sanic-org/sanic/pull/1477) Fix grammar + in README.md +- [#1489](https://github.com/sanic-org/sanic/pull/1489) Added + \"databases\" to the extensions list +- [#1483](https://github.com/sanic-org/sanic/pull/1483) Add + sanic-zipkin to extensions list +- [#1487](https://github.com/sanic-org/sanic/pull/1487) Removed link + to deleted repo, Sanic-OAuth, from the extensions list +- [#1460](https://github.com/sanic-org/sanic/pull/1460) 18.12 + changelog +- [#1449](https://github.com/sanic-org/sanic/pull/1449) Add example + of amending request object +- [#1446](https://github.com/sanic-org/sanic/pull/1446) Update + README +- [#1444](https://github.com/sanic-org/sanic/pull/1444) Update + README +- [#1443](https://github.com/sanic-org/sanic/pull/1443) Update + README, including new logo +- [#1440](https://github.com/sanic-org/sanic/pull/1440) fix minor + type and pip install instruction mismatch +- [#1424](https://github.com/sanic-org/sanic/pull/1424) + Documentation Enhancements + +Note: 19.3.0 was skipped for packagement purposes and not released on +PyPI + +## Version 18.12 + +### 18.12.0 + +- Changes: + + - Improved codebase test coverage from 81% to 91%. + - Added stream_large_files and host examples in static_file + document + - Added methods to append and finish body content on Request + (#1379) + - Integrated with .appveyor.yml for windows ci support + - Added documentation for AF_INET6 and AF_UNIX socket usage + - Adopt black/isort for codestyle + - Cancel task when connection_lost + - Simplify request ip and port retrieval logic + - Handle config error in load config file. + - Integrate with codecov for CI + - Add missed documentation for config section. + - Deprecate Handler.log + - Pinned httptools requirement to version 0.0.10+ + +- Fixes: + + - Fix `remove_entity_headers` helper function (#1415) + - Fix TypeError when use Blueprint.group() to group blueprint + with default url_prefix, Use os.path.normpath to avoid invalid + url_prefix like api//v1 f8a6af1 Rename the `http` module to + `helpers` to prevent conflicts with the built-in Python http + library (fixes #1323) + - Fix unittests on windows + - Fix Namespacing of sanic logger + - Fix missing quotes in decorator example + - Fix redirect with quoted param + - Fix doc for latest blueprint code + - Fix build of latex documentation relating to markdown lists + - Fix loop exception handling in app.py + - Fix content length mismatch in windows and other platform + - Fix Range header handling for static files (#1402) + - Fix the logger and make it work (#1397) + - Fix type pikcle-\>pickle in multiprocessing test + - Fix pickling blueprints Change the string passed in the + \"name\" section of the namedtuples in Blueprint to match the + name of the Blueprint module attribute name. This allows + blueprints to be pickled and unpickled, without errors, which + is a requirement of running Sanic in multiprocessing mode in + Windows. Added a test for pickling and unpickling blueprints + Added a test for pickling and unpickling sanic itself Added a + test for enabling multiprocessing on an app with a blueprint + (only useful to catch this bug if the tests are run on + Windows). + - Fix document for logging + +## Version 0.8 + +**0.8.3** + +- Changes: + - Ownership changed to org \'sanic-org\' + +**0.8.0** + +- Changes: + - Add Server-Sent Events extension (Innokenty Lebedev) + - Graceful handling of request_handler_task cancellation (Ashley + Sommer) + - Sanitize URL before redirection (aveao) + - Add url_bytes to request (johndoe46) + - py37 support for travisci (yunstanford) + - Auto reloader support for OSX (garyo) + - Add UUID route support (Volodymyr Maksymiv) + - Add pausable response streams (Ashley Sommer) + - Add weakref to request slots (vopankov) + - remove ubuntu 12.04 from test fixture due to deprecation + (yunstanford) + - Allow streaming handlers in add_route (kinware) + - use travis_retry for tox (Raphael Deem) + - update aiohttp version for test client (yunstanford) + - add redirect import for clarity (yingshaoxo) + - Update HTTP Entity headers (Arnulfo Solís) + - Add register_listener method (Stephan Fitzpatrick) + - Remove uvloop/ujson dependencies for Windows (abuckenheimer) + - Content-length header on 204/304 responses (Arnulfo Solís) + - Extend WebSocketProtocol arguments and add docs (Bob Olde + Hampsink, yunstanford) + - Update development status from pre-alpha to beta (Maksim + Anisenkov) + - KeepAlive Timeout log level changed to debug (Arnulfo Solís) + - Pin pytest to 3.3.2 because of pytest-dev/pytest#3170 (Maksim + Aniskenov) + - Install Python 3.5 and 3.6 on docker container for tests (Shahin + Azad) + - Add support for blueprint groups and nesting (Elias Tarhini) + - Remove uvloop for windows setup (Aleksandr Kurlov) + - Auto Reload (Yaser Amari) + - Documentation updates/fixups (multiple contributors) +- Fixes: + - Fix: auto_reload in Linux (Ashley Sommer) + - Fix: broken tests for aiohttp \>= 3.3.0 (Ashley Sommer) + - Fix: disable auto_reload by default on windows (abuckenheimer) + - Fix (1143): Turn off access log with gunicorn (hqy) + - Fix (1143): Turn off access log with gunicorn (hqy) + - Fix (1266): Add content_type flag to Sanic.static (Cosmo Borsky) + - Fix: subprotocols parameter missing from add_websocket_route + (ciscorn) + - Fix (1242): Responses for CI header (yunstanford) + - Fix (1237): add version constraint for websockets (yunstanford) + - Fix (1231): memory leak - always release resource (Phillip Xu) + - Fix (1221): make request truthy if transport exists (Raphael + Deem) + - Fix failing tests for aiohttp\>=3.1.0 (Ashley Sommer) + - Fix failing tests for aiohttp\>=3.1.0 (Ashley Sommer) + - Fix (1158): default to auto_reload in debug mode (Raphael Deem) + - Fix (1136): ErrorHandler.response handler call too restrictive + (Julien Castiaux) + - Fix: raw requires bytes-like object (cloudship) + - Fix (1120): passing a list in to a route decorator\'s host arg + (Timothy Ebiuwhe) + - Fix: Bug in multipart/form-data parser (DirkGuijt) + - Fix: Exception for missing parameter when value is null + (NyanKiyoshi) + - Fix: Parameter check (Howie Hu) + - Fix (1089): Routing issue with named parameters and different + methods (yunstanford) + - Fix (1085): Signal handling in multi-worker mode (yunstanford) + - Fix: single quote in readme.rst (Cosven) + - Fix: method typos (Dmitry Dygalo) + - Fix: log_response correct output for ip and port (Wibowo + Arindrarto) + - Fix (1042): Exception Handling (Raphael Deem) + - Fix: Chinese URIs (Howie Hu) + - Fix (1079): timeout bug when self.transport is None (Raphael + Deem) + - Fix (1074): fix strict_slashes when route has slash (Raphael + Deem) + - Fix (1050): add samesite cookie to cookie keys (Raphael Deem) + - Fix (1065): allow add_task after server starts (Raphael Deem) + - Fix (1061): double quotes in unauthorized exception (Raphael + Deem) + - Fix (1062): inject the app in add_task method (Raphael Deem) + - Fix: update environment.yml for readthedocs (Eli Uriegas) + - Fix: Cancel request task when response timeout is triggered + (Jeong YunWon) + - Fix (1052): Method not allowed response for RFC7231 compliance + (Raphael Deem) + - Fix: IPv6 Address and Socket Data Format (Dan Palmer) + +Note: Changelog was unmaintained between 0.1 and 0.7 + +## Version 0.1 + +**0.1.7** + +- Reversed static url and directory arguments to meet spec + +**0.1.6** + +- Static files +- Lazy Cookie Loading + +**0.1.5** + +- Cookies +- Blueprint listeners and ordering +- Faster Router +- Fix: Incomplete file reads on medium+ sized post requests +- Breaking: after_start and before_stop now pass sanic as their + first argument + +**0.1.4** + +- Multiprocessing + +**0.1.3** + +- Blueprint support +- Faster Response processing + +**0.1.1 - 0.1.2** + +- Struggling to update pypi via CI + +**0.1.0** + +- Released to public