#現状
ログイン処理の作成中、postされたユーザ情報を元にDBベースに検索をかけてパスワードのチェックをして結果を返すはずが、結果を待たずundefinedを返してしまいます。
#問題のコード
まずは、Expressのポストを受けつける部分です。
app.post('/signup', async (req, res, next) => {
try {
var controler = new AccountControler(dbcontroler);
var result = await controler.signup(req.body);
logging.logger.debug(result);
res.json(result);
} catch(error) {
next(error)
}
});
ログイン処理を行うAccountControlerのsignupメソッドをawaitでコールしています。
また、その結果をコンソールにログとして吐き出しています。
async getLoginSuccessMessage(res) {
return {
success: true,
token: await jwt.sign(
{
userid: res.userid,
authtime: moment().format()
},
publickey
),
userinfo: {
username: res.username
}
}
}
async signup(data) {
try {
data = JSON.parse(JSON.stringify(data));
// DBからユーザ情報を取得
var res = await this.dbcontroler.selectRecode(
this.LoginUserViewModel.getSelectSqlOfLoginUser(data.loginuser.loginid),
(res) => this.LoginUserViewModel.serializerFromDatabase(res));
if (res.length == 0) {
return this.getLoginFailedMessage();
} else {
// パスワードが一致しているかチェック
return await bcrypt.compare(data.loginuser.loginpassword, res[0].loginpassword, async (err, result) => {
if (err) {
this.logger.debug(err);
return await this.getLoginFailedMessage();
} else if (result) {
// ログ出力
this.logger.debug('result is' + JSON.stringify(await this.getLoginSuccessMessage(res)));
// ログイン可能時に返却するデータを生成
return await this.getLoginSuccessMessage(res);
} else {
return await this.getLoginFailedMessage();
}
})
}
} catch (error) {
this.logger.debug(error);
throw error;
}
}
いくつか処理を行っていますが、
①ログインIDをキーにDBからユーザ情報を取得(コールバック使用)
②bcryptでハッシュ化されたパスワードと受信したパスワードを比較(コールバック使用)
③結果をjwtでユーザ情報に関する情報を暗号化
上記のような感じで全てawaitを使用しています。
[2019-09-03T10:03:09.072] [DEBUG] default - undefined
[2019-09-03T10:03:09.216] [DEBUG] default - result is{"success":true,"token":"eyJhbGcU...
長いので少し省略していますが、分かりにくいログですが、1行目が最初のコードのログ出力で、次が2つめのコードのログ出力内容です。
最初のコードでawaitが待ってくれず、処理されているように思えます。
#修正内容1
一応Expressが同期処理に対応しているかの確認も含めて、以下をチェックしました
とりあえず、promise形式に変更してみました。
app.post('/signup', (req, res, next) => {
var controler = new AccountControler(dbcontroler);
controler.signup(req.body)
.then((d) => {
logging.logger.debug(d);
res.json(d)
})
.catch((error) => next(error));
});
結果はかわらず
#修正内容2
上記の変更はのままに、コール側の処理も合わせてPromiseに変更しました
async signup(data) {
return new Promise((resolve, reject) => {
data = JSON.parse(JSON.stringify(data));
this.logger.debug(JSON.stringify(data))
this.dbcontroler.selectRecode(
this.LoginUserViewModel.getSelectSqlOfLoginUser(data.loginuser.loginid),
(res) => this.LoginUserViewModel.serializerFromDatabase(res))
.then((res) => {
if (res.length == 0) {
return this.getLoginFailedMessage();
} else {
bcrypt.compare(data.loginuser.loginpassword, res[0].loginpassword)
.then( async (result) => {
if (result) {
this.logger.debug('result is' + JSON.stringify(await this.getLoginSuccessMessage(res)));
resolve(await this.getLoginSuccessMessage(res));
} else {
resolve(await this.getLoginFailedMessage());
}
})
.catch((error) => {
this.logger.debug(error);
reject(error)
})
}
})
.catch((error) =>{
this.logger.debug(error);
reject(error);
})
});
}
結果、うまく動作しました。ログは下記の通りです。
[2019-09-03T11:45:41.723] [DEBUG] default - result is{"success":true,"token":"eyJhbGciOiJ...
[2019-09-03T11:45:41.724] [DEBUG] default - { success: true,token:'eyJhb...
#結論
最終的に期待通りに動作はしました。しかし、なぜ動作したのか解っていません。
(私のasync/awaitの使い方が悪いということかも知れません。多分そんなことだと思いますが…)
解るのは、他も全部修正ということだけです…
ちなみに私は、下記を参考にプロジェクトを生成しています。
Vue-CLI3 ベースのアプリ開発で JSON API が使える express server を使う
簡単に作成方法を記載すると、
$vue create prpjectname
$vue add express
上記でvue-cli-plugin-expressがインストールされるようです。
バージョンは下記の通りです。
$npm view express version
4.17.1
$npm view vue-cli-plugin-express version
1.0.2
バージョンは現時点では最新のようです。
vue-cliを用いずExpressを使用した場合、このような動作を行うか分かりません。
残念ながら試すのも困難(面倒…)なのでここまでです。
この方法がベスト(スタンダード?)なのか、どうなのか気になります。
知っている方はコメントして頂けると、私の気持ちがスッキリするのでコメントしてください。
#再修正
コメント頂きました!ありがとうございます。
ということで、大本のソースの状態に戻した上で、下記のように修正を行いました。
async signup(data) {
try {
data = JSON.parse(JSON.stringify(data));
this.logger.debug(JSON.stringify(data))
var res = await this.dbcontroler.selectRecode(
this.LoginUserViewModel.getSelectSqlOfLoginUser(data.loginuser.loginid),
(res) => this.LoginUserViewModel.serializerFromDatabase(res));
if (res.length == 0) {
return this.getLoginFailedMessage();
} else {
// 修正は以下の部分でcallbackを取りやめました
const result = await bcrypt.compare(data.loginuser.loginpassword, res[0].loginpassword);
if (result) {
this.logger.debug('result is' + JSON.stringify(await this.getLoginSuccessMessage(res)));
return await this.getLoginSuccessMessage(res);
} else {
return await this.getLoginFailedMessage();
}
}
} catch (error) {
this.logger.debug(error);
throw error;
}
}
上記の結果、期待通りに動作致しました!
コメント頂いた下記からの抜粋です。
bcrypt
Async methods that accept a callback, return a Promise when callback is not specified if Promise support is available.
callbackを指定しなければ、Promiseを返すと書かれていますね。
やはり、私の書き方が悪かったようです。でも、スッキリしました。