elm/urlで?を含むクエリパラメータを正しくパースする
ElmというAltJSではelm/urlというパッケージを使ってURLをパースし、その内容に応じた状態遷移を行わせるのが一般的です。(多分。筆者はElmにそこまで詳しくないです。)
elm/urlを使ってURLのパース処理を行っているSPAを開発している際に ?
をクエリパラメータに含んだURLを正しくパースできない という現象に若干時間を溶かしたので、起きた事象と解決方法を書いておきます。(微妙にレアケースな気もするので誰かの役に立つかはわからん。)
環境
- Elm 0.19.1
- elm/url 1.0.0
事象
「?
をクエリパラメータに含んだURL」というのは https://example.com/auth?redirect_to=a/b?fuga=piyo
みたいなURLです。
この例ではredirect_to
というクエリパラメータにリダイレクト先のURLを渡したいみたいな想定で、リダイレクト先URLにもクエリパラメータが付与されているため?
が登場するといった感じです。
解決策
parserに食わせるURLのクエリパラメータ部分をURLエンコードし https://example.com/auth?redirect_to=a%2Fb%3Ffuga%3Dpiyo
こんな感じにしておくとうまくいきます。
elm replで実験してみるとこんな感じです。
import Url
import Url.Parser exposing (..)
import Url.Parser.Query as Query
parser =
s "auth" <?> Query.string "redirect_to"
-- URLエンコードしていないURL
url =
{ fragment = Nothing, host = "example.com", path = "/auth", port_ = Nothing, protocol = Url.Https, query = Just "redirect_to=a/b?fuga=piyo" }
-- パースを実行する
parse parser url
上記を実行すると最後のparse
関数の戻り値はJust Nothing : Maybe (Maybe String)
となり、redirect_to
にはなにも入っていないという結果が得られます。
import Url
import Url.Parser exposing (..)
import Url.Parser.Query as Query
parser =
s "auth" <?> Query.string "redirect_to"
-- クエリパラメータ部分をURLエンコードしたURL
-- (この例ではレコードを直接作っていますが、Url.fronStringでも同じ結果が得られます)
url =
{ fragment = Nothing, host = "example.com", path = "/auth", port_ = Nothing, protocol = Url.Https, query = Just "redirect_to=a%2Fb%3Ffuga%3Dpiyo" }
-- パースを実行する
parse parser url
今度はこちらのクエリパラメータをURLエンコードしたものでparse
を実行すると、最後の関数の戻り値はJust (Just "a/b?fuga=piyo") : Maybe (Maybe String)
となり、redirect_to
に入れた値が取れていることに加え、URLエンコードした値もelm/urlの内部でデコードして返してくれていることがわかりました。
おまけ
ちなみにこのようなURLエンコードの処理をElm内でやりたい場合もelm/urlパッケージに含まれる関数で対応できます。
単純なencode/decodeであればUrlモジュールに含まれているpercentEncode
/percentDecode
関数を使ってあげると
import Url exposing (percentEncode, percentDecode)
encodedString = percentEncode "ハローワールド"
-- "%E3%83%8F%E3%83%AD%E3%83%BC%E3%83%AF%E3%83%BC%E3%83%AB%E3%83%89"
percentDecode encodeString
-- Just "ハローワールド" : Maybe String
という感じでイケます。
また、Url.Builderモジュールには今回のようなクエリパラメータをうまく扱うための関数も用意されているようです。
import Url.Builder exposing (string, toQuery)
[string "redirect_to" "a/b?fuga=piyo", string "hoge" "ほげ"] |> toQuery
-- "?redirect_to=a%2Fb%3Ffuga%3Dpiyo&hoge=%E3%81%BB%E3%81%92" : String
という感じでURLエンコード含めて、めちゃくちゃイイ感じにやってくれます。アプリケーション内でURLを組み立てて何かする必要があるときはこちらを使ってやると、何も気にすることなく安全に書けて良い感じなのではないでしょうか。
Discussion