使用 fetch 和 ES6 承诺处理自定义错误的最简洁方法

Posted

技术标签:

【中文标题】使用 fetch 和 ES6 承诺处理自定义错误的最简洁方法【英文标题】:Cleanest way to handle custom errors with fetch & ES6 promise 【发布时间】:2016-02-27 17:01:19 【问题描述】:

我正在尝试使用 fetch 和 ES6 承诺智能地处理来自我们 API 的成功/错误响应。

以下是我需要如何处理响应状态:

204: has no json response, but need to treat as success
406: should redirect to sign in
422: has json for error message
< 400 (but not 204): success, will have json
>= 400 (but not 422): error, will not have json

所以,我正在为如何干净利落地写这个而苦苦挣扎。

我现在有一些不太出色的代码,看起来像这样:

fetch()
  .then(response => checkStatus(response))
  .then(parseJSON)                           //will throw for the 204
  .then(data => notify('success', someMsg))
  .catch(error => checkErrorStatus(error))
  .then(parseJSON)
  .then(data => notify('error', dataForMsg)
  .catch(error => notify('error', someGenericErrorMsg)

但是使用两次 catch 似乎很奇怪,我还不知道如何处理那个 204。

另外,为了澄清 checkStatuscheckErrorStatus 做类似的事情:

export function checkStatus(response) 
  if (response.status >= 200 && response.status < 300) 
    return response
   else 
    let error = new Error(response.statusText)
    error.response = response
    throw error
  


function checkErrorStatus(error) 
  if(error.response.status === 422) 
    return error.response
   else 
    let error = new Error(response.statusText)
    error.response = response
    throw error
  

对清理这个有什么建议吗?

【问题讨论】:

哦,就这么简单:export function parseJSON(response) return response.json() 对于422 的情况,请参阅this question ”是什么意思? 好吧,来自服务器的状态为 422 的响应将包含一些 JSON,其中包含我用来在错误通知中显示的验证错误消息之类的内容。但是所有其他 400 都没有那个 json。他们需要区别对待。 我的意思是它应该是 "" 和 ">= 400 (but not 422): error, no json " 【参考方案1】:

我认为你可以很容易地写出来:

fetch(…).then(response => 
    if (response.ok)
        return response[response.status == 204 ? "text" : "json"]();
    if (response.status == 422)
        return response.json().then(err =>  throw err; );
    if (response.status == 406)
        var error = new AuthentificationError(response.statusText); // or whatever
    else
        var error = new Error(response.statusText)
    error.response = response
    throw error;
)

【讨论】:

是的,这很好,你至少想到了两件我可能没有想到的事情。现在我看到这个,这个问题在面试中将是一个很好的挑战! 请注意,我的 webpack 设置抱怨 throw err 不在大括号中,但除此之外,您的解决方案破坏了它。 @jasongonzales:谢谢 - 既然你提到了它,我的心理语法解析器也会发出警报 :-) 仅供参考,您不能在单行 if 块中使用 let。您需要进一步声明let error,或者使用...var :) @RGraham:很好,谢谢。我刚刚了解到这甚至是一个语法错误,因为let 声明不是语句,它们是语句列表项。【参考方案2】:

按照 Bergi 的解决方案,您可以考虑使用相当简单的 ResponseHandler() 对象来机械化处理响应;

function ResponseHandler() 
    this.handlers = [];
    this.handlers[0] = function() ; // a "do nothing" default handler

ResponseHandler.prototype.add = function(code, handler) 
    this.handlers[code] = handler;
;
ResponseHandler.prototype.handle = function(response) 
    var h = this.handlers,
        s = response.status,
        series = Math.floor(s / 100) * 100; // 100, 200, 300 etc
    (h[s] || h[series] || h[0])(response); // sniff down the line for a specific/series/default handler, then execute it.
;

使用中:

// create an instance of ResponseHandler() and add some handlers :
var responseHandler = new ResponseHandler();
responseHandler.add(204, function(response) ...); // specific handler
responseHandler.add(422, function(response) ...); // specific handler
responseHandler.add(406, function(response) ...); // specific handler
responseHandler.add(200, function(response) ...); // 200 series default handler
responseHandler.add(400, function(response) ...); // 400 series default handler
responseHandler.add(0, function(response) ...); // your overall default handler

// then :
fetch(…).then(response =>  responseHandler.handle(response); );

您会失去像 Bergi 这样的硬编码解决方案的效率,但可能会从改进的可管理性和可重用性中受益。

【讨论】:

以上是关于使用 fetch 和 ES6 承诺处理自定义错误的最简洁方法的主要内容,如果未能解决你的问题,请参考以下文章

并行承诺的自定义错误处理

如何使用 fetch 在 javascript 中获取 php 错误消息

fetch:拒绝带有 JSON 错误对象的承诺

next() 承诺错误处理

使用 fetch 时拒绝承诺 [重复]

.catch() 甚至通过 fetch() 从另一个承诺中捕获所有错误