Railsを使用したAPIの作成
今日では、API(アプリケーションプログラミングインターフェイス)に大きく依存するのが一般的な方法です。 FacebookやTwitterなどの大規模なサービスだけでなく、APIは、React、Angularなどのクライアント側フレームワークが普及しているため非常に人気があります。 Ruby on Railsはこの傾向に従っており、最新バージョンでは、APIのみのアプリケーションを作成できる新機能が提供されています。
当初、この機能はrails-apiと呼ばれる別のgemにパックされていましたが、Rails 5のリリース以降、フレームワークのコアの一部になりました。この機能とActionCableはおそらく最も期待されていたので、今日はそれについて説明します。
この記事では、APIのみのRailsアプリケーションを作成する方法と、ルートとコントローラーを構造化する方法、JSON形式で応答する方法、シリアライザーを追加する方法、CORS(クロスオリジンリソースシェアリング)を設定する方法について説明します。また、APIを保護し、悪用から保護するためのいくつかのオプションについても学びます。
この記事のソースはGitHubで入手できます。
APIのみのアプリケーションの作成
開始するには、次のコマンドを実行します。
rails new RailsApiDemo --api
RailsApiDemoと呼ばれる新しいAPIのみのRailsアプリケーションを作成します 。 --apiのサポートを忘れないでください オプションはRails5でのみ追加されたため、このバージョンまたは新しいバージョンがインストールされていることを確認してください。
Gemfileを開きます そして、それは通常よりもはるかに小さいことに注意してください:coffee-railsのような宝石 、turbolinks 、およびsass-rails なくなった。
config / application.rb ファイルに新しい行が含まれています:
config.api_only = true
これは、Railsがより小さなミドルウェアのセットをロードすることを意味します。たとえば、Cookieとセッションのサポートはありません。さらに、スキャフォールドを生成しようとすると、ビューとアセットは作成されません。実際、ビュー/レイアウトを確認すると ディレクトリの場合、 application.html.erb ファイルもありません。
もう1つの重要な違いは、ApplicationController ActionController::APIから継承します 、ActionController::Baseではありません 。
これでほぼ完了です。全体として、これは何度も見た基本的なRailsアプリケーションです。次に、いくつかのモデルを追加して、作業できるようにします。
rails g model User name:string rails g model Post title:string body:text user:belongs_to rails db:migrate
ここでは特別なことは何も起こっていません。タイトルと本文がユーザーに属している投稿です。
適切な関連付けが設定されていることを確認し、いくつかの簡単な検証チェックも提供します。
models / user.rb
has_many :posts validates :name, presence: true
models / post.rb
belongs_to :user validates :title, presence: true validates :body, presence: true
素晴らしい!次のステップは、新しく作成されたテーブルにいくつかのサンプルレコードをロードすることです。
デモデータの読み込み
一部のデータを読み込む最も簡単な方法は、 seeds.rbを利用することです。 db内のファイル ディレクトリ。しかし、私は(多くのプログラマーがそうであるように)怠惰であり、サンプルコンテンツについて考えたくありません。したがって、名前、電子メール、流行に敏感な単語、「loremipsum」テキストなどのさまざまな種類のランダムデータを生成できる偽物の宝石を利用してみませんか。
ジェムファイル
group :development do
gem 'faker'
end 宝石をインストールします:
bundle install
次に、 seeds.rbを微調整します :
db / seeds.rb
5.times do
user = User.create({name: Faker::Name.name})
user.posts.create({title: Faker::Book.title, body: Faker::Lorem.sentence})
end 最後に、データを読み込みます:
rails db:seed
JSONで応答する
もちろん、APIを作成するためにいくつかのルートとコントローラーが必要です。 api/の下にAPIのルートをネストするのが一般的な方法です 道。また、開発者は通常、パスにAPIのバージョンを提供します(例:api/v1/)。 。後で、いくつかの重大な変更を導入する必要がある場合は、新しい名前空間(v2)を作成するだけです。 )と別のコントローラー。
ルートは次のようになります。
config / routers.rb
namespace 'api' do
namespace 'v1' do
resources :posts
resources :users
end
end これにより、次のようなルートが生成されます:
api_v1_posts GET /api/v1/posts(.:format) api/v1/posts#index
POST /api/v1/posts(.:format) api/v1/posts#create
api_v1_post GET /api/v1/posts/:id(.:format) api/v1/posts#show
scopeを使用できます namespaceの代わりにメソッド 、ただし、デフォルトではUsersControllerを検索します およびPostsController コントローラーの内部 controllers / api / v1内ではなくディレクトリ 、注意してください。
apiを作成します ネストされたディレクトリを持つフォルダv1 コントローラーの内部 。コントローラを追加します:
controllers / api / v1 / users_controller.rb
module Api
module V1
class UsersController < ApplicationController
end
end
end controllers / api / v1 / posts_controller.rb
module Api
module V1
class PostsController < ApplicationController
end
end
end
api / v1の下にコントローラーのファイルをネストする必要があるだけではないことに注意してください。 パスですが、クラス自体もApi内で名前空間を設定する必要があります およびV1 モジュール。
次の質問は、JSON形式のデータで適切に応答する方法です。この記事では、jBuilderとactive_model_serializersのgemというソリューションを試してみます。したがって、次のセクションに進む前に、それらを Gemfileにドロップしてください。 :
ジェムファイル
gem 'jbuilder', '~> 2.5' gem 'active_model_serializers', '~> 0.10.0'
次に実行します:
bundle install
jBuilderGemの使用
jBuilderは、Railsチームによって維持されている人気のあるgemであり、ビューでJSON構造を定義できるシンプルなDSL(ドメイン固有言語)を提供します。
ユーザーがindexを押したときに、すべての投稿を表示したいとします。 アクション:
controllers / api / v1 / posts_controller.rb
def index
@posts = Post.order('created_at DESC')
end .json.jbuilder を使用して、対応するアクションにちなんで名付けられたビューを作成するだけです。 拡大。ビューはapi/ v1の下に配置する必要があることに注意してください パスも:
views / api / v1 / posts / index.json.jbuilder
json.array! @posts do |post| json.id post.id json.title post.title json.body post.body end
json.array! @postsをトラバースします 配列。 json.id 、json.title およびjson.body 引数を値として設定する対応する名前でキーを生成します。 http:// localhost:3000 / api / v1 / posts.jsonに移動すると、次のような出力が表示されます。
[
{"id": 1, "title": "Title 1", "body": "Body 1"},
{"id": 2, "title": "Title 2", "body": "Body 2"}
] 各投稿の作成者も表示したい場合はどうなりますか?簡単です:
json.array! @posts do |post|
json.id post.id
json.title post.title
json.body post.body
json.user do
json.id post.user.id
json.name post.user.name
end
end 出力は次のように変わります:
[
{"id": 1, "title": "Title 1", "body": "Body 1", "user": {"id": 1, "name": "Username"}}
] .jbuilderの内容 ファイルはプレーンなRubyコードであるため、通常どおりすべての基本操作を利用できます。
jBuilderは、通常のRailsビューと同じようにパーシャルをサポートしているため、次のように言うこともできます。
json.partial! partial: 'posts/post', collection: @posts, as: :post
次に、 views / api / v1 / posts / _post.json.jbuilderを作成します 次の内容のファイル:
json.id post.id
json.title post.title
json.body post.body
json.user do
json.id post.user.id
json.name post.user.name
end ご覧のとおり、jBuilderは簡単で便利です。ただし、別の方法として、シリアライザーを使用することもできるので、次のセクションでそれらについて説明しましょう。
シリアライザーの使用
rails_model_serializers gemは、最初にrails-apiを管理したチームによって作成されました。ドキュメントに記載されているように、rails_model_serializersは、設定より規約をJSON生成にもたらします。基本的に、シリアル化(つまり、JSON生成)時に使用するフィールドを定義します。
これが最初のシリアライザーです:
serializers / post_serializer.rb
class PostSerializer < ActiveModel::Serializer attributes :id, :title, :body end
ここでは、これらすべてのフィールドが結果のJSONに存在する必要があると言います。現在、to_jsonのようなメソッド およびas_json 投稿時に呼び出されると、この構成が使用され、適切なコンテンツが返されます。
実際の動作を確認するには、indexを変更します このようなアクション:
controllers / api / v1 / posts_controller.rb
def index
@posts = Post.order('created_at DESC')
render json: @posts
end
as_json @postsで自動的に呼び出されます オブジェクト。
ユーザーはどうですか?シリアライザーを使用すると、モデルと同じように関係を示すことができます。さらに、シリアライザーはネストできます:
serializers / post_serializer.rb
class PostSerializer < ActiveModel::Serializer
attributes :id, :title, :body
belongs_to :user
class UserSerializer < ActiveModel::Serializer
attributes :id, :name
end
end
これで、投稿をシリアル化すると、ネストされたuserが自動的に含まれます。 IDと名前を持つキー。後で:idを使用して、ユーザー用に別のシリアライザーを作成する場合 除外される属性:
serializers / post_serializer.rb
class UserSerializer < ActiveModel::Serializer
attributes :name
end
次に、@user.as_json ユーザーのIDを返しません。それでも、@post.as_json ユーザーの名前とIDの両方が返されるので、注意してください。
APIの保護
多くの場合、APIを使用してアクションを実行するだけでは不十分です。それでは、簡単なセキュリティチェックを提示し、投稿を作成および削除するときにユーザーにトークンを送信するように強制しましょう。
トークンの有効期間は無制限で、ユーザーの登録時に作成されます。まず、新しいtokenを追加します usersへの列 テーブル:
rails g migration add_token_to_users token:string:index
同じトークンを持つ2人のユーザーは存在できないため、このインデックスは一意性を保証する必要があります。
db / migrate / xyz_add_token_to_users.rb
add_index :users, :token, unique: true
移行を適用します:
rails db:migrate
次に、before_saveを追加します コールバック:
models / user.rb
before_create -> {self.token = generate_token}
generate_token プライベートメソッドは、無限のサイクルでトークンを作成し、それが一意であるかどうかを確認します。一意のトークンが見つかったらすぐに返します:
models / user.rb
private
def generate_token
loop do
token = SecureRandom.hex
return token unless User.exists?({token: token})
end
end 別のアルゴリズムを使用して、たとえばユーザー名のMD5ハッシュとソルトに基づいてトークンを生成できます。
ユーザー登録
もちろん、ユーザーがトークンを取得できないため、ユーザーが登録できるようにする必要もあります。アプリケーションにHTMLビューを導入したくないので、代わりに新しいAPIメソッドを追加しましょう:
controllers / api / v1 / users_controller.rb
def create
@user = User.new(user_params)
if @user.save
render status: :created
else
render json: @user.errors, status: :unprocessable_entity
end
end
private
def user_params
params.require(:user).permit(:name)
end
開発者が何が起こっているのかを正確に理解できるように、意味のあるHTTPステータスコードを返すことをお勧めします。これで、ユーザーに新しいシリアライザーを提供するか、 .json.jbuilderを使用することができます。 ファイル。私は後者のバリアントを好みます(そのため、:jsonを渡しません renderのオプション 方法)、しかしあなたはそれらのどれでも自由に選ぶことができます。ただし、トークンはあってはならないことに注意してください。 たとえば、すべてのユーザーのリストを返す場合は、常にシリアル化されます。安全に保管する必要があります。
views / api / v1 / users / create.json.jbuilder
json.id @user.id json.name @user.name json.token @user.token
次のステップは、すべてが正しく機能しているかどうかをテストすることです。 curlを使用することもできます コマンドを実行するか、Rubyコードを記述します。この記事はRubyに関するものなので、コーディングオプションを使用します。
ユーザー登録のテスト
HTTPリクエストを実行するには、Faraday gemを使用します。これは、多くのアダプターに共通のインターフェースを提供します(デフォルトはNet::HTTPです)。 )。別のRubyファイルを作成し、Faradayを含めて、クライアントをセットアップします。
api_client.rb
require 'faraday'
client = Faraday.new(url: 'https://localhost:3000') do |config|
config.adapter Faraday.default_adapter
end
response = client.post do |req|
req.url '/api/v1/users'
req.headers['Content-Type'] = 'application/json'
req.body = '{ "user": {"name": "test user"} }'
end
これらのオプションはすべて自明です。デフォルトのアダプターを選択し、リクエストURLをhttp:// localhost:300 / api / v1 / usersに設定し、コンテンツタイプをapplication/jsonに変更します。 、リクエストの本文を提供してください。
サーバーの応答にはJSONが含まれるため、解析するにはOjgemを使用します。
api_client.rb
require 'oj' # client here... puts Oj.load(response.body) puts response.status
解析された応答とは別に、デバッグ目的でステータスコードも表示します。
これで、次のスクリプトを実行できます:
ruby api_client.rb
受け取ったトークンをどこかに保存します。次のセクションで使用します。
トークンによる認証
トークン認証を実施するには、authenticate_or_request_with_http_token メソッドを使用できます。これはActionController::HttpAuthentication ::Token ::ControllerMethodsモジュールの一部であるため、忘れずに含めてください:
controllers / api / v1 / posts_controller.rb
class PostsController < ApplicationController
include ActionController::HttpAuthentication::Token::ControllerMethods
# ...
end
新しいbefore_actionを追加します および対応する方法:
controllers / api / v1 / posts_controller.rb
before_action :authenticate, only: [:create, :destroy]
# ...
private
# ...
def authenticate
authenticate_or_request_with_http_token do |token, options|
@user = User.find_by(token: token)
end
end これで、トークンが設定されていない場合、またはそのようなトークンを持つユーザーが見つからない場合、401エラーが返され、アクションの実行が停止します。
クライアントとサーバー間の通信はHTTPSを介して行われる必要があることに注意してください。そうしないと、トークンが簡単にスプーフィングされる可能性があります。もちろん、提供されているソリューションは理想的ではなく、多くの場合、認証にOAuth2プロトコルを使用することをお勧めします。この機能をサポートするプロセスを大幅に簡素化する少なくとも2つのgemがあります。DoorkeeperとoPROです。
投稿の作成
認証の動作を確認するには、createを追加します PostsControllerへのアクション :
controllers / api / v1 / posts_controller.rb
def create
@post = @user.posts.new(post_params)
if @post.save
render json: @post, status: :created
else
render json: @post.errors, status: :unprocessable_entity
end
end
ここではシリアライザーを利用して、適切なJSONを表示します。 @user before_action内にすでに設定されています 。
次に、この単純なコードを使用してすべてをテストします。
api_client.rb
client = Faraday.new(url: 'https://localhost:3000') do |config|
config.adapter Faraday.default_adapter
config.token_auth('127a74dbec6f156401b236d6cb32db0d')
end
response = client.post do |req|
req.url '/api/v1/posts'
req.headers['Content-Type'] = 'application/json'
req.body = '{ "post": {"title": "Title", "body": "Text"} }'
end
token_authに渡された引数を置き換えます 登録時に受け取ったトークンを使用して、スクリプトを実行します。
ruby api_client.rb
投稿の削除
投稿の削除も同様に行われます。 destroyを追加します アクション:
controllers / api / v1 / posts_controller.rb
def destroy
@post = @user.posts.find_by(params[:id])
if @post
@post.destroy
else
render json: {post: "not found"}, status: :not_found
end
end ユーザーが実際に所有している投稿のみを破棄することを許可します。投稿が正常に削除されると、204ステータスコード(コンテンツなし)が返されます。または、メモリから引き続き利用できるため、削除された投稿のIDで返信することもできます。
この新機能をテストするためのコードは次のとおりです。
api_client.rb
response = client.delete do |req| req.url '/api/v1/posts/6' req.headers['Content-Type'] = 'application/json' end
投稿のIDを自分に合った番号に置き換えてください。
CORSの設定
他のWebサービスが(クライアント側から)APIにアクセスできるようにする場合は、CORS(クロスオリジンリソースシェアリング)を適切に設定する必要があります。基本的に、CORSを使用すると、WebアプリケーションがAJAXリクエストをサードパーティのサービスに送信できます。幸いなことに、すべてを簡単にセットアップできるラックコアと呼ばれる宝石があります。 Gemfileに追加します :
ジェムファイル
gem 'rack-cors'
インストールする:
bundle install
次に、 config / initializers / cors.rb内に構成を提供します ファイル。実際、このファイルはすでに作成されており、使用例が含まれています。宝石のページにもかなり詳細なドキュメントがあります。
たとえば、次の構成では、誰でも任意の方法を使用してAPIにアクセスできます。
config / initializers / cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '/api/*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end 不正使用の防止
このガイドで最後に言及するのは、APIを悪用やサービス拒否攻撃から保護する方法です。ラックアタック(Kickstarterのユーザーによって作成された)と呼ばれる優れた宝石があり、クライアントをブラックリストまたはホワイトリストに登録したり、リクエストによるサーバーのフラッディングを防止したりできます。
宝石をGemfileにドロップします :
ジェムファイル
gem 'rack-attack'
インストールする:
bundle install
次に、 rack_attack.rb内に構成を提供します イニシャライザファイル。 gemのドキュメントには、利用可能なすべてのオプションがリストされており、いくつかのユースケースが提案されています。これは、あなた以外の人がサービスにアクセスすることを制限し、リクエストの最大数を1秒あたり5つに制限するサンプル構成です。
config / initializers / rack_attack.rb
class Rack::Attack
safelist('allow from localhost') do |req|
# Requests are allowed if the return value is truthy
'127.0.0.1' == req.ip || '::1' == req.ip
end
throttle('req/ip', :limit => 5, :period => 1.second) do |req|
req.ip
end
end 実行する必要があるもう1つのことは、ミドルウェアとしてRackAttackを含めることです。
config / application.rb
config.middleware.use Rack::Attack
結論
この記事は終わりです。うまくいけば、これまでにRailsを使用したAPIの作成に自信が持てるようになりました。利用できるオプションはこれだけではないことに注意してください。かなり前から存在していたもう1つの一般的なソリューションは、Grapeフレームワークであるため、これもチェックしてみてください。
不明な点がある場合は、遠慮なく質問を投稿してください。私と一緒にいてくれてありがとう、そして幸せなコーディング!
-
Rails5でのAngularの使用
あなたは前にその話を聞いたことがあります。分散型で完全に機能するバックエンドAPIと、通常のツールセットで作成されたフロントエンドで実行されているアプリケーションがすでにあります。 次に、Angularに移動します。または、AngularをRailsプロジェクトと統合する方法を探しているだけかもしれません。これは、この方法を好むためです。私たちはあなたを責めません。 このようなアプローチを使用すると、両方の世界を活用して、たとえばRailsとAngularのどちらの機能を使用してフォーマットするかを決定できます。 構築するもの 心配する必要はありません。このチュートリアルは、この目的のた
-
パスワードを使用してLinuxユーザーを作成する
Linuxを作成する必要がある場合があります バッチモード(完全自動)のユーザーアカウントですが、多くの場合、初心者はパスワードの設定方法を尋ねます。 手動で入力せずに新しいユーザーの場合。天国のコマンドに感謝しますuseradd 入力パラメータとしてパスワードを取得できますが、暗号化する必要があります。 つまり、パスワードを使用してLinuxユーザーアカウントを作成するには、次のコマンドが役立ちます。 useradd -m -pencryptedPass ユーザー名 パスワードを暗号化する方法を少なくとも2つ知っています。 1つ目は、 perlを使用することです。 cry