Skip to content

Connect likeの実装 #65

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 11 commits into from
Sep 19, 2015
25 changes: 21 additions & 4 deletions ja/connect/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ Echoサーバでは `req.pipe(res);` という形でリクエストをそのま

それぞれの処理を_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 errorHandler.js](../../src/connect/errorHandler.js)

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

基本的にどの_middleware_も`app.use(middleware)`という形で拡張でき、
Expand All @@ -66,9 +67,9 @@ Connectが登録された_middleware_をどう処理するかというと、

上記の例だと以下の順番で_middleware_が呼び出されることになります。

- errorHandler
- nosniff
- hello
- errorHandler

エラーハンドリングの_middleware_は処理中にエラーが起きた時のみ呼ばれます。

Expand Down Expand Up @@ -110,6 +111,22 @@ Rackを参考にして実装されています。

- [Ruby - Rack解説 - Rackの構造とRack DSL - Qiita](http://qiita.com/higuma/items/838f4f58bc4a0645950a#2-5 "Ruby - Rack解説 - Rackの構造とRack DSL - Qiita")

次は、先ほど抽象的なコードとなっていたものを、具体的な実装にしていきます。
次は、先ほど抽象的なコードとなっていたものを具体的な実装にしながら見ていきます。

## 実装してみよう

`Junction`というConnectライクな_middleware_をサポートしたものを作成してみます。

`Junction`は、`use(middleware)` と `process(value, (error, result) => { });`を持っているシンプルなクラスです。

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

実装を見てみると、`use`で_middleware_を登録して、`process`で登録したミドルウェアを順番に実行していきます。
Copy link
Owner Author

Choose a reason for hiding this comment

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

ミドルウェア…

そのため、`Junction`自体は渡されたデータは何も処理せずに、_middleware_との中継のみをしています。

登録する_middleware_はConnectと同じで、処理をしたら`next`を呼んで、次の_middleware_が処理するというのを繰り返しています。

使い方はConnectと引数の違いはありますが、ほぼ同じような形で利用できます。

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

## 実装してみよう
5 changes: 3 additions & 2 deletions src/connect/errorHandler.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"use strict";
export default function () {
return function errorHandling(err, req, res, next) {
console.error(err.stack);
res.status(500).send(err.message);
res.writeHead(404);
res.write(err.message);
res.end();
next();
};
}
25 changes: 25 additions & 0 deletions src/connect/junction-example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use strict";
import Junction from "./junction";
import assert from "assert";
let junction = new Junction();
junction.use(function toUpperCase(res, next) {
res.value = res.value.toUpperCase();
next();
});
junction.use(function exclamationMark(res, next) {
res.value = res.value + "!";
next();
});
junction.use(function errorHandling(error, res, next) {
console.error(error.stack);
next();
});

let text = "hello world";
junction.process(text, function (error, result) {
if (error) {
console.error(error);
}
let value = result.value;
assert.equal(value, "HELLO WORLD!");
});
43 changes: 43 additions & 0 deletions src/connect/junction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"use strict";
function isErrorHandingMiddleware(middleware) {
// middleware(error, text, next)
let arity = middleware.length;
return arity === 3;
}
function applyMiddleware(error, response, middleware, next) {
let errorOnMiddleware = null;
try {
if (error && isErrorHandingMiddleware(middleware)) {
middleware(error, response, next);
} else {
middleware(response, next);
}
return;
} catch (error) {
errorOnMiddleware = error;
}
// skip the middleware or Error on the middleware
next(errorOnMiddleware, response);
}

export default class Junction {
constructor() {
this.stack = [];
}

use(middleware) {
this.stack.push(middleware);
}

process(initialValue, callback) {
let response = {value: initialValue};
let next = (error) => {
let middleware = this.stack.shift();
if (!middleware) {
return callback(error, response);
}
applyMiddleware(error, response, middleware, next);
};
next();
}
}
6 changes: 3 additions & 3 deletions test/connect/hello-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ describe("connect", function () {
describe("errorHandler", function () {
beforeEach(function (done) {
let app = connect();
app.use(errorHandler());
app.use((req, res, next) => {
next(new Error("wrong"));
});
app.use(errorHandler());
server = http.createServer(app).listen(3000, done);
});
afterEach(function () {
server && server.close();
});
it("should return 500 status response", function () {
it("should return 404 status response", function () {
return fetch("http://localhost:3000")
.then(res => res.status)
.then(status => {
assert(status, 500);
assert(status, 404);
});
});

Expand Down
49 changes: 49 additions & 0 deletions test/connect/junction-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// LICENSE : MIT
"use strict";
import assert from "power-assert";
import Junction from "../../src/connect/junction";
describe("junction", function () {
context("when register middlewares", function () {
it("should connect middleware, the order is register", function (done) {
let junction = new Junction();
junction.use(function errorHandling(error, text, next) {
next(error);
});
junction.use(function toUpper(res, next) {
res.value = res.value.toLocaleUpperCase();
next();
});
junction.use(function addDesu(res, next) {
res.value += " suffix";
next();
});
junction.process("text", (error, result) => {
if (error) {
return done(error);
}
assert.equal(result.value, "TEXT suffix");
done();
});
});
});
context("when occur error in middleware", function () {
it("should call errorHandling middleware", function (done) {
let junction = new Junction();
junction.use(function toUpper(res) {
throw new Error("error on " + res);
});
junction.use(function errorHandling(error, res, next) {
assert(error instanceof Error);
assert.equal(res.value, "text");
next();
});
junction.process("text", (error, res) => {
if (error) {
return done(error);
}
assert.equal(res.value, "text");
done();
});
});
});
});