CLOVER🍀

That was when it all began.

Node.jsで、JSONをPOSTするHttpClient/受けるHttpServerを書いてみる

Node.jsのお勉強ということで。

HTTPのClient/Serverを書いてみようと思ったのですが、お題としてJSONをPOSTする/受けるClient/Serverを書いて
みましょうかと。

環境。

$ node -v
v9.4.0

使うモジュールは、HTTPのようです。

HTTP | Node.js v9.4.0 Documentation

APIは、このあたりを参考にすればいいみたいですね。

Server側。
http.createServer

Client側。
http.request

では、書いていってみましょう。

Server側

まずは、Server側から。

お題として、演算子と値2つを受け取り、演算して返すようなServerにしてみます。パスはなんでも良い感じで。結果、このように。
server.js

const http = require("http");

const port = 8080;

const logger = fun => console.log(`[${new Date()}] ${fun.call(null)}`);

const server = http.createServer((request, response) => {
    request.setEncoding("utf-8");
    
    request.on("data", chunk => {
        logger(() => `received data[${chunk}]`);

        const data = JSON.parse(chunk);

        const operator = data["operator"];
        const a = data["a"];
        const b = data["b"];

        const responseSender = d => response.end(JSON.stringify(d));

        if (operator === "+") {
            responseSender({ result: a + b});
        } else if (operator === "-") {
            responseSender({ result: a - b});
        } else if (operator === "*") {
            responseSender({ result: a * b});
        } else if (operator === "/") {
            responseSender({ result: a / b});
        } else {
            logger(() => "Bad Request");
            response.statusCode = 400;
            responseSender({ message: `Unknown Operator[${operator}]` });
        }
    });
});

server.on("request", (request, response) => {
    const socket = request.socket;
    logger(() => `client connected[${socket.remoteAddress}:${socket.remotePort}] URL[${request.url} ${request.httpVersion}] Method[${request.method}]`);
});

server.listen(port);

logger(() => "Server startup");

/*
server.close();
logger(() => "Server shutdown");
*/

HTTPモジュールをロードして

const http = require("http");

http.createServerでサーバーで行う処理を書きます、と。

const server = http.createServer((request, response) => {
    // ここに処理を書く
});

ここで受け取るリクエストはhttp.IncomingMessage、レスポンスはhttp.ServerResponseのようです。

http.IncomingMessage

http.ServerResponse

http.IncomingMessage#setEncodingしておくと、Chunkを文字列として受け取ることができます。

    request.setEncoding("utf-8");
    
    request.on("data", chunk => {
        logger(() => `received data[${chunk}]`);

        const data = JSON.parse(chunk);

        const operator = data["operator"];
        const a = data["a"];
        const b = data["b"];

        const responseSender = d => response.end(JSON.stringify(d));

        if (operator === "+") {
            responseSender({ result: a + b});
        } else if (operator === "-") {
            responseSender({ result: a - b});
        } else if (operator === "*") {
            responseSender({ result: a * b});
        } else if (operator === "/") {
            responseSender({ result: a / b});
        } else {
            logger(() => "Bad Request");
            response.statusCode = 400;
            responseSender({ message: `Unknown Operator[${operator}]` });
        }
    });

今回は、JSONでパースして演算した結果を返しています。レスポンスは、http.ServerResponse#endで送り返せばよい、と。

        const responseSender = d => response.end(JSON.stringify(d));

ステータスコードなども設定することができます。

            response.statusCode = 400;

http.createServerで作成したServerにイベントを紐づけることもできるようで、今回は「request」イベントに対してログ出力するようにしてみました。
Event: 'request'

server.on("request", (request, response) => {
    const socket = request.socket;
    logger(() => `client connected[${socket.remoteAddress}:${socket.remotePort}] URL[${request.url} ${request.httpVersion}] Method[${request.method}]`);
});

あとは、ポートを指定してリッスンすれば起動です。

server.listen(port);

logger(() => "Server startup");

停止はcloseですが、今回はコメントアウト。Ctrl-Cで止めることにします。

/*
server.close();
logger(() => "Server shutdown");
*/

起動。

$ node server.js 
[Thu Feb 01 2018 23:54:54 GMT+0900 (JST)] Server startup

確認。

$ curl -XPOST http://localhost:8080/calc -d '{ "operator": "+", "a": 8, "b": 3 }'
{"result":11}

$ curl -XPOST http://localhost:8080/calc -d '{ "operator": "*", "a": 8, "b": 3 }'
{"result":24}

$ curl -XPOST http://localhost:8080/calc -d '{ "operator": "?", "a": 8, "b": 3 }'
{"message":"Unknown Operator[?]"}

OKそうです。

Server側のログ。

[Thu Feb 01 2018 23:55:36 GMT+0900 (JST)] client connected[::ffff:127.0.0.1:56404] URL[/calc 1.1] Method[POST]
[Thu Feb 01 2018 23:55:36 GMT+0900 (JST)] received data[{ "operator": "+", "a": 8, "b": 3 }]
[Thu Feb 01 2018 23:55:50 GMT+0900 (JST)] client connected[::ffff:127.0.0.1:56406] URL[/calc 1.1] Method[POST]
[Thu Feb 01 2018 23:55:50 GMT+0900 (JST)] received data[{ "operator": "*", "a": 8, "b": 3 }]
[Thu Feb 01 2018 23:55:58 GMT+0900 (JST)] client connected[::ffff:127.0.0.1:56408] URL[/calc 1.1] Method[POST]
[Thu Feb 01 2018 23:55:58 GMT+0900 (JST)] received data[{ "operator": "?", "a": 8, "b": 3 }]
[Thu Feb 01 2018 23:55:58 GMT+0900 (JST)] Bad Request

Client側

続いて、Client側。

Client側は、こんな感じのコードにしてみました。
client.js

const http = require("http");

const host = "localhost";
const port = 8080;

const logger = fun => console.log(`[${new Date()}] ${fun.call(null)}`);

const options = {
    host: host,
    port: port,
    method: "POST",
    path: "/calc",
    headers: {
        "Content-Type": "application/json"
    }
};

const addReq = http.request(options, res => {
    res.setEncoding("utf-8");
    res.on("data",  chunk =>  logger(() => `add result => ${chunk}`));
});

addReq.write(JSON.stringify({ operator: "+", a: 8, b: 3 }));
addReq.end();

const minusReq = http.request(options, res => {
    res.setEncoding("utf-8");
    res.on("data", chunk => logger(() => `minus result => ${chunk}`));
});

minusReq.write(JSON.stringify({ operator: "-", a: 8, b: 5}));
minusReq.end();

const multiplyReq = http.request(options, res => {
    res.setEncoding("utf-8");
    res.on("data",  chunk =>  logger(() => `multiply result => ${chunk}`));
});

multiplyReq.write(JSON.stringify({ operator: "*", a: 8, b: 3}));
multiplyReq.end();

const divideReq = http.request(options, res => {
    res.setEncoding("utf-8");
    res.on("data", chunk => logger(() => `divide result => ${chunk}`));
});

divideReq.write(JSON.stringify({ operator: "/", a: 8, b: 3}));
divideReq.end();

const badReq = http.request(options, res => {
    res.setEncoding("utf-8");
    res.on("data", chunk => logger(() => `bad result => ${chunk}`));
});

badReq.write(JSON.stringify({ operator: "?", a: 8, b: 3}));
badReq.end();

Serverと同様に、使うのはHTTPモジュール。

const http = require("http");

http.requestでレスポンスを受け取った時の処理を書くようですが、その第1引数にはホストやポート、HTTPメソッドなどの情報を設定します。

const host = "localhost";
const port = 8080;

const options = {
    host: host,
    port: port,
    method: "POST",
    path: "/calc",
    headers: {
        "Content-Type": "application/json"
    }
};

const addReq = http.request(options, res => {
    res.setEncoding("utf-8");
    res.on("data",  chunk =>  logger(() => `add result => ${chunk}`));
});

addReq.write(JSON.stringify({ operator: "+", a: 8, b: 3 }));
addReq.end();

http.requestのコールバックで受けとるのは、http.IncomingMessageのようです。
http.IncomingMessage

    res.setEncoding("utf-8");
    res.on("data",  chunk =>  logger(() => `add result => ${chunk}`));

http.IncomingMessage#setEncodingを使うことで、こちらも文字列としてデータを受け取ることができます。

データの送信は、http.requestの戻り値、http.ClientRequestに対して行います。
http.ClientRequest

writeでデータ送信、終わったらendを呼べばよいようです。

const addReq = http.request(options, res => {
  //
});

addReq.write(JSON.stringify({ operator: "+", a: 8, b: 3 }));
addReq.end();

この例は加算のものでしたが、その他の演算子も並べて…

const minusReq = http.request(options, res => {
    res.setEncoding("utf-8");
    res.on("data", chunk => logger(() => `minus result => ${chunk}`));
});

minusReq.write(JSON.stringify({ operator: "-", a: 8, b: 5}));
minusReq.end();

const multiplyReq = http.request(options, res => {
    res.setEncoding("utf-8");
    res.on("data",  chunk =>  logger(() => `multiply result => ${chunk}`));
});

multiplyReq.write(JSON.stringify({ operator: "*", a: 8, b: 3}));
multiplyReq.end();

const divideReq = http.request(options, res => {
    res.setEncoding("utf-8");
    res.on("data", chunk => logger(() => `divide result => ${chunk}`));
});

divideReq.write(JSON.stringify({ operator: "/", a: 8, b: 3}));
divideReq.end();

const badReq = http.request(options, res => {
    res.setEncoding("utf-8");
    res.on("data", chunk => logger(() => `bad result => ${chunk}`));
});

badReq.write(JSON.stringify({ operator: "?", a: 8, b: 3}));
badReq.end();

実行。

$ node client.js 
[Fri Feb 02 2018 00:08:13 GMT+0900 (JST)] add result => {"result":11}
[Fri Feb 02 2018 00:08:13 GMT+0900 (JST)] minus result => {"result":3}
[Fri Feb 02 2018 00:08:13 GMT+0900 (JST)] multiply result => {"result":24}
[Fri Feb 02 2018 00:08:13 GMT+0900 (JST)] divide result => {"result":2.6666666666666665}
[Fri Feb 02 2018 00:08:13 GMT+0900 (JST)] bad result => {"message":"Unknown Operator[?]"}

こちらもOKそうですね。

とりあえず、簡単なHttpClient/Serverを書いてみることができましたね。