如何使用Node.js的http模块处理错误

huangapple go评论71阅读模式
英文:

How to handle error using nodejs http module

问题

我有两台服务器。Server1中的addUser API使用Node.js的http模块调用Server2中的getId API。以下是我的代码,但当addUser尝试解析从getId API获取的数据时,我总是收到错误(我不知道是否是错误,因此解析数据时出现错误)。如果Server2中的getId API返回一个正常的res.json(),解析数据就没有问题。我想知道为什么getId会抛出错误,而addUser API仍然进入on(data,...)?在这种情况下,处理错误的正确方法是什么?

Server1

app.get('/api/addUser/:userName', (req, res) => {
    const options = {
        host: 'localhost',
        port: 3090,
        path: '/api/getId',
        method: 'GET',
        headers: {
            accept: 'application/json'
        }
    };
    try {
        const x = http.request(options, function(res){
            let ret;
            res.on('data', function(data){
                console.log(data);
                if (!data) {
                    return;
                }
                const userId = JSON.parse(data); // 这一行抛出错误
                const user = {
                    userName: req.params.userName,
                    userId
                };
                ret = user;
                users.push(user);
            });
        }).on("error", err => { console.log(err); }).end();
    } catch (err) {
        errorCounter.add(1);
    }
});

Server2

app.get('/api/getId', (req, res) => {
   throw new Error('getId API错误');
});
英文:

I have two servers. An addUser api in Server1 calls getId api in Server2 using nodejs http module. Below is my code, but I always receive error when addUser tries to parse the data(I don't know if it is error so it has error parsing data) from getId api. If getId api in Server2 returns a normal res.json(), parsing data has no issue. I wonder why getId throw error, the addUser api still goes into on.(data, ...)? What is the right way to handle error in this case?

Server1

app.get('/api/addUser/:userName', (req, res) => {
        const options = {
          host: 'localhost',
          port: 3090,
          path: '/api/getId',
          method: 'GET',
          headers: {
              accept: 'application/json'
          }
        };
        try {
            const x = http.request(options, function(res){
              let ret;
              res.on('data', function(data){
                console.log(data);
                if (!data) {
                  return;
                }
                const userId = JSON.parse(data); // throw error this line
                const user = {
                  userName: req.params.userName,
                  userId
                };
                ret = user;
                users.push(user);
            });
            }).on("error", err => { console.log(err); }).end();
        } catch (err) {
          errorCounter.add(1);
        }

Server2

app.get('/api/getId', (req, res) => {
   throw new Error('getId api error');
});

答案1

得分: 1

以下是您要翻译的内容:

当您在Express请求处理程序的顶层抛出异常时(不在异步回调内部),Express会捕获该异常,并且默认情况下(如果您没有Express错误处理程序),将其转换为500响应状态,并将text/html发送回带有错误摘要的响应。您尝试JSON.parse()该HTML失败。

您的http.request()代码需要检查HTTP状态。在http.request()工作的底层,仅当实际HTTP请求遇到参数错误或网络故障错误时,您才会从http.request()获得错误。

当请求达到服务器并且服务器返回任何类型的响应时,这都不被视为错误。获取404状态或500状态被视为从http.request()的角度来看是成功的HTTP请求。如果您希望将4xx和5xx状态视为错误,则必须手动编写代码或使用一个为您执行此操作的更高级别库。

因此,您必须检查从响应返回的HTTP状态,仅在获得2xx响应时才尝试解析响应。

此外,data事件是来自响应流的数据块。对于快速网络上的小型响应,它可能是整个响应,但也可能仅是响应的一部分。要可靠地执行此操作,您必须累积所有来自data事件的数据,然后当end事件发生时,您现在知道已经获得了整个响应,可以解析它。

这是一个示例:

const options = {
    host: 'localhost',
    port: 3090,
    path: '/api/getId',
    method: 'GET',
    headers: {
        accept: 'application/json'
    }
};

http.request(options, function (res) {
    let result = "";
    if (res.statusCode < 300) {
        res.on('data', function (data) {
            // accumulate data from the response stream
            result += data.toString();
        }).on('end', () => {
            try {
                const userId = JSON.parse(result);
                // do something with userId here
            } catch (e) {
                // do something with error e here - error parsing JSON
            }
        });
    } else {
        // do something because of a non-2xx status code here
    }
}).on("error", err => { 
    console.log(err); 
    // do something with err here
}).end();

注意,您有3个不同的位置需要进行错误处理,以及1个位置,您拥有最终的响应。

正如我在评论中所说,使用高级别的HTTP库(例如got()node-fetch())会简化这个过程。以下是使用got()的代码示例:

import got from 'got';

const options = {
    headers: { accept: 'application/json' }
};

got("http://localhost:3090/api/getId", options).json().then(result => {
    // 这是您解析的JSON
    console.log(result);
}).catch(err => {
    // 出现错误
    console.log(err);
});

如您所见,got()库为您执行了许多操作。它检查HTTP状态,解析JSON,将所有错误路径组合成一个错误返回(拒绝的Promise)。它接受URL而不是选项对象,并且相同的库调用适用于https或http URL。并且它返回一个Promise。

英文:

When you throw at the top level of your Express request handler (not inside an asynchronous callback), Express will catch that exception and, by default (if you don't have an Express error handler), will turn it into a 500 response status and will send back text/html with a summary of the error. Your attempt to JSON.parse() that HTML fails.

Your http.request() code needs to check the http status. At the low level that http.request() works, the only way you get an error from http.request() is if the actual http request encounters a parameter error or a network failure error.

When the request reaches the server and the server returns ANY kind of response, that is NOT considered an error. Getting a 404 status or a 500 status back is considered a successful http request from that standpoint of http.request(). If you want 4xx and 5xx status to be considered an error, then you will either have to manually code that or use a higher level library that does that for you.

As such, you have to check the http status that comes back from the response and only try to parse the response IF you get a 2xx response.

In addition, the data event is a chunk of data from the response stream. For small responses on a fast network, it might be the whole response, but it also might be just a chunk of the response. To do this reliably, you have to accumulate all data from the data events and then when the end event comes, you now know you have the whole response and you can parse it.

Here's an example:

const options = {
    host: &#39;localhost&#39;,
    port: 3090,
    path: &#39;/api/getId&#39;,
    method: &#39;GET&#39;,
    headers: {
        accept: &#39;application/json&#39;
    }
};

http.request(options, function (res) {
    let result = &quot;&quot;;
    if (res.statusCode &lt; 300) {
        res.on(&#39;data&#39;, function (data) {
            // accumulate data from the response stream
            result += data.toString();
        }).on(&#39;end&#39;, () =&gt; {
            try {
                const userId = JSON.parse(result);
                // do something with userId here
            } catch (e) {
                // do something with error e here - error parsing JSON
            }
        });
    } else {
        // do something because of a non-2xx status code here
    }
}).on(&quot;error&quot;, err =&gt; { 
    console.log(err); 
    // do something with err here
}).end();

Note, you have 3 different places, you need error handling here and 1 place where you have the final response.

As I said in the comments, this is a whole lot simpler with a higher level http library such as got() or node-fetch().

Here's how this code looks for got():

import got from &#39;got&#39;;

const options = {
    headers: { accept: &#39;application/json&#39; }
};

got(&quot;http://localhost:3090/api/getId&quot;, options).json().then(result =&gt; {
    // this is your parsed JSON here
    console.log(result);
}).catch(err =&gt; {
    // got an error here
    console.log(err);
});

As you can see, the got() library does a lot for you. It checks the http status. It parses the JSON. It combines all the error paths into one error return (a rejected promise). It takes a URL instead of an options object and the same library call works for https or http URLs. And, it returns a promise.

huangapple
  • 本文由 发表于 2023年1月5日 11:05:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/75013462.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定