Saba note

醜悪コード Ugly hacks ITものづくり

MVCとルーティング

On RailsであれLaravelであれ、ここらの仕組みはだいたい同じです。WEBというものはそもそもそうだったのだ!と思い出させてれるのがこのMVCとルーティングを使った仕組みです。
WEBの勉強をはじめた場合、ここらがすっ飛ばされていきなりHTMLコーディングなどをはじめてしまうためこれらの根本的な仕組みが浸透せず一部のエンジニアだけが知るということになっています。
しかしより複雑なことを簡単に表現するにはこれらの原始的な仕組みを利用した方がよく、且つこれらの仕組みはさほど難しいことではありません。

誤解1: URLとフォルダー階層

最初に言ってしまうと、WEBの世界にURLとフォルダー階層は「ない」と思ってください。これが最大の誤解です。こんなものは最終的に尾ひれがついて出来上がったまがい物であり一部の利便性に特化した特別なしくみです。index.htmlを作ってそれぞれのページは/foo/index.htmlとか/bar/index.htmlとかやっているのは非常に小さい世界であり、これはWEBの仕組みではありません。これはWEBの仕組みが作り上げたほんの一部の機能です。
WEBページのしくみは

  • リクエスト
  • レスポンス

という非常に明快且つシンプルなもので、いずれも文字列でリクエストし文字列でレスポンスを返すというのがその基礎になっています。

リクエストとレスポンス

リクエストはどんなものかというと、WEBサーバーに向けられた文字列のリクエスト(要求)になります。つまりズバリそのまま要求する内容と要求した本人自身の内容がかかれています。Request URLはWEBサーバーに何を要求するのかという具体的な文字列になります。User-Agentはアクセス元の自分自身の情報です。
ざっと読んで(意味がわからなくても)どんなものなのか読んでみてください。

Request URL:http://httpbasic.local/baz
Request Method:GET
Status Code:200 OK
Remote Address:127.0.0.1:80
Referrer Policy:no-referrer-when-downgrade
Response Headers
view source
Connection:Keep-Alive
Content-Length:60
Content-Type:text/html; charset=UTF-8
Date:Wed, 31 Jan 2018 14:19:47 GMT
Keep-Alive:timeout=5, max=100
Server:Apache/2.4.29 (Win32) OpenSSL/1.0.2n PHP/7.1.12
X-Powered-By:PHP/7.1.12
Request Headers
view source
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding:gzip, deflate
Accept-Language:ja,en-US;q=0.9,en;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Host:httpbasic.local
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36

かなり端折りますが、ここでの説明に最も適した情報が以下です。

Request URL:http://httpbasic.local/baz
Request Method:GET
Content-Type:text/html; charset=UTF-8

つまりブラウザー(クライアント)がWEBサーバーに対して、

  • httpbasic.localという場所に
  • http://というプロトコルでもって、
  • GETメソッド(送信方法)でもって、
  • /bazという文字列を送っておくので、
  • textかhtmlのUTF-8の文字コードで、
  • レスポンスしてね。

ということになります。他にもいろいろとごちゃごちゃ要求をしますが初心者はこれで全部だと思ってよいかと思います。
これらの単純明快なリクエスト(要求)に対してWEBサーバーは単純な文字列でレスポンス(応答)するというわけです。
(ここでは画像やバイナリデータのレスポンスは自体を複雑化してしまうので今回は省略)

レスポンス

リクエストの結果、WEBサーバーが返す文字列(データ)がレスポンスになるわけですが、このレスポンスがブラウザーに表示されているものになります。一部のバイナリファイルを除いて基本はすべて文字列で返します。先程のリクエスト「Content-Type:text/html; charset=UTF-8」でもって文字列を返す約束です。
ここで注意したいのは「誤解1: URLとフォルダー階層」で説明したように、

URL
フォルダー階層

URI
レスポンス

ということです。
ここでちょっと概念的なことについて説明します。URLはロケーション、場所を指す言葉です。これはURLがあるファイルの場所を指定している…というように感じられます。なので、プログラマーは可能な限りURIという言い方をします。何かしらのIdentifyをしているとみなすわけです。これは、

http://localhost/foo.html
http://localhost/foo/bar
http://localhost/foo?id=xxxx&mode=zz&cat=yy
http://localhost/33A9F48629E2A43E2AB04769A7AF2E05F388CF79
http://localhost/foo#bar
http://localhost/foo.php?id=xxxx&mode=zz&cat=yy

という文字列は場所(フォルダー階層を連想させるようなもの)ではなくユニークなUniform Resource IdentifierなのでURLでもURNでもなく単なる指定子というか記号ということなのです。WEBサーバーはこの文字列によって好き勝手に反応(レスポンス)することができます。信じられないかもしれませんが、http://localhost/foo/barとアクセスして、/baz/quxにあるファイルの内容をレスポンスすることができます。ほとんど何でもできると言っても過言ではないのでDocumentRootよりも上位にあるファイルの情報をレスポンスすることもできます。(DocumentRoot以下のファイルが表示されるというのはWebサーバーの機能の一部であってそれがすべてではありません。)
スラッシュの区切りはあくまでも便宜的なものであってフォルダー階層を示しているわけではないのです。それはWebサーバーのデフォルトがスラッシュをフォルダー階層であると判断し認識しているというだけです。リクエストは単なるシンプルな文字列でありIdentifierです。
MVCとルーティングで制御するフレームワークがindex.phpまたはindex.rbなどのたった1つのファイルにすべてのリクエストを集めて、そののちにそのIdentifierを処理しているというのは、このリクエストとレスポンスというシンプルな構造を利用しているということなんです。

フレームワークを極限までミニマルにする

フレムワークをむちゃくちゃシンプルにするとこんな感じになります。httpbasic
mod_rewriteが使えるようにしておくのと、PHPを動かせるようにしてあればOKです。
あくまでも極限まで簡素化したらこういうイメージというだけで実際のフレームワークはもっとちゃんとしたことをやってます。

.htaccess

Laravelの.htaccessを借ります。

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews
    </IfModule>

    RewriteEngine On

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} (.+)/$
    RewriteRule ^ %1 [L,R=301]

    # Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]

    # Handle Authorization Header
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
</IfModule>
index.php
<?php
if ($_SERVER["REQUEST_URI"] == "/foo") {
    echo "foo is a surrogate character useful for programmers.";
} elseif ($_SERVER["REQUEST_URI"] == "/bar") {
    echo "There is no Japanese applicable to the translation of bar.";
} elseif ($_SERVER["REQUEST_URI"] == "/baz") {
    echo "The second metasyntactic variable, after foo and before baz.";
} else {
    echo "foo bar baz qux quux corge grault garply waldo fred plugh xyzzy thud";
}

/**
/foo, /bar, /bazでアクセスをすると所定の文字列(レスポンス)を返します。
リクエストURIを受けて、どんな処理をするのか決定します。
if文自身がルーティングと呼ばれている作業です。
if文の中身がMVCの最も単純化された形です。
    C: echoがコントローラ
    V: ""(ダブルクォートが)view
    M: 文字列がモデル
という感じになります。
この動作をClassを使ってより精巧にしたものが最もポピュラーなFrameworkの動作になります。
コントローラは与えられた情報の処理を担います。モデルは与えられた情報そのものです。ビューはそれらの加工したデータを最終的に出力する役目を果たします。
*/

/fooにアクセスしても/barにアクセスしてもそんなファイルもないし、そこにはディレクトリすらありません。リクエストによってレスポンスしているだけなのです。

Model Controller View & Routing

現実的にはこれらの言葉は実に抽象的でその文脈によっていろいろなものを指しますが、これらの抽象的な構造というのが実にWEBアプリケーションを精巧に組み立てることに役立っています。ですので、

Model = Database
Controller = Class
View = Template

みたいな覚え方をすると高度な開発やデバックをする際には役に立ちません。(普通の開発には多少役に立ちます。)Laravelにでさえ、Controller, Middleware, Facadeといろいろなクラスがあり粒度が違うのです。実際にmodelはClassから取得したsetter()やgetter()といったものもModelとして扱ったほうが自然です。エンティティのオブジェクトはどうでしょう。これもたぶんModelの一部でしょう。DBから読み出した値をオブジェクトに保持しているのですから。

「ブラウザーでアクセスして見る」はWEB機能のほんの一部

WEBといったらブラウザーでアクセスしてというのがちょっと分かった気になった人の大きな勘違いです。WEBは正直リクエストとレスポンスしかないゆえにとてつもない可能性があります。つまりhttpのプロトコルに従えばどんな機器でもWEBという仕組みを利用することができるのです。
ちょっと考えればわかりますが、将来自動車にはその走行履歴、スピード、距離、消費したガソリンや電気など様々な情報がクラウドサーバーに蓄積される時代がきます。そのデータのフィードバックで更に燃費のよいエンジンの開発が進められ、更に運転者の癖などの更生、自動車利用の用途などレコメンドなど様々な情報をやりとりするようになります。その際に自動車にブラウザーを仕込むわけでありません。機器がhttpのプロトコルを利用してデータを送信するだけです。またレスポンスから自動車の制御コンピューターに情報を送るだけです。
いわゆるAPIは内部的に通信するインターフェイスを提供するものであるし、最近はhttpを利用してFTPのような機能を利用できるようになりました。アスタリスクなど電話とWEBが情報のやりとりをできるようにしたものもあります。おそらくリクエストとレスポンスという仕組みは今度更に利用頻度が多くなります。
逆にいうとこれまで淘汰されていった別の通信プロトコルがたくさんあるということもいえます。私の私見ではFTPは間もなく消滅するだろうと思います。10年後にはFTPはもう名前すら残ってないかもしれません。httpもなくなります。httpsが標準で暗号化のない通信は皆無になります。


RFC

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

This site uses Akismet to reduce spam. Learn how your comment data is processed.