第5回「Node.js入門」フォームのGET/POST送信 node.js

こんにちは。今日でNode.jsを始めてから1週間!!引き続き、やって行こうと思います^^

 

 梅雨の中の晴れ間っていいですよね。

雨が降って気がめ入っている時に、青空が広がる日があると、思わず散歩へ出て行きます!

気分も晴ればれで気持ちいいです。

↑の木、秋になると

枯葉が落ちて、「枯葉吹雪?」を見せてくれます。

冬になると、葉が落ち、少し寂しげです・・・

 

 

春になると

 なんとっ!!サクラの木でしたっ!?!?💦

 

自分でも誰かの役に立てているだろうか。と奮闘するマンです。

31歳 梅雨

 

 

始めに

今回、ブログを執筆させて頂くうえで、とても参考になるサイトがありました。

Node.jsのスクリプトの基本を覚えよう (libro様)

おかげさまで、より深く理解でき、何も問題なくプログラムを動かせることができました。
感謝いたします。

※すごく丁寧で分かりやすいサイトですので、良かったら見てみてください。
※本サイトは、著者の自分が解りやすくまとめ、オリジナルをアレンジしたものであり、パクリにならないように気を付けます。

 

目次

・「GET/POST送信」とは

・複数ページのルーティングの考え方

・「url」オブジェクトによるURL処理

・フォームのPOST送信について

・POST送信されたデータの処理

・まとめ

・今回の記事を書くにあたって参考にしたサイト

 


・「GET/POST送信」とは

 一言で言いますと、「ページ移動する際に、データを渡し受け取る仕組み」です。

GETは、URLに付記されますが、POSTは、URLに表示されません

(POSTはクッキーに一時的に保存されるようです。)

GET/POST送信とは

 例えば、ユーザー新規登録する際に、メールアドレスとパスワード等を入力した後、登録ボタンを押した経験はありますよね。

「登録ボタン」を押したら、データを次のページへ渡し、受け取っているのです。(その時にデータベースへ登録処理が行われたりします)

 

もっと複雑な処理もありますが、概要はこのくらい知っておけば、十分です。

 

次から「Liblo」様の記事を参考に、詳しくやって行きます。


・複数ページのルーティングの考え方

引用元

 Node.jsで簡単なページが表示できるようになった、では次に何を覚えればいいんだろう?とここで考えてしまう人はけっこう多いようです。1ページだけの表示ができても、実用にはなりません。通常のWebではもっとさまざまなリクエストに応える処理をしなければいけません。

 まずは、「複数のページ」から考えていきましょう。普通、Webというのは複数のページがあるものです。Node.jsで複数ページを表示させるにはどうすればいいんでしょう?

 ここでは、前回使ったEJSというテンプレートエンジンを使って考えることにしましょう(使わなくても考え方は同じですが……)。基本的な考え方はとてもシンプルです。複数のページを利用する場合、まずそれらのページをあらかじめ読み込んでおき、リクエストに応じてどのページをレンダリングして表示するかを決めればいいわけです。

 前前回に行ったEJSを使うのですね。

確かに、非同期でサーバーを立ち上げる前に、変数にHTMLを代入していました。

それと同じ考えで、複数のHTMLソースを、それぞれの変数に代入するという事です。

(Webアプリケーションなので、やっぱり複数ページ作りたいですよね)

 

引用元

 では、「どのページを表示するか」をクライアントからどうやって伝えるのか? 普通のWebサイトでは、URLによってそれは伝えられます。http://○○/indexならindexのページを、http://○○/heloならheloページを……というような具合ですね。

 通常のWebサーバーでは、自動的に対応するHTMLページを読み込み送り返しますが、Node.jsの場合、プログラムの中でそうした処理を考えてやらなければいけません。送られてきたURLからドメインより後の部分を取り出し、その値によって表示するページを変える、といった処理を考えればよいわけですね。

 なるほど~。

自分はPHPとJAVAを中心にやってきました。この考え方は初めてです💦💦

form タグの action属性に、移動するHTMLファイル。そして metahod属性に GET か POST と記入するだけでした。

↓こんな感じ

<form action="./singup.html" method="POST">
    <input type="mail">
    <input type="pass">
    <input type="submit">
</form>

PHPなら、各HTMLファイルごとに、プログラムを書いて処理していたのですが、

なんとNode.jsの場合は、1つのNode.jsファイルで、複数のHTMLファイルの処理を行えるのですね^^

これはスゴです✨✨

 

引用元

 では、実際にやってみましょう。まず、下準備として、表示に使用するEJSのテンプレートファイルを用意しておきましょう。ここではページ全体のレイアウトとなる「template.ejs」(前回まで、hello.ejsというファイル名で使ってたやつです)と、実際に表示されるコンテンツとなる「content1.ejs」「content2.ejs」の計3つのテンプレートファイルを用意することにしました。

※template.ejs
 
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta http-equiv="content-type"
        content="text/html; charset=UTF-8">
    <title><%=title %></title>
    <style>
    body { font-size:12pt; color:#006666; }
    h1 { font-size:18pt; background-color:#AAFFFF; }
    pre { background-color:#EEEEEE; }
    </style>
</head>
 
 
<body>
    <header>
        <h1 id="h1"><%=title %></h1>
    </header>
    <div role="main">
        <p><%-content %></p>
    </div>
</body>
 
</html>
 
 
※content1.ejs
 
<h2>※サンプルで作ったコンテンツです。</h2>
<p><%= message %></p>
<hr>
<p><a href="/other">other pageへ</a></p>
 
 
※content2.ejs
 
<p>※別のページのコンテンツです。</p>
<p><%= message %></p>
<p> </p>
<p><a href="/">戻る</a></p>

 これらを使って、2つのページを表示するスクリプトを作成します。

 保存形式は、必ず「UTF-8 (BOM無し)」で保存しましょう。

3つのファイルを作れましたでしょうか。

もしEJSファイルの作り方がわからなければ、一度、前前回に行ったEJSを見直して頂くと分かると思います。

 


・「url」オブジェクトによるURL処理

引用元

 では、用意したテンプレートを使うスクリプトを作成しましょう。これは、先にスクリプトを見ながら説明をしていったほうが早いですね。

 下のリスト欄にNode.jsのスクリプトを掲載しておきます。これを実行して、http://127.0.0.1:1234/ にアクセスしてみてください。content1.ejsの内容が表示されます。このページにあるリンクをクリックすると、http://127.0.0.1:1234/otherにジャンプし、content2.ejsの内容を表示します。

var http = require('http');
var fs = require('fs');
var ejs = require('ejs');
var url = require('url');
 
var template = fs.readFileSync('./template.ejs', 'utf8');
var content1 = fs.readFileSync('./content1.ejs', 'utf8');
var content2 = fs.readFileSync('./content2.ejs', 'utf8');
 
var routes = {
    "/":{
        "title":"Main Page",
        "message":"これはサンプルのページですよ。",
        "content":content1},
    "/index":{
        "title":"Main Page",
        "message":"これはサンプルのページですよ。",
        "content":content1},
    "/other":{
        "title":"Other Page",
        "message":"別のページを表示していますよ。",
        "content":content2}
};
 
var server = http.createServer();
server.on('request', doRequest);
server.listen(1234);
console.log('Server running!');
 
// リクエストの処理
function doRequest(request, response) {
    var url_parts = url.parse(request.url);
    // route check
    if (routes[url_parts.pathname] == null){
        console.log("NOT FOUND PAGE:" + request.url);
        response.writeHead(200, { 'Content-Type': 'text/html' });
        response.end("<html><body><h1>NOT FOUND PAGE:" + 
            request.url + "</h1></body></html>");
        return;
    }
    // page render 
    var content = ejs.render( template,
        {
            title: routes[url_parts.pathname].title,
            content: ejs.render(
                routes[url_parts.pathname].content,
                {
                    message: routes[url_parts.pathname].message
                }
            )
        }
    );
    response.writeHead(200, {'Content-Type': 'text/html'});
    response.write(content);
    response.end();
}

 動きました!!

いやー、本当にLiblo様の記事はスゴイです。(開発者のブログって、自分が解るように書いてるだけのものが多いので、実際、その通り書いても動かないのですが)

Liblo様には足を向けて寝れません。

 

ブラウザで確認すると、

リンクをクリックすると、ページ移動し、GETの内容によって表示される文字が変わります。

うまく動きましたでしょうか。

 

引用元

 では、動作の確認ができたところで、スクリプトの内容を見ながら処理の仕方を説明していきましょう。

 

urlのロード

var url = require('url');

 URLを扱うためには、「url」というオブジェクトをロードしておきます。このurlオブジェクトは、URLの文字列をパースし、そこから必要なものを取り出す機能を提供します

 

URLのパース

var url_parts = url.parse(request.url);

 リクエストがアクセスしてきたURLをパース処理します。リクエストされたURLは、requestイベントハンドラの引数に渡されるrequestオブジェクトの「url」というプロパティで得ることができます。

 urlオブジェクトの「parse」は、URLの文字列分を要素ごとに割しオブジェクト化して返すものです。これで、変数url_partsにURLの要素が保管されます。それぞれの要素は生成されたオブジェクトのプロパティとして保管されており、いつでも利用できるようになります。

 今回は get ですので、URLに付記されます。

例えば、getで渡された値を全て確認したい場合は、下記を付記します。

console.log(url_parts);

すると、コマンドプロンプトでは以下のように出力されます。

pathname: ‘/other’,   となっているのが解ります!

この ‘/other’ は content1.ejs の <a></a> タグに指定している、リンク先と同じものです。

 

引用元

各ページのデータを用意する

 今回のスクリプトでは、それぞれのページのデータをroutesという変数にまとめています。これは、アクセス先のパスをキーとして用意し、そのパスで表示されるページの情報を連想配列でまとめたものを値に設定してあります。例えば、ルートである“/”の値を見てみると、

"/":{
    "title":"Main Page",
    "message":"これはサンプルのページですよ。",
    "content":content1 }

 こんな具合になっていることがわかるでしょう。連想配列には、titlemessagecontentというキーが用意されており、それぞれ「タイトルテキスト」「ページに表示するメッセージテキスト」「表示するページの内容(テンプレートデータ)」を値として保管してあります。この変数routesから、アクセスするアドレスのパスごとに必要な情報を取り出し処理しよう、というわけです。

 3パターン分、作っていますね。

それぞれ表示する、「タイトル」「詳細」「リンク先(ejsファイル名)

 

引用元

パスが得られない場合の処理

if (routes[url_parts.pathname] == null){……}

 変数url_partsにURLの各要素がオブジェクトとして保管されていましたが、この中で「パス」の値は「pathname」というプロパティとして保管されています。

 変数routesには、既に述べたように各パスごとで必要となる情報が、パスをキーとした連想配列にまとめられていました。ということは、routes[url_parts.pathname]の値を取り出せば、現在のリクエストがアクセスしているパスの情報が取り出せることになります。もしこの値がnullならば、変数routesは情報がない、すなわち「そのパスにはアクセスできない」ということになるわけです。

 そこで、nullの場合にはエラーメッセージなどを表示させておきます。これで、用意されていないアドレスへの対応ができました。

 例えば、「 http://localhost:1234/aaaa 」とした場合、「 /aaaa 」というのは準備していないので、そうした場合は、エラーメッセージを表示させるという事になります。

引用元

アクセスしたパスのページをレンダリングする

var content = ejs.render( template,
    {
        title: routes[url_parts.pathname].title,
        content: ejs.render(
            routes[url_parts.pathname].content,
            {
                message: routes[url_parts.pathname].message
            }
        )
    }
);

 後は、変数routesから必要な値を取り出してレンダリングをするだけです。例えば、titleに設定する値は

title: routes[url_parts.pathname].title

 このように用意すればいいですし、コンテンツのレンダリングをcontentに用意するには、

content: ejs.render( 
    routes[url_parts.pathname].content, ……)

 このようにすればよいでしょう。ここで表示するコンテンツのテンプレートはroutes[url_parts.pathname].contentで得られますから、これをrenderするだけです。

 後は、writeHeadwriteendのセットを実行してページ出力の完了です。テンプレートのレンダリング処理があるのでちょっと面倒に見えますが、基本的に「ページで使う値はroutes[url_parts.pathname]の中にまとめられている」ということさえわかっていれば難しいことはありません。

 いやいやいや、何をおっしゃいますやら、

Liblo様にとっては簡単かもしれません。Liblo様の教え方がうまいので、私も理解できています。

 

凄い分かりやすい説明でしたー。順番にやって行くと、どんどん理解できていきます!

そして、これはお決まり

  1. writeHead」 は head 情報
  2. write」 は body 内の情報
  3. end」 で 終了
res
writeHead ヘッダー情報
write html情報
end 完了

 

今回はGETでした。次はPOSTです。


・フォームのPOST送信について

引用元

 続いて、フォームをPOST送信した時の処理について考えてみましょう。既に、アドレスによってページ処理する方法はわかりましたから、フォームを持ったページを用意して、その送信先のアドレスの処理をする、という作業はわかっています。

 最大の問題は、「GETか、POSTか」をどう見分けるかという点。そしてもう1点は「送信されたフォーム情報をどう取得するか」という点でしょう。

 まずは、GETとPOSTの切り分けについてです。これは実は簡単に行えます。requestイベントハンドラで引数に渡されるrequestオブジェクトの「method」を調べるだけで済みます。ハンドラ内で、こんな具合に処理を用意すればよいでしょう。

if (request.method == "GET"){
    ……GETの処理……
}
if (request.method == "POST"){
    ……POSTの処理……
}

残る「POST送信されたフォームデータの取得」は、実際にコードを見たほうがわかりやすいでしょう。――では、これもサンプルを作って説明しましょう。まずは、表示コンテンツのテンプレートからです。今回は、content1.ejsにフォームを設置し、新たに用意するcontent3.ejsで送信されたフォームの表示を行わせることにしましょう。

下にサンプルリストを掲載しておきます。まずはこれらを作成しておいてください。

※content1.ejs
 
<h2>※サンプルで作ったコンテンツです。</h2>
<p><%= message %></p>
<hr>
<form method="post" action="./post">
<table>
    <tr><td>ID:</td><td><input type="text" name="idname"></td></tr>
    <tr><td>PASS:</td><td><input type="password" name="pass"></td></tr>
    <tr><td></td><td><input type="submit"></td></tr>
</table>
</form>
 
 
※content3.ejs
 
<p>※POSTでアクセスされたコンテンツです。</p>
<p>ID: <%= idname %></p>
<p>PASS: <%= pass %></p>
<p><a href="/">戻る</a></p>

 あっ💦これ先述しましたね。

例えば、

<form action="./singup.html" method="POST">
    <input type="mail">
    <input type="pass">
    <input type="submit">
</form><form 

 formタグの、「 method 」属性で、POSTGET を指定するのです。

それが、requestオブジェクトの「method」に入るという事ですね!

 


・POST送信されたデータの処理

「 sampleapp.js 」と名前を付けて保存してください。

 引用元

var http = require('http');
var fs = require('fs');
var ejs = require('ejs');
var url = require('url');
var qs = require('querystring');
 
var template = fs.readFileSync('./template.ejs', 'utf8');
var content1 = fs.readFileSync('./content1.ejs', 'utf8');
var content2 = fs.readFileSync('./content2.ejs', 'utf8');
var content3 = fs.readFileSync('./content3.ejs', 'utf8');
 
var routes = {
    "/":{
        "title":"Main Page",
        "message":"これはサンプルのページですよ。",
        "content":content1},
    "/index":{
        "title":"Main Page",
        "message":"これはサンプルのページですよ。",
        "content":content1},
    "/other":{
        "title":"Other Page",
        "message":"別のページを表示していますよ。",
        "content":content2},
    "/post":{
        "title":"Post Page",
        "content":content3}
};
 
var server = http.createServer();
server.on('request', doRequest);
server.listen(1234);
console.log('Server running!');
 
// リクエストの処理
function doRequest(request, response) {
    var url_parts = url.parse(request.url);
    // route check
    if (routes[url_parts.pathname] == null){
        response.writeHead(200, { 'Content-Type': 'text/html' });
        response.end("<html><body><h1>NOT FOUND PAGE:" + 
            request.url + "</h1></body></html>");
        return;
    }
    // get
    if (request.method == "GET"){
        var content = ejs.render( template,
            {
                title: routes[url_parts.pathname].title,
                content: ejs.render(
                    routes[url_parts.pathname].content,
                    {
                        message: routes[url_parts.pathname].message
                    }
                )
            }
        );
        response.writeHead(200, {'Content-Type': 'text/html'});
        response.write(content);
        response.end();
        return;
    }
    // post
    if (request.method == "POST"){
        if (url_parts.pathname == "/post"){
            var body='';
            request.on('data', function (data) {
                body +=data;
            });
            request.on('end',function(){
                var post =  qs.parse(body);
                var content = ejs.render( template,
                    {
                        title: routes[url_parts.pathname].title,
                        content: ejs.render(
                            routes[url_parts.pathname].content,
                            {
                                idname: post.idname,
                                pass: post.pass
                            }
                        )
                    }
                );
                response.writeHead(200, {'Content-Type': 'text/html'});
                response.write(content);
                response.end();
            });
        } else {
            response.writeHead(200, {'Content-Type': 'text/plain'});
            response.write("NO-POST!!");
            response.end();
        }
    }
}
 
 続いて、Node.jsのスクリプトを作成しましょう。今回は、フォームの送信先でcontent3.ejsを表示するため、“/post”というパスの情報を追加してあります。

 スクリプトを記述したら、実際にNode.jsを起動し、アクセスしてみましょう。http://127.0.0.1:1234/にアクセスすると用意されたフォームが表示されます。ここでIDとPASSに適当に値を記入し送信すると、送信された内容が表示されます。

 どうでしょう。正常に動きましたでしょうか。

私のブラウザでは、完璧に動きました。

http://localhost:1234/post 」のページでは、しっかり情報を受けっていますね。

素晴らしいです!

うまく表示されない人は、サンプルのソースが、ちゃんとコピペできているか確認してみてはいかがでしょうか。

 

引用元

 今回のスクリプトでは、「querystring」というオブジェクトをロードして使っています。冒頭付近にある以下の文ですね。

var qs = require('querystring');

 このquerystringは、クエリー文字列を扱うための機能を提供します。これを利用することで、クエリー文字列から必要な値を適格に取り出せるようになるわけです。――では、スクリプトの内容を見ていきましょう。

 ここでは、request.methodでGET時とPOST時の処理を分けています。GET時の処理は先ほどと同じですね。問題はPOST時の処理です。ここでは、まず「data」というイベントハンドリングを行なっています。 

var body='';
request.on('data', function (data) {
    body +=data;
});

 このdataイベントは、POSTで送信されたデータを受信した際に発生します。イベントハンドラでは、送られてきたデータが引数として渡されます。こうして得られたデータを変数bodyにどんどん追加していくくことで、受信したデータが完成されていきます。

 

 そして、すべての受信処理が完了した後で、POSTデータの処理とページのレンダリングなどを行います。これは「end」というイベントハンドラを用意して実装します。

request.on('end',function(){
    var post =  qs.parse(body);
    ……略……

 endイベントハンドラで最初に行なっているのは、先ほどdataイベントで受け取ったデータをまとめてある変数bodyをパースする処理です。これは、querystringオブジェクトの「parse」というメソッドで行なっています。このメソッドは、引数に渡されたクエリー文字列をパースし、オブジェクトにまとめます。例えば、こんな具合です。

a=abc&x=xyz

 ↓

{ a: "abc", x:"xyz" }

 この際、URLエンコードされた値なども自動的に元の文字列に復号されます。こうして得られた変数postから、必要に応じて値を取り出せばいいわけです。

 

 request の今回使うイベント
data POSTで送信されたデータを受信した際
end すべての受信処理が完了した後

data時にPOSTされた情報を変数へ代入し、 end時にレンダリングするイメージです。

 

バース」という聞きなれない単語が出てきましたね。

おそらく、「文字列を切り分けて、配列に代入する。」というイメージでOKだと思います

今回ですと、「idname=~~&pass=~~」となっている文字列を

{ idname : ~~ , pass : ~~ } という配列に分け、 post という変数に代入しているのですね。

 

引用元

今回はcontent3をレンダリングするとき以下のようにしていますね。

content: ejs.render(
    routes[url_parts.pathname].content,
    {
        idname: post.idname,
        pass: post.pass
    }
)

 送信された値は、post.idnamepost.passで取り出せます。後は、それらをまとめてレンダリングするだけです。

 POST送信は、データの受信がちょっと面倒ですが、それさえクリアできれば後は簡単です。これでだいぶ普通のWebページのようなものが作れるようになってきましたね。(まぁ、Node.jsで普通のWebサーバーのような使い方をするのがよいか?という問題はありますが……)

 これで、今回はおしまいです。お疲れ様でした。

「Liblo」様も書いてますが、Node.js でWebページを作っても・・・と、アリだと思います。

まだ、レンタルサーバーでは、Node.jsを安価で使えるのが難しそうです。

しかし、3~5年後には Node.js が無いとプログラムじゃない!という日が来るのを信じて頑張りましょう!!

 

ではでは、お疲れ様でしたー。

 


・まとめ(飛ばしてもOKです)

 自分は、チャットプログラムを作成したくて、Node.js を触ったのがきっかけです。

皆さんが、Node.js を始めたキッカケは何でしょうか。

日に日に覚えていき、充実した疲れを感じます。

これも、解りやすいサイト「Liblo」様のおかけです。

 

そういえば、一年の予定を管理するのに、自分は「 Your Calender 」と言うサイトを使っています。

https://testtesttest21.sakura.ne.jp/your-calendar/

一目でカレンダーが見れて、しかもスマートフォンにも対応してるので、すごい見やすくてオススメです。

良かったら、使ってみてくださいね。

 


・今回の記事を書くにあたって参考にしたサイト

Node.js公式サイト

初心者のための Node.jsプログラミング入門

 

参考にさせて頂いたサイトの管理者様、今回もありがとうございました。

,,,

Comments

Leave a comment

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