rm /blog

IT系技術職のおっさんがIT技術とかライブとか日常とか雑多に語るブログです。* 本ブログに書かれている内容は個人の意見・感想であり、特定の組織に属するものではありません。/All opinions are my own.*

【Node.js】request-promiseのerrorについて

Node.jsでrequest-promiseを使ってREST APIを呼び出していたら、HTTP 403が返ってきてるのにrejectにならなくて、「なんでだ?」と思って簡単に調べた記録。
なお、2020年2月11日でNode.jsのrequest-promise(の依存モジュールであるrequestを含め関連モジュール)は「完全に非推奨」になっている。(公式のgithubより)
要するに完全非推奨のモジュールに関する記事であり、この先参考になるケースが現れるとはあまり思えないので(というか開発者が非推奨とした意向を汲み取るならそのようなケースが出てきてはならないはずだ)、まあ、その辺留意したうえで、暇つぶしの雑記として読んでもらうのが吉だと思います。。


 

実験準備

とりあえずリクエストを受け付ける用の簡単なexpressサーバを用意する。
HTTP 200と403を返すエンドポイントだけある、簡易なもの。

const express = require('express');
const app = express();

app.post('/test200', (req,res)=>{
    res.status(200).send('status200');
});

app.post('/test403', (req,res)=>{
    res.status(403).send('status403');
});

app.listen(4000, ()=>{
    console.log('server listened...');
});

 

実験1

これに対して下記のコード(別のjsファイル)でrequest-promiseを使ってPOSTを投げてみる(POSTであることにあまり意味はない、個人的にそのとき遭遇したのがPOSTだったからである)

(async()=>{
    let options = {
        uri: 'http://localhost:4000/' + process.argv[2],
        method: 'POST'
    };

    try {
        let res1 = await doRequest1(options);
    }catch(error) {
        console.log('error happended');
    }


})();

async function doRequest1(options) {
    return new Promise((resolve,reject)=>{
        rp(options,(err,res,body)=>{
            if(err){
                console.log('doRequest1:error');
                reject(err);
            } else {
                console.log('doRequest1:success');
                resolve(res);
            }
        })
    });
}


引数にエンドポイントの相対パスを渡すことで実験を簡単にできるようにした。。
console.logはデバッグ用。

これで"/test403"を引数に渡して上のjsを実行すると、なんでかわからんがrejectのルートに入らず、resolveのルートに入って成功した感じになる。
標準出力にはresolveにある"doRequest1:success"が出力される
403ってエラーじゃないの?なんで?と思ったけどどうもrequestはそういう動きを取るみたいだ。

逆にどういうケースでエラーになるんだ?と思ってちょっと試したが、存在しないホストや起動していないポートを指定するとエラーになるようだ。
上記のケースだと例えばhttp://localhost:4001/~に対してリクエストするとエラーになる(もちろん事前に4001番ポートでアプリ動かしていないことが前提となるが)

実験2

一方、以下のような処理内容にすると、403でもエラー扱いになる。

async function doRequest2(options) {
    return new Promise((resolve,reject)=>{
        rp(options)
        .then((res)=>{
            console.log('doRequest2:success');
            resolve(res);
        })
        .catch((err)=>{
            console.log('doRequest2:error');
            reject(err);
        })
    });
}


この書き方だと403が起きるとcatchのほうに流れていく。

書き方が違うだけで「リクエストの後処理」という意味では同じだと思ってたが、挙動を見る限り異なるので、なんか違うんだろう。

どうでもいいが、今になってよく考えるとPromiseは別にこれ特に必要ないな…
呼び出し元がasyncだったんで合わせて無理やりPromiseにしたが…
要するにrequest-promiseのコールバック関数を使うやり方と、コールバック関数なしでthen・catchで結果を受け取るやり方は挙動が違うらしい、という実験結果なだけだ。
まあ、そんな感じです。(?)