Help us understand the problem. What is going on with this article?

「Webを支える技術」読書録 第2部 URI

続いて第2部です。
他の記事はこちら↓
1部 Web概論:https://qiita.com/Ohtak/items/056a8d594216c8e9f3cd

本に書いてあること以外で自分で調べて考えたりしてるところには「追記」と書いてます。

第4章 URIの仕様

4.1 URIの重要性

URI = Uniform Resource Identifier = 統一リソース識別子
これを使うとWeb上のリソースを一意に示すことができ、簡単なアクセスを実現する

4.2 URIの構文

仕様はRFC 3986

簡単なURIの例

http://blog.example.jp/entires/1

このとき、

  • URIスキーム:http
  • ホスト名:blog.example.jp
  • パス:entires/1

URIスキームはそのURIが利用するプロトコルを示すのが一般的。
ホストとパスはURIの一意性を確保するためのもの。

  • ホスト名はDNSで名前解決できるドメイン名かIPアドレス。これはインターネット上で必ず一意になる。
  • パスはそのホストの中でのリソースまでを一意に示すもの。

これらを組み合わせることで、任意のリソースのURIが他のリソースのURIと重複しないようにできる。

複雑なURIの例

http://yohei:pass@blog.example.jp:8000/search?q=test&debug=true#n10

簡単なURI例になかったものは下記

  • ユーザー情報:yohei:pass
  • ポート番号:8000
  • クエリパラメータ:q=test&debug=true
  • URIフラグメント:#n10

ユーザー情報は{ユーザー名}:{パスワード}の形で、このリソースへのアクセスに使用するユーザー情報。
この場合、ホスト名との区切りには@を使用する。
ホスト情報にポート番号が入る場合、区切りに:を使い、{ホスト名}:{ポート番号}の形で記述する。
ポート番号はアクセスに利用するTCPのポート番号で、書かない場合デフォルトのポートが使われる。
(ちなみにHTTPのデフォルト値は80。)
クエリパラメータはクライアントから動的にURIを生成するのに使用し、サーバーにデータを渡すことができる。
URIフラグメントはリソース内部のさらに細かい部分を特定するのに使用する。
HTML文書であれば、この書き方でid=n10の要素を示すことになる。

4.3 絶対URIと相対URI

パスには2種類あり、ホストからのルートから記述するのを絶対URI、あるベースURIを基準にしたパスを相対URIと呼ぶ。
同じホスト内で毎回絶対URIを書くのは冗長なので相対URIを使うのが一般的。
相対URIを書く場合、カレントディレクトリを.、親ディレクトリを..で表現する

ベースURI

相対パスを使う場合起点になるパスを示す必要があり、これをベースURIと呼ぶ。
指定の仕方として代表的なのは下記の2つ。

リソースのURIをベースURIとする方法

あるリソース内に相対URIがあれば、ベースURIをそのリソースURIとして解釈する方法。
直感的でわかりやすいが、そのリソースのURIをクライアント側で保存しておかなければならない問題がある。

ベースURIを明示的に指定する方法

HTMLやXMLには明示的に示すための属性がある。

test.html
<head>
    <title>test</title>
    <!-- このHTML内のベースURIは「http://example.jp」になる。-->
    <base href="http://example.jp" />
</head>
test.xml
<foo xml:base="http://example.jp/foo">
    <!-- ここののベースURIは「http://example.jp/foo」になる。-->
    <bar xml:base="http://example.jp/foo/bar">
        <!-- ここののベースURIは「http://example.jp/foo」になる。-->
    </bar>
</foo>

これを使えばそのリソースを取得した時点でベースURIがわかるため、先の問題を解決できる。

追記

URIに関してはフレームワークに依存する部分があります。
http://human-nature.hatenablog.com/entry/2016/10/08/230816
等のサイトでも議論されてますが、ファイルパスに左右される都合上、環境の変化に耐えうる書き方にするのが重要です。

例えばcakephp2、cakephp3であれば、主なパスはフレームワークで用意されてる定数で表すことができます。
https://book.cakephp.org/2/ja/core-libraries/global-constants-and-functions.html#id3
https://book.cakephp.org/3/ja/core-libraries/global-constants-and-functions.html#id3
これを使えば同じ書き方で環境に応じたパスを取得できます。
jsやcssであればwebroot以下に置くのが一般的なので、

WWW_ROOT . '/js/test.js'
WWW_ROOT . '/css/test.css'

で参照できます。

ただしwebrootが複数あるとか特殊な環境の場合は用意されてる定数だけだと多分参照できません。
(そんな環境が需要あるかどうかは別にして。。多分と書いたのは試したことないからです。どっちかが優先されるのかエラー吐くのかは不明です。)
定数はconfigで定義することも可能なので、APP_DIRと連結させて独自の定数を定義するとかで結構柔軟に対応できます。

これを使わずハードコーディングしてしまうと、環境が変わった時点でnot found吐かれて動かなくなります。

4.4 URIと文字

URIでは下記が使えると定められている。

  • a-zA-Z
  • 0-9
  • -.~:@!$&'()

つまり日本語は入れられないため、入れたい場合は%エンコーディングを行う。
例えばUTF-8の場合、という文字は%E3%81%82に変換されブラウザとサーバーの間でやり取りされる。
どのようにエンコードされるかは文字コードによって異なるため、ブラウザ上のフォーム等で自分で行う場合は整合性に注意が必要。

4.5 URIの長さ制限

仕様上は長さに制限はないが、実装上制限が存在する。
例えばIEはバージョン問わず2,038バイトが上限(追記:2010年時点の話です。)

4.6 さまざまなスキーム

URIスキームの公式な一覧はIANAにあり、2010年時点で70弱が登録されている

4.7 URIの実装で気をつけること

仕様上気をつけるべきなのは

  • 相対URIの解決
  • %エンコーディング

の2点

追記

本が書かれたのが2010年で、古くなってる数字が現在どうなってるか確認しました。

URIの文字数上限

https://mussyu1204.myhome.cx/wordpress/it/?p=693
にまとめられていました。
2020年現在、主流のブラウザであるchromeやFireFox、およびIE11はかなり長い文字列まで許すようですが、サーバーで上限が設けられている場合があるとのことでした。
とはいえ1000文字を突破する時点で使い勝手はよろしくない気がするので、短くする努力の方が大事そうです。
この話は後述する「クールURI」でも出てきます。
ECサイトや情報サイト等、日本語をクエリパラメータに入れる必要がある場合は%エンコードする必要があり長くなりやすいと思うので、上限を気にする場面があるかもしれません。

スキーム

2020/2/24現在のスキーム一覧を下記にて確認したところ、314存在しました。
https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml

http://koseki.hatenablog.com/entry/20100605/URIschemes
のサイトでまとめられていたので見てみるといいかもしれません

(コラム)URIとURLとURN

一貫してURIという名称を使っていたが、これはURLとURNを総称する呼び名。
URLはURIとほぼ同じで言い換えても問題ないが、URNはだいぶ異なりこちらはドメインに依存しないIDをリソースに振る仕組みになっている。
要するにリソースを識別する際に場所(Locator)を使うか名前(Name)を使うかの違い。
URLにリンク切れの可能性があるための解決策だが、現在URLは恒久的になってきているのとサーバー側の努力でリンク切れしないようにする方が一般的なのであまり普及はしてない。

第5章 URIの設計

5.1 クールなURIは変わらない

「いいURI」のことを「クールURI」と呼ぶ。
発祥はWebの発明者であるTim Bermers-Leeが作った「Cool URIs don't change」というWebページ。
Webがリンクを辿るハイパーメディアである以上リンク切れの問題は深刻で、できるだけURIを変えないようにする努力が必要。

5.2 URIを変わりにくくするためには

URIが変わる要因として考えられるのはドメインの変更やサービスの終了などなど。
どうしても発生するときはするので「永久に変わらないURI」は無理だが、「変わりにくいURI」は設計でなんとかできる。

プログラミング言語に依存する拡張子やパスを含めない

http://example.jp/cgi-bin/login.pl

というURIを考える。
cgi-binというパスはCGIでしか使われず、.plという拡張子もPerl特有のもの。
こういったURIは言語を変更した時点で変わってしまうので避けた方が良い。

メソッド名やセッションIDを含めない

http://example.jp/Login.do?action=showPage

Strutsという古いフレームワークでみられるURI。
クエリパラメータでアクション名を指定しているが、メソッド名が変わった途端にURIも変わってしまう。

http://example.jp/home.jsp?jsessionid=1111111

このjavaのURIはセッションIDを含んでいるが、これもログインのたびに変わる情報。
こういった変わりやすい情報を含んでしまうとURIがしょっちゅう変更になってしまう。

URIはリソースを表現する名詞にする

URIはリソースの名前であるため、名詞にすべきという作法がある。
例えば、

http://example.jp/sample/people/show/123

というURIであれば、

http://example.jp/sample/people/123

にすべき。

URIの設計指南

まとめると、

  • URIにプログラミング言語依存の拡張子を利用しない
  • 実装依存のパス名を指定しない
  • セッションIDを含めない
  • リソースを表現する名詞にする

となる。
これを念頭において下記のURIを設計し直してみる。

http://example.jp/cgi-bin/login.pl

ひとまず言語依存のcgi-binと.plは排除する。
PJルートにはなく、http://example.jp/foo/loginとなる場合もあるかもしれないが、共通のログインページであればパスは浅い方が良さそう。
ということで

http://example.jp/login

となる。

追記

この章に書いてあることは人の流儀が分かれそうですが、もちろん上記の限りではない場合もあります。
MVCのフレームワークであれば末尾はアクション名になることが多く、アクション名=メソッド名になるので動詞になるのが一般的です。
変更になる可能性を抑えるためにできるだけ短い単語にする方が良さそうではあります。

また、上記の「設計指南」の箇条書きは本に書いてあることのほぼコピペなのですが、「セッションID」と名指しで言っているのは「かなりの頻度で変わる情報」の代表としてなのか、それとも「セッションIDだけは含めるな」なのかは不明です。
(文脈的に前者だとは思いますが。)

5.3 URIのユーザビリティ

変わりにくいURIはシンプルになる傾向があるが、これはユーザビリティ向上にも役立つ
短いURIであれば覚えやすく、また一般にも馴染みがある単語のみで構成されていれば誤認識も防いでくれる。

5.4 URIを変更したいとき

システムの入替等で変更が発生した場合は転送の仕組みを検討する。
apacheであればmod_rewriteを使うことで、古いURIが入力された時に新しい任意のURIに転送させることが可能

5.5 URI設計のテクニック

拡張子で表現を指定する

URIに拡張子をいれない方がいいのは、その拡張子が実装依存である場合。
そうでなければ効果的な場面もある。
例えば言語を指定したい時。

http://example.jp/2010/05/01/press.ja

は日本語のページ、

http://example.jp/2010/05/01/press.en

は英語のページという具合にすれば同じパスで表現のみを変えられる。
あくまでリソースは1つだけなのでこちらの方が良い場合もあり、このテクニックはW3Cでも推奨している。
言語だけでなくhtmlやjsonなどファイル形式を指定したい時も有効。

マトリクスURI

パラメータを;で区切ることで地図など複数の階層構造でないパラメータを持つリソースの指定に有効

http://example.jp/map/lat=35.000000;lns=139.000000

lat=lng=は省略可で、;,でも良いので下記でも同じリソースを示す

http://example.jp/map/35.000000,139.000000

5.6 URIの不透明性

シンプルなURIは可読性が高い代わりにURIの構造を推測しやすくなる。例えば

http://example.jp/2010/05/01/press.ja
http://example.jp/2010/05/01/press.en

というURIがあれば、フランス語は

http://example.jp/2010/05/01/press.fr

と推測できるが、ここにリソースがあるとは限らない。

Webではあくまでリンクを辿って操作をする都合上、URIを勝手に想像して操作したり、クライアント側でURIを構築したりするのは良くない。
URIを推測できないこと、クライアント側で組み立てられないことを「不透明である」と言い、クライアントを作る際は不透明性を確保することを心がける必要がある。

5.7 URIを強く意識する

URIはWebアプリケーションフレームワークが隠蔽することもあるため、意識から外れがちだが次の点で重要。

  • URIはリソースの名前である
  • URIは寿命が長い
  • URIはブラウザがアドレス欄に表示する

2部通しての私見

最後に書かれてる通りURIをあまり意識していなかったのですが、結構耳が痛い内容がありました。

ちょうど先日、仕事でURIの変更をしたくなるときがありました。
原因はURIに一般的でない名称が含まれておりわかりづらかったためで、致命的なバグを生んだとかではないのですが変更作業自体にバグが発生するリスクが伴うので避けたかった事態です。
設計の段階で意識していれば防げたと思います。。

個々人の流儀によって分かれそうな部分はあったものの、「変わりにくいURI」が重要なのは共通だと思うので特に意識していきます。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした