REST APIは、Web間のインターフェースを確立するための一般的な方法になりました バックエンド そして フロントエンド 、および異なるWebサービス間。この種のインターフェースのシンプルさと、さまざまなネットワークやフレームワークにわたるHTTPおよびHTTPSプロトコルのユビキタスサポートにより、相互運用性の問題を検討する際に簡単に選択できます。
ボトル ミニマリストのPythonWebフレームワークです。軽量、高速、使いやすく、RESTfulサービスの構築に最適です。 A 必要最低限の比較 作られた Andriy Kornatskyy 応答時間とスループット(1秒あたりのリクエスト数)の観点から、上位3つのフレームワークの1つに入れてください。 DigitalOceanから入手できる仮想サーバーでの私自身のテストでは、uWSGIサーバースタックとBottleの組み合わせにより、要求ごとに140μsのオーバーヘッドを達成できることがわかりました。
この記事では、Bottleを使用してRESTfulAPIサービスを構築する方法のウォークスルーを提供します。
ボトルフレームワークは、その軽量さのおかげで、その印象的なパフォーマンスを実現しています。実際、ライブラリ全体が1つのファイルモジュールとして配布されています。これは、他のフレームワークほど手を握らないことを意味しますが、より柔軟性があり、多くの異なる技術スタックに適合するように適合させることができます。したがって、Bottleは、パフォーマンスとカスタマイズ性が重要視され、より頑丈なフレームワークの時間節約の利点があまり考慮されていないプロジェクトに最適です。
ボトルの柔軟性により、プラットフォームのセットアップについての詳細な説明は、独自のスタックを反映していない可能性があるため、少し無駄になります。ただし、オプションの概要と、オプションの設定方法の詳細については、ここで説明します。
Bottleのインストールは、他のPythonパッケージのインストールと同じくらい簡単です。オプションは次のとおりです。
pip install bottle
でPythonPackageIndexを使用してシステムにインストールします。ボトルを仮想環境にインストールするには、 virtualenv そして ピップ ツール。それらをインストールするには、を参照してください virtualenv そして ピップ ドキュメントは、おそらくすでにシステムにありますが。
Bashで、Python3を使用して環境を作成します。
$ virtualenv -p `which python3` env
-p `which python3`
の抑制パラメータを指定すると、システムに存在するデフォルトのPythonインタプリタ(通常はPython 2.7)がインストールされます。 Python 2.7がサポートされていますが、このチュートリアルではPython3.4を想定しています。
次に、環境をアクティブにして、Bottleをインストールします。
$ . env/bin/activate $ pip install bottle
それでおしまい。ボトルが取り付けられ、すぐに使用できます。よく知らない場合 virtualenv または ピップ 、彼らのドキュメントは一流です。見てください!彼らはそれだけの価値があります。
ボトルはPythonの標準に準拠しています Webサーバーゲートウェイインターフェイス(WSGI) 、つまり、WSGI準拠の任意のサーバーで使用できます。これも uWSGI 、 ツイスター 、 Gunicorn 、 Apache 、 アマゾンビーンズトーク 、 Google App Engine 、 その他。
正しい設定方法は、環境ごとに若干異なります。ボトルはWSGIインターフェースに準拠するオブジェクトを公開し、サーバーはこのオブジェクトと対話するように構成する必要があります。
サーバーの設定方法の詳細については、サーバーのドキュメントとボトルのドキュメントを参照してください。 ここに 。
ボトルはデータベースに依存せず、データがどこから来ているかを気にしません。アプリでデータベースを使用する場合は、 Python PackageIndex のようないくつかの興味深いオプションがあります SQLAlchemy 、 PyMongo 、 MongoEngine 、 CouchDB そして 投票 DynamoDBの場合。選択したデータベースで動作させるには、適切なアダプタのみが必要です。
それでは、Bottleで基本的なアプリを作成する方法を見てみましょう。コード例では、Python> = 3.4と仮定します。ただし、ここで説明する内容のほとんどは、Python2.7でも機能します。
ボトルの基本的なアプリは次のようになります。
import bottle app = application = bottle.default_app() if __name__ == '__main__': bottle.run(host = '127.0.0.1', port = 8000)
基本と言えば、このプログラムは「HelloWorld」でさえありません。 (「HelloWorld?」と応答したRESTインターフェースに最後にアクセスしたのはいつですか?)127.0.0.1:8000
へのすべてのHTTPリクエスト404 NotFound応答ステータスを受け取ります。
ボトルにはアプリのインスタンスがいくつか作成されている場合がありますが、便宜上、最初のインスタンスが作成されます。これがデフォルトのアプリです。ボトルは、これらのインスタンスをモジュール内部のスタックに保持します。ボトルを使用して何か(アプリの実行やルートの添付など)を行い、話しているアプリを指定しない場合は常に、デフォルトのアプリを参照します。実際、app = application = bottle.default_app()
この基本的なアプリにlineが存在する必要はありませんが、Gunicorn、uWSGI、または一般的なWSGIサーバーを使用してデフォルトのアプリを簡単に呼び出すことができるように存在します。
複数のアプリの可能性は最初は混乱しているように見えるかもしれませんが、Bottleに柔軟性を追加します。アプリケーションのさまざまなモジュールについて、他のボトルクラスをインスタンス化し、必要に応じてさまざまな構成でセットアップすることにより、特殊なボトルアプリを作成できます。これらのさまざまなアプリには、BottleのURLルーターを介してさまざまなURLからアクセスできます。このチュートリアルでは詳しく説明しませんが、Bottleのドキュメントをご覧になることをお勧めします。 ここに そして ここに 。
スクリプトの最後の行は、指定されたサーバーを使用してBottleを実行します。ここの場合のようにサーバーが示されていない場合、デフォルトのサーバーはPythonの組み込みWSGI参照サーバーであり、開発目的にのみ適しています。次のように別のサーバーを使用できます。
bottle.run(server='gunicorn', host = '127.0.0.1', port = 8000)
これは、このスクリプトを実行してアプリを起動できるシンタックスシュガーです。たとえば、このファイルの名前がmain.py
の場合、python main.py
を実行するだけです。アプリを起動します。ボトルキャリー サーバーアダプタの非常に広範なリスト これはこのように使用できます。
一部のWSGIサーバーにはボトルアダプターがありません。これらは、サーバー独自の実行コマンドで開始できます。たとえば、uWSGIでは、uwsgi
に電話するだけです。このような:
$ uwsgi --http :8000 --wsgi-file main.py
ボトルは、アプリのファイル構造を完全にあなたに任せます。ファイル構造ポリシーはプロジェクトごとに進化しますが、MVC哲学に基づいている傾向があります。
もちろん、要求されたURIごとに404を返すだけのサーバーは必要ありません。 REST APIを構築することを約束したので、それを実行しましょう。
あなたがしたいとします インターフェースを構築する 名前のセットを操作します。実際のアプリでは、おそらくこれにデータベースを使用しますが、この例では、インメモリset
を使用します。データ構造。
APIのスケルトンは次のようになります。このコードはプロジェクトのどこにでも配置できますが、api/names.py
などの別のAPIファイルをお勧めします。
from bottle import request, response from bottle import post, get, put, delete _names = set() # the set of names @post('/names') def creation_handler(): '''Handles name creation''' pass @get('/names') def listing_handler(): '''Handles name listing''' pass @put('/names/') def update_handler(name): '''Handles name updates''' pass @delete('/names/') def delete_handler(name): '''Handles name deletions''' pass
ご覧のとおり、Bottleでのルーティングはデコレータを使用して行われます。インポートされたデコレータpost
、get
、put
、およびdelete
これらの4つのアクションのハンドラーを登録します。これらの作業を次のように分類する方法を理解します。
default_app
へのショートカットです。ルーティングデコレータ。たとえば、@get()
デコレータが適用されますbottle.default_app().get()
ハンドラーに。default_app
のルーティング方法route()
のすべてのショートカットです。だからdefault_app().get('/')
default_app().route(method='GET', '/')
と同等です。だから@get('/')
は@route(method='GET', '/')
と同じです。これは@bottle.default_app().route(method='GET', '/')
と同じであり、これらは同じ意味で使用できます。
@route
に関する1つの役立つことデコレータは、たとえば、同じハンドラを使用してオブジェクトの更新と削除の両方を処理する場合、次のように処理するメソッドのリストを渡すことができます。
@route('/names/', method=['PUT', 'DELETE']) def update_delete_handler(name): '''Handles name updates and deletions''' pass
それでは、これらのハンドラーのいくつかを実装しましょう。
POSTハンドラーは次のようになります。
import re, json namepattern = re.compile(r'^[a-zA-Zd]{1,64}$') @post('/names') def creation_handler(): '''Handles name creation''' try: # parse input data try: data = request.json() except: raise ValueError if data is None: raise ValueError # extract and validate name try: if namepattern.match(data['name']) is None: raise ValueError name = data['name'] except (TypeError, KeyError): raise ValueError # check for existence if name in _names: raise KeyError except ValueError: # if bad request data, return 400 Bad Request response.status = 400 return except KeyError: # if name already exists, return 409 Conflict response.status = 409 return # add name _names.add(name) # return 200 Success response.headers['Content-Type'] = 'application/json' return json.dumps({'name': name})
まあ、それはかなりたくさんです。これらの手順を少しずつ確認してみましょう。
ボディ解析
このAPIでは、ユーザーが「name」という名前の属性を持つJSON文字列を本文にPOSTする必要があります。
request
bottle
から以前にインポートされたオブジェクト常に現在のリクエストを指し、リクエストのすべてのデータを保持します。そのbody
属性には、リクエスト本文のバイトストリームが含まれます。これには、ストリームオブジェクトを読み取ることができる任意の関数(ファイルの読み取りなど)からアクセスできます。
request.json()
メソッドは、リクエストのヘッダーで「application / json」コンテンツタイプをチェックし、正しい場合は本文を解析します。 Bottleが不正な形式のボディ(例:空または間違ったコンテンツタイプ)を検出した場合、このメソッドはNone
を返します。したがって、ValueError
を発生させます。不正な形式のJSONコンテンツがJSONパーサーによって検出された場合。これもまたValueError
としてキャッチしてリレイズするという例外を発生させます。
オブジェクトの解析と検証
エラーがない場合は、リクエストの本文をdata
によって参照されるPythonオブジェクトに変換しました。変数。 「名前」キーが付いた辞書を受け取った場合は、data['name']
からアクセスできます。このキーのない辞書を受け取った場合、それにアクセスしようとするとKeyError
になります。例外。辞書以外のものを受け取った場合は、TypeError
を受け取ります例外。これらのエラーのいずれかが発生した場合は、もう一度、ValueError
として再レイズし、入力が正しくないことを示します。
名前キーの形式が正しいかどうかを確認するには、namepattern
などの正規表現マスクに対してテストする必要があります。ここで作成したマスク。キーの場合name
文字列ではありません、namepattern.match()
TypeError
を発生させ、一致しない場合はNone
を返します。
この例のマスクでは、名前は1〜64文字の空白のないASCII英数字である必要があります。これは単純な検証であり、たとえば、ガベージデータを含むオブジェクトをテストしません。より複雑で完全な検証は、次のようなツールを使用して実現できます。 FormEncode 。
存在のテスト
要求を満たす前の最後のテストは、指定された名前がセットにすでに存在するかどうかです。より構造化されたアプリでは、そのテストはおそらく専用モジュールによって実行され、特殊な例外を介してAPIに通知される必要がありますが、セットを直接操作しているため、ここで実行する必要があります。
KeyError
を上げることで、名前の存在を知らせます。
エラー応答
リクエストオブジェクトがすべてのリクエストデータを保持するのと同じように、レスポンスオブジェクトもレスポンスデータに対して同じことを行います。応答ステータスを設定するには、次の2つの方法があります。
response.status = 400
そして:
response.status = '400 Bad Request'
この例では、より単純な形式を選択しましたが、2番目の形式を使用してエラーのテキストの説明を指定できます。内部的に、Bottleは2番目の文字列を分割し、数値コードを適切に設定します。
成功への対応
すべてのステップが成功した場合、セット_names
に名前を追加し、Content-Type
を設定することで、リクエストを実行します。応答ヘッダー、および応答を返します。関数によって返される文字列はすべて、200 Success
の応答本文として扱われます。応答なので、json.dumps
で単純に生成します。
名前の作成から進んで、名前リストハンドラーを実装します。
@get('/names') def listing_handler(): '''Handles name listing''' response.headers['Content-Type'] = 'application/json' response.headers['Cache-Control'] = 'no-cache' return json.dumps({'names': list(_names)})
名前をリストするのはずっと簡単でしたね。名前の作成と比較して、ここで行うことはあまりありません。いくつかの応答ヘッダーを設定し、すべての名前のJSON表現を返すだけで、完了です。
それでは、updateメソッドを実装する方法を見てみましょう。 createメソッドと大差ありませんが、この例を使用してURIパラメータを導入します。
@put('/names/') def update_handler(name): '''Handles name updates''' try: # parse input data try: data = json.load(utf8reader(request.body)) except: raise ValueError # extract and validate new name try: if namepattern.match(data['name']) is None: raise ValueError newname = data['name'] except (TypeError, KeyError): raise ValueError # check if updated name exists if oldname not in _names: raise KeyError(404) # check if new name exists if name in _names: raise KeyError(409) except ValueError: response.status = 400 return except KeyError as e: response.status = e.args[0] return # add new name and remove old name _names.remove(oldname) _names.add(newname) # return 200 Success response.headers['Content-Type'] = 'application/json' return json.dumps({'name': newname})
更新アクションの本体スキーマは作成アクションの場合と同じですが、新しいoldname
もあります。ルート@put('/names/')
で定義されているURIのパラメーター。
Googleはどのバージョン管理を使用しますか
URIパラメータ
ご覧のとおり、URIパラメータのBottleの表記は非常に簡単です。必要な数のパラメーターを使用してURIを作成できます。ボトルはそれらをURIから自動的に抽出し、リクエストハンドラーに渡します。
@get('//') def handler(param1, param2): pass
カスケードルートデコレータを使用して、オプションのパラメータを使用してURIを作成できます。
@get('/') @get('//') def handler(param1, param2 = None) pass
また、Bottleでは、URIで次のルーティングフィルターを使用できます。
int
int
に変換される可能性のあるパラメーターのみに一致し、変換された値をハンドラーに渡します。@get('/') def handler(param): pass
float
int
と同じですが、浮動小数点値があります。@get('/') def handler(param): pass
re
(正規表現)指定された正規表現に一致するパラメーターのみに一致します。
@get('/') def handler(param): pass
path
柔軟な方法でURIパスのサブセグメントを照合します。
@get('//id>') def handler(param): pass
一致:
/x/id
、合格x
param
として。/x/y/id
、合格x/y
param
として。
GETメソッドと同様に、DELETEメソッドはほとんどニュースをもたらしません。 None
を返すことに注意してくださいステータスを設定しないと、本文が空でステータスコードが200の応答が返されます。
@delete('/names/') def delete_handler(name): '''Handles name updates''' try: # Check if name exists if name not in _names: raise KeyError except KeyError: response.status = 404 return # Remove name _names.remove(name) return
名前APIをapi/names.py
として保存したとします。 、メインアプリケーションファイルmain.py
でこれらのルートを有効にできるようになりました。
import bottle from api import names app = application = bottle.default_app() if __name__ == '__main__': bottle.run(host = '127.0.0.1', port = 8000)
names
のみをインポートしたことに注意してくださいモジュール。すべてのメソッドをデフォルトのアプリにアタッチされたURIで装飾しているため、これ以上設定する必要はありません。私たちのメソッドはすでに配置されており、アクセスする準備ができています。
CurlやPostmanなどのツールを使用してAPIを使用し、手動でテストできます。 (Curlを使用している場合は、 JSONフォーマッター 応答がすっきりと見えるようにします。)
REST APIを構築する一般的な理由の1つは、AJAXを介してJavaScriptフロントエンドと通信することです。一部のアプリケーションでは、これらのリクエストは、APIのホームドメインだけでなく、任意のドメインからの送信を許可する必要があります。デフォルトでは、ほとんどのブラウザはこの動作を許可していないので、設定方法を紹介します クロスオリジンリソースシェアリング(CORS) これを可能にするためにボトルで:
from bottle import hook, route, response _allow_origin = '*' _allow_methods = 'PUT, GET, POST, DELETE, OPTIONS' _allow_headers = 'Authorization, Origin, Accept, Content-Type, X-Requested-With' @hook('after_request') def enable_cors(): '''Add headers to enable CORS''' response.headers['Access-Control-Allow-Origin'] = _allow_origin response.headers['Access-Control-Allow-Methods'] = _allow_methods response.headers['Access-Control-Allow-Headers'] = _allow_headers @route('/', method = 'OPTIONS') @route('/', method = 'OPTIONS') def options_handler(path = None): return
hook
デコレータを使用すると、各リクエストの前後に関数を呼び出すことができます。この場合、CORSを有効にするには、Access-Control-Allow-Origin
、-Allow-Methods
を設定する必要があります。および-Allow-Headers
各応答のヘッダー。これらは、指定されたリクエストを処理することをリクエスターに示します。
また、クライアントはサーバーに対してOPTIONS HTTP要求を行って、他の方法で実際に要求を行うことができるかどうかを確認する場合があります。このサンプルのキャッチオールの例では、200のステータスコードと空の本文ですべてのOPTIONSリクエストに応答します。
これを有効にするには、保存してメインモジュールからインポートするだけです。
これですべてです。
このチュートリアルでは、BottleWebフレームワークを使用してPythonアプリ用のRESTAPIを作成するための基本的な手順について説明しました。
この小さいながらも強力なフレームワークについての知識を深めるには、 チュートリアル そして APIリファレンスドキュメント 。
関連: Node.js / TypeScript REST APIの構築、パート1:Express.js