RubyでオレオレWebフレームワークを作っているのですが、だいぶ形になってきたので知識の整理がてら色々まとめてみる✍
今回は第3回目です🤟
👇第2回 GETに対応したRouting機能を作る🛣はこちら https://madogiwa0124.hatenablog.com/entry/2019/09/01/233842
今回のゴール
前回は、GETリクエストに対応したRouting機能を作りましたが、それをクエリ文字列を受け取れるように修正します👩💻
下記のように定義したら/hello?word="hoge"にアクセス時に'Hello hoge.'が表示出来るような機能を作ります。
require_relative './lib/main.rb' router.get '/hello' do |params| "Hello #{params['word']}." end
太字で記載したようにリクエスト時にクエリ文字列を追加で取得し、routeで定義したProcオブジェクトを実行する際に引数として渡します📦
- WEBrickからリクエスト(URL、METHOD、クエリ文字列)取得
appでmainで定義したDSLを使って作成したrouterで管理しているroutingをリクエストのURLとMETHODから探索- 見つかったroutingのProcオブジェクトを返却
applicationでProcオブジェクトの引数にクエリ文字列を渡して実行- 結果からレスポンスを生成
- WEBrickにレスポンスを返却
まずはリクエスト時にクエリ文字列を取得する。
まずはリクエストからURLに設定されたクエリ文字列を取得していきます。
application.rbでそのまま実装してもいいのですが、処理が結構多くなってきたのでRack::Requestを継承した独自クラスを作ってそちらに実装していきます👩💻
👇実装はこんな感じ、envを引数でとってurl、method、queryを設定する処理とクエリ文字列で渡された値を返却するparamsメソッドを追加しています🙋
require 'rack' class MyRequest < Rack::Request def initialize(env) super @url = env['REQUEST_URI'] @method = env['REQUEST_METHOD'] @query = URI.parse(url).query end def params return unless query @params ||= Hash[URI.decode_www_form(query)] end attr_reader :url, :method, :query end
rubyの標準ライブラリのURIを使うとURI.parse(url).queryでクエリ文字列部分が取得出来て、URI.decode_www_formの結果をHashの引数で渡すと簡単にHash形式で取得出来るので便利ですね✨
👇実際にapplication.rbに組み込んだ形がこちら
require 'my_request.rb' require 'rack' class Application def initialize(router:) @router = router end attr_reader :router, :request def call(env) @request = MyRequest.new(env) body = router.bind(url: request.url, method: request.method).process.call ['200', {'Content-Type' => 'text/html'}, [body]] end def run! Rack::Handler::WEBrick.run self end end
RoutingのProcオブジェクトにクエリ文字列を渡す
リクエストに設定されたクエリ文字列を取得出来るようになったので、今度はそれをroutingで定義したProcオブジェクトに渡していきます🙋
これは簡単で単純にcallの引数にrequest.paramsを渡すだけです👍
require 'my_request.rb' require 'rack' class Application def initialize(router:) @request = MyRequest.new(env) @router = router end attr_reader :router, :request def call(env) body = router.bind(url: request.url, method: request.method).process.call(request.params) ['200', {'Content-Type' => 'text/html'}, [body]] end def run! Rack::Handler::WEBrick.run self end end
これで下記のようなroutingを定義してhttp://localhost:8080/hello?word=hogeにアクセスすると、、、
router.get '/hello' do |params| "Hello #{params['word']}" end

無事にクエリ文字列が反映されました🙌
おわりに
今回はクエリ文字列を含んだGETに対応したRouting機能を作りました👩💻
ここまで出来ると文字列のみを返すような静的サイトだったらなんとか実現できるぐらいには出来てきましたね👍
次回はViewのレンダリング処理を実装していこうかなと思います 🙋♀️ レンダリングが出来るとちょっとフレームワークっぽくなってきた感じがしますね✨