diff --git a/ja/connect/README.md b/ja/connect/README.md index d3b1308..e84ab02 100644 --- a/ja/connect/README.md +++ b/ja/connect/README.md @@ -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)`という形で拡張でき、 @@ -66,9 +67,9 @@ Connectが登録された_middleware_をどう処理するかというと、 上記の例だと以下の順番で_middleware_が呼び出されることになります。 -- errorHandler - nosniff - hello +- errorHandler エラーハンドリングの_middleware_は処理中にエラーが起きた時のみ呼ばれます。 @@ -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) -## 実装してみよう \ No newline at end of file diff --git a/src/connect/errorHandler.js b/src/connect/errorHandler.js index de79749..e34d024 100644 --- a/src/connect/errorHandler.js +++ b/src/connect/errorHandler.js @@ -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(); }; } \ No newline at end of file diff --git a/src/connect/junction-example.js b/src/connect/junction-example.js new file mode 100644 index 0000000..63ae6f2 --- /dev/null +++ b/src/connect/junction-example.js @@ -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!"); +}); \ No newline at end of file diff --git a/src/connect/junction.js b/src/connect/junction.js new file mode 100644 index 0000000..8b379d6 --- /dev/null +++ b/src/connect/junction.js @@ -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(); + } +} \ No newline at end of file diff --git a/test/connect/hello-test.js b/test/connect/hello-test.js index ef25e72..02a43e2 100644 --- a/test/connect/hello-test.js +++ b/test/connect/hello-test.js @@ -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); }); }); diff --git a/test/connect/junction-test.js b/test/connect/junction-test.js new file mode 100644 index 0000000..22d5f30 --- /dev/null +++ b/test/connect/junction-test.js @@ -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(); + }); + }); + }); +}); \ No newline at end of file diff --git a/test/prh-rule.yaml b/test/prh-rule.yaml index d93cc60..def02aa 100644 --- a/test/prh-rule.yaml +++ b/test/prh-rule.yaml @@ -72,3 +72,6 @@ rules: pattern: - プラグイン機構 - プラグインのアーキテクチャ + - expected: middleware + pattern: + - ミドルウェア \ No newline at end of file