GET /@:id みたいなURLで他との衝突を避ける

Webアプリのルーティングにおいて、各ユーザの個別ページを特殊なものにしたい場合にどういうURLにすると便利かという話に触れる。例えばr7kamuraというIDのユーザ用のページは、Twitterだと https://twitter.com/r7kamura で、GitHubだと https://github.com/r7kamura というURLになっている。

GET /:id だと他のパターンと衝突してしまう

もしユーザページに GET /:id というパターンを採用すると、他のURLのパターンと競合してしまうという問題がある。どんなときに困るかと言うと、例えば https://twitter.com/about でTwitterというサービスについて説明するWebページを提供しようとすると、aboutというIDを持ったユーザ用のページが提供できなくなってしまう。

この問題に対してよく見られる解決方法の一つは、「ユーザページに GET /:id というパターンを採用する」という方針は変えず、ユーザのIDに使えない文字列の一覧を用意しておくというもの。例えば、aboutやhelpなどURLに使う可能性の高い文字列を予め確保しておくことになる。しかし、将来利用する文字列を全て予測しておくことは難しい。新しいURLを追加するたびに、同名のIDのユーザが居ないか調べて一安心、という状況は避けたい。

GET /@:id にしてみる

他のURLと衝突しないようにPrefixを付けておけば良いのだけど、しかしPrefixが付いていないURLがかっこ良いと思っているからこそ困っている。なので「極力Prefix感の薄い文字をPrefixとして付けておく」という方針が、まあそこそこ妥協しつつも折り合いが付けられる範囲で良い。

mediumはそうなっていて、r7kamuraというIDのユーザページは https://medium.com/@r7kamura/ という具合になっている。URLに @ が含まれると最初は抵抗感があるけど、まあ大丈夫大丈夫、慣れてくると気持ちいいしみんなやってる。TwitterやGitHubなど、ユーザIDを @r7kamura のように表記する文化があるサービスであれば、受け入れられやすい可能性が高い。

例えばRailsであれば、以下のように実装できる。GET /@r7kamura に対するリクエストが、UsersControllerクラスのshowメソッドで処理され、params[:id] というパラメータで "r7kamura" という文字列を参照できる。

Rails.application.routes.draw do
  get "/@:id" => "users#show"
end