質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

ルーティング

ルーティングとは、TCP/IPネットワークにおいて、目的のホストまでパケットを送る為のパス選定のプロセスを言います。

Express

ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

Q&A

解決済

2回答

3338閲覧

Expressのルーティング順序について

maskmelon

総合スコア63

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

ルーティング

ルーティングとは、TCP/IPネットワークにおいて、目的のホストまでパケットを送る為のパス選定のプロセスを言います。

Express

ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

0グッド

1クリップ

投稿2020/09/10 13:23

編集2020/09/13 08:58

Express.jsでアプリを作成しています。初めてサイトに訪れたときに匿名ユーザーを自動で作成してクッキーに保存するために以下のミドルウェアを作りました。

JavaScript

1const User = require('../models/user'); 2 3const createAnonymUser = async (req, res, next) => { 4 if (!req.cookies.anonymUser && !req.user) { 5 const anonymUser = await User.create({ username: 'anonymous', displayName: '名無しさん' }); 6 res.cookie('anonymUser', anonymUser); 7 return next(); 8 } 9 next(); 10}; 11 12module.exports = createAnonymUser; 13

このミドルウェアをすべてのルーティングに優先させて実行したいのですが、現状匿名ユーザーが作成されるよりも先に他のルーティングが実行されてしまう結果、匿名ユーザーのプロパティを取得できずエラーが発生してしまいます。リロードするとエラーは消えます。

ルーティングの設定は以下のようになっています。Expressは上から順にルーティングを実行していくと聞いたことがあり、ミドルウェアを一番上に置いているのですが、なぜうまく行かないのでしょうか?

JavaScript

1// router setup 2app.use(createAnonymUser); 3app.use(indexRouter); 4app.use('/questions', questionsRouter); 5app.use('/oauth2', oauth2Router); 6app.use('/comments', commentsRouter); 7app.use('/users', usersRouter);

###追記
実行例①

JavaScript

1const createAnonymUser = (req, res, next) => { 2 console.log('createAnonymUser'); 3 if (!req.cookies.anonymUser && !req.user) { 4 const anonymUser = (async () => await User.create({ username: 'anonymous', displayName: '名無しさん' }))(); 5 console.log(anonymUser); 6 res.cookie('anonymUser', anonymUser); 7 return next(); 8 } 9 next(); 10};

結果①
イメージ説明

実行例②

JavaScript

1const createAnonymUser = (req, res, next) => { 2 console.log('createAnonymUser'); 3 if (!req.cookies.anonymUser && !req.user) { 4 (async () => { 5 const anonymUser = new User({ username: 'anonymous', displayName: '名無しさん' }); 6 await anonymUser.save(); 7 res.cookie('anonymUser', anonymUser); 8 console.log(anonymUser); 9 return next(); 10 })(); 11 } else { 12 next(); 13 } 14};

結果②
イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

JavaScript

1const createAnonymUser = (req, res, next) => { 2 console.log('createAnonymUser'); 3 if (!req.cookies.anonymUser && !req.user) { 4 User.create({ username: 'anonymous', displayName: '名無しさん' }, (err, anonymUser) => { 5 if (err) { 6 console.log(err); 7 } 8 res.cookie('anonymUser', anonymUser); 9 res.redirect(req.originalUrl); 10 }); 11 } else next(); 12}; 13

レスポンスが帰ってきた後でなければcookieに保存されているデータにアクセスできないので、cookieにデータを保存した直後にリダイレクトさせることで、後続のミドルウェアでreq.cookieからデータを取得できるようにしました。

投稿2020/09/20 18:22

maskmelon

総合スコア63

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

なぜうまく行かないのでしょうか?

ミドルウェアを async 関数にしているためです。

ExpressはPromiseが標準化される以前に登場したNodeJS用のフレームワークです
コールバック形式のミドルウェア関数を内部実装でフロー制御します。

ご質問のように async 関数をミドルウェアにした場合、
async 関数自体は非同期ですので、Promiseの結果を待たずにフロー制御され、
次のミドルウェア関数に処理を移そうとします。


Promise を返却する関数を活用する場合、
以下のように async関数を即時実行する形にするなど、工夫が必要です。

javascript

1const User = require('../models/user'); 2 3const createAnonymUser = (req, res, next) => { 4 5 if (!req.cookies.anonymUser && !req.user) { 6 const anonymUser = (async () => { 7 return await User.create({ username: 'anonymous', displayName: '名無しさん' }); 8 })(); 9 res.cookie('anonymUser', anonymUser); 10 return next(); 11 } 12 next(); 13}; 14/** 15const createAnonymUser = (req, res, next) => { 16 17 if (!req.cookies.anonymUser && !req.user) { 18 User.create({ username: 'anonymous', displayName: '名無しさん' }) 19 .then( anonymUser => { 20 res.cookie('anonymUser', anonymUser); 21 return next(); 22 }) 23 })(); 24 } 25 else next(); 26}; 27 */ 28module.exports = createAnonymUser;

投稿2020/09/13 05:15

編集2020/09/13 09:37
AkitoshiManabe

総合スコア5432

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maskmelon

2020/09/13 09:00 編集

回答くださりありがとうございます。確かに非同期関数のミドルウェアを順次実行すると実行完了を待たずに次の関数に移るのが自然ですね。。 ご指摘頂いた方法を試してみたのですが(実行例①)、プロミスがPending状態で返ってきてしまいました。 そこで匿名ユーザーの作成、保存、return next()までをasync関数の中に入れて、ifelseで分岐してnext()を実行させるようにしました。(実行例②) ログ出力をすると、createAnonymUser関数の実行→作成したanonymUserの表示→レンダリングという順になっているのですが、やはりレンダリングの時点でanonymUserのプロパティにアクセスすることができません。度々の質問で申し訳ないのですが、ご教授頂けますと幸いです。
AkitoshiManabe

2020/09/13 11:00 編集

本来は、コールバックで記述するのが安定するフレームワークですので、フローは色々考えてみる必要があります。回答欄のソースにコメントでもう一例を記述してみました。
maskmelon

2020/09/20 18:23

返事が遅くなってしまい申し訳ありません。 非同期処理の制御について理解が深まりました。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問