Skip to content

connect: サンプルの実装 #44

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Sep 12, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"semi": [
2,
"always"
]
],
"no-console": 0
},
"env": {
"es6": true,
Expand Down
3 changes: 1 addition & 2 deletions .md.eslintrc
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
{
"rules": {
"no-undef": 0,
"no-unused-vars": 0,
"no-console": 0
"no-unused-vars": 0
},
"plugins": [
"markdown"
Expand Down
1 change: 1 addition & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
* [Introduction](ja/introduction/README.md)
* [jQuery](ja/jQuery/README.md)
* [ESLint](ja/ESLint/README.md)
* [connect](ja/connect/README.md)

50 changes: 50 additions & 0 deletions ja/connect/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Connect

> この文章はConnect 3.4.0を元に書かれています。

[Connect](https://github.com/senchalabs/connect "Connect")はNode.jsで動くHTTPサーバフレームワークです。
_middleware_という拡張する仕組みを持っていて、connectが持つ機能自体はとても少ないです。

この章ではconnectの_middleware_の仕組みついて見て行きましょう。

## どう書ける?

Connectを使った簡単なEchoサーバを書いてみましょう。
Echoサーバとは、送られてきたリクエストの内容をそのままレスポンスとして返すサーバのことです。

[import, connect-echo-example.js](../../src/connect/connect-echo-example.js)

このEchoサーバに対して、以下のようなリクエストBodyを送信すると、レスポンスとして同じ値が返ってきます。

```json
{
"key": "value"
}
```

`app.use(middleware)` という形で、_middleware_と呼ばれる関数には`request`や`response`といったオブジェクトが渡されます。
そのため、リクエストみてフィルタリングしたり、任意のレスポンスを返したり出来るようになっています。

Echoサーバでは `req.pipe(res);` という形でリクエストをそのままレスポンスとして流す事で実現されています。

### middlewareをモジュールとして実装

もう少し_middleware_をプラグインらしくモジュールとして実装したものを見てみます。

次の[connect-example.js](#connect-example.js)では、あらゆるリクエストに対して、
`"response text"`というレスポンスを`"X-Content-Type-Options"`ヘッダを付けて返すだけのものです。

それぞれの処理を_middleware_としてファイルを分けて実装し、`app.use(middleware)`で処理を追加しています。

[import errorHandler.js](../../src/connect/errorHandler.js)

[import nosniff.js](../../src/connect/nosniff.js)

[import hello.js](../../src/connect/hello.js)

[import connect-example.js](../../src/connect/connect-example.js)

基本的にどの_middleware_も`app.use(middleware)`という形で拡張でき、
モジュールとして実装すれば再利用もしやすい形となっています。

> **Note** _middleware_となる関数の引数が4つであると、それはエラーハンドリングの_middleware_とするという、connectの独自のルールがあります。
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,19 @@
],
"devDependencies": {
"babel": "^5.8.23",
"connect": "^3.4.0",
"coveralls": "^2.11.4",
"eslint": "^1.3.0",
"eslint-plugin-markdown": "git://github.com/eslint/eslint-plugin-markdown.git",
"espower-babel": "^3.3.0",
"gitbook-cli": "^0.3.4",
"gitbook-plugin-edit-link": "^1.4.1",
"gitbook-plugin-include-codeblock": "^1.4.0",
"gitbook-plugin-richquotes": "0.0.5",
"gitbook-summary-to-path": "^1.0.1",
"jsdom": "^6.3.0",
"mocha": "^2.2.5",
"node-fetch": "^1.3.2",
"npm-run-all": "^1.2.8",
"power-assert": "^1.0.0",
"punctuate-coverage": "^1.0.3",
Expand Down
34 changes: 34 additions & 0 deletions src/connect/connect-echo-example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"use strict";
import connect from "connect";
import http from "http";
import fetch from "node-fetch";
import assert from "assert";
var app = connect();
// add Error handling
app.use(function (err, req, res, next) {
console.error(err.stack);
res.status(500).send(err.message);
next();
});
// request to response
app.use(function (req, res) {
req.pipe(res);
});
//create node.js http server and listen on port
var server = http.createServer(app).listen(3000, request);

// request => response
function request() {
var closeServer = server.close.bind(server);
var requestBody = {
"key": "value"
};
fetch("http://localhost:3000", {
method: "POST",
body: JSON.stringify(requestBody)
})
.then(res => res.text())
.then(text => {
assert.deepEqual(text, requestBody);
}).then(closeServer, closeServer);
}
30 changes: 30 additions & 0 deletions src/connect/connect-example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use strict";
import errorHandler from "./errorHandler";
import hello from "./hello";
import nosniff from "./nosniff";
import assert from "assert";
import connect from "connect";
import http from "http";
import fetch from "node-fetch";
const responseText = "response text";
var app = connect();
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

varまざってるの何か統一したいな

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#45 issueを立てた

// add Error handling
app.use(errorHandler());
// add "X-Content-Type-Options" to response
app.use(nosniff());
// respond to all requests
app.use(hello(responseText));
//create node.js http server and listen on port
var server = http.createServer(app).listen(3000, request);

function request() {
var closeServer = server.close.bind(server);
fetch("http://localhost:3000")
.then(res => res.text())
.then(text => {
assert.equal(text, responseText);
server.close();
})
.catch(console.error.bind(console))
.then(closeServer, closeServer);
}
6 changes: 6 additions & 0 deletions src/connect/echo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"use strict";
export default function () {
return function (req, res) {
req.pipe(res);
};
}
8 changes: 8 additions & 0 deletions src/connect/errorHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"use strict";
export default function errorHandler() {
return function (err, req, res, next) {
console.error(err.stack);
res.status(500).send(err.message);
next();
};
}
6 changes: 6 additions & 0 deletions src/connect/hello.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"use strict";
export default function (text) {
return function (req, res) {
res.end(text);
};
}
17 changes: 17 additions & 0 deletions src/connect/nosniff.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use strict";
function setHeaders(res, headers) {
Object.keys(headers).forEach(key => {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これES6だともっとキレイに書けるような気がする…
for of以外に何かあったりするかなー(for ofはtranspileにやさしくない

let value = headers[key];
if (value !== null) {
res.setHeader(key, value);
}
});
}
export default function () {
return function (req, res, next) {
setHeaders(res, {
"X-Content-Type-Options": "nosniff"
});
next();
};
}
97 changes: 97 additions & 0 deletions test/connect/hello-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// LICENSE : MIT
"use strict";
import assert from "power-assert";
import connect from "connect";
import errorHandler from "../../src/connect/errorHandler";
import nosniff from "../../src/connect/nosniff";
import hello from "../../src/connect/hello";
import echo from "../../src/connect/echo";
import http from "http";
import fetch from "node-fetch";
describe("connect", function () {
var responseText = "test";
var server;
describe("errorHandler", function () {
beforeEach(function (done) {
var app = connect();
app.use(errorHandler());
app.use((req, res, next) => {
next(new Error("wrong"));
});
server = http.createServer(app).listen(3000, done);
});
afterEach(function () {
server && server.close();
});
it("should return 500 status response", function () {
return fetch("http://localhost:3000")
.then(res => res.status)
.then(status => {
assert(status, 500);
});
});

});
describe("hello", function () {
beforeEach(function (done) {
var app = connect();
app.use(errorHandler());
app.use(hello(responseText));
server = http.createServer(app).listen(3000, done);
});
afterEach(function () {
server && server.close();
});
it("should return response text", function () {
return fetch("http://localhost:3000")
.then(res => res.text())
.then(text => {
assert.equal(text, responseText);
});
});
});
describe("sniff", function () {
beforeEach(function (done) {
var app = connect();
app.use(nosniff());
app.use(hello(responseText));
server = http.createServer(app).listen(3000, done);
});
afterEach(function () {
server && server.close();
});
it("should return response has `X-Content-Type-Options` header", function () {
return fetch("http://localhost:3000")
.then(res => {
assert.equal(res.headers.get("x-content-type-options"), "nosniff");
});
});
});
describe("echo", function () {
beforeEach(function (done) {
var app = connect();
app.use(echo());
server = http.createServer(app).listen(3000, done);
});
afterEach(function () {
server && server.close();
});
it("should return request as response", function () {
var requestBody = {
key: "value"
};
return fetch("http://localhost:3000", {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(requestBody)
}).then(res => {
return res.json();
}).then(json => {
assert.deepEqual(json, requestBody);
});
});
});
});