Skip to content
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`で登録した_middleware_を順番に実行していきます。
そのため、`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();
});
});
});
});
3 changes: 3 additions & 0 deletions test/prh-rule.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,6 @@ rules:
pattern:
- プラグイン機構
- プラグインのアーキテクチャ
- expected: middleware
pattern:
- ミドルウェア