Fetch-错误的获取方式在JavaScript中非常棒。但是,您的代码中可能充斥着这样的内容:constres=awaitfetch('/user')constuser=awaitres.json()这段代码虽然简单易用,但存在很多问题。你可以说“哦,是的,错误处理”,然后像这样重写它:当然,这是一种改进,但仍然存在问题。在这里,我们假设user实际上是一个User对象……但这是假设我们收到200响应。但fetch不会针对200以外的状态抛出错误,因此您实际上可能会收到400(错误请求)、401(未授权)、404(未找到)、500(内部服务器错误)或各种其他问题。A一种更安全但更丑陋的方式所以我们可以进行另一个更新:401:/*Handle*/breakcase404:/*Handle*/breakcase500:/*Handle*/break}}//User是用户constuser=awaitres.json()}catch(err){//错误处理}现在,我们终于可以很好地使用fetch了。但它可能有点笨拙,因为每次都必须记住它,而且你必须希望你团队中的每个人每次都能处理这些情况。它在控制流方面也不是最优雅的。在可读性方面,我个人更喜欢本文开头提到的代码。它读起来很清晰——获取用户,解析为json,用用户对象做一些事情。但是在这种格式中,我们获取用户,处理一堆错误情况,解析json,处理其他错误情况等。这有点不协调,尤其是在我们在业务逻辑之上和之下进行错误处理的这一点上,而不是在一个地方。一种不那么丑陋的方法如果请求有问题,一个更优雅的解决方案可能是抛出异常而不是在多个地方处理错误:try{constres=awaitfetch('/user')if(!res.ok){thrownewError('Badfetchresponse')}constuser=awaitres.json()}catch(err){//错误处理}但我们还有最后一个问题——当我们需要处理错误时,我们输了很多有用的上下文。我们实际上无法访问catch块内的res,因此在处理错误时我们实际上不知道响应的状态代码或主体是什么。这将使我们很难知道要采取的最佳行动方案,并给我们留下非常无用的日志。这里一个改进的解决方案可能是创建您自己的自定义错误类,您可以在其中转发响应详细信息:fetch('/user')if(!res.ok){thrownewResponseError('Badfetchresponse',res)}constuser=awaitres.json()}catch(err){//使用完全访问权限处理错误状态和文字开关(err.response.status){case400:/*Handle*/breakcase401:/*Handle*/breakcase404:/*Handle*/breakcase500:/*Handle*/break}当我们保留状态代码时,我们现在可以更智能地处理错误。例如,我们可以通过500提醒用户我们遇到了问题,并可能重试或联系我们的支持人员。或者如果状态为401,他们当前未被授权,可能需要重新登录等。创建包装器对于我们最新最好的解决方案,我还有最后一个问题——它仍然需要开发人员每次都编写一些像样的样板文件。在整个项目范围内进行更改或强制执行此结构仍然是一个挑战。这是我们可以包装fetch以根据需要处理事情的地方:..options)if(!res.ok){thrownewResponseError('Badfetchresponse',res)}returnres}然后我们可以如下使用它:try{constres=awaitmyFetch('/user')constuser=awaitres.json()}catch(err){//通过error.response处理问题。*}在我们的最后一个例子中,最好确保我们有一个统一的方法来处理错误。这可能包括对用户的警报、日志记录等。探索开源解决方案很有趣,但重要的是要记住,您不必总是为事物创建自己的包装器。以下是一些可能值得使用的流行的现有选项,包括一些小于1kb的选项:Axiosaxios是一个非常流行的JS抓取选项,它可以自动为我们处理上面的几个场景。try{const{data}=awaitaxios.get('/user')}catch(err){//基于error.response的错误处理。*}我对Axios的唯一批评是它是一个简单的数据获取包装器大的。因此,如果大小是您的首要任务(我认为通常应该这样做以保持您的性能一流),您可能需要查看以下两个选项之一:Redaxios如果您喜欢Axios,但不喜欢它提供的软件包添加11kb的大小,Redaxios是一个很好的选择,它使用与Axios相同的API但小于1kb。importaxiosfrom'redaxios'//像往常一样使用Wretch一个更新的选项是Wretch,它是Fetch的一个非常薄的包装器,就像Redaxios。Wretch的独特之处在于它在很大程度上仍然感觉像fetch,但为您提供了处理常见状态的有用方法,这些状态很好地链接在一起:constuser=awaitwretch("/user").get()//以更易读的方式处理错误情况方式。notFound(error{/*...*/}).unauthorized(error{/*...*/}).error(418,error{/*...*/}).res(response/*...*/).catch(error{/*othererrors*/})也不要忘记安全地写入数据最后但同样重要的是,我们不要忘记直接使用fetch当通过POST、PUT或PATCH发送数据你能发现这段代码中的错误吗?//这里至少有一个错误,你能发现吗?constres=awaitfetch('/user',{method:'POST',body:{name:'SteveSewell',company:'Builder.io'}})至少有一个,但可能有两个。首先,如果我们发送JSON,body属性必须是一个JSON序列化的字符串:constres=awaitfetch('/user',{method:'POST',//?我们必须JSON序列化这个bodybody:JSON.stringify({name:'SteveSewell',company:'Builder.io'})})这很容易忘记,但如果我们使用TypeScript,这至少会自动提示我们。TypeScript不会为我们捕获的另一个错误是我们没有在此处指定Content-Type标头。许多后端要求您指定它,否则它们将无法正确处理文字。constres=awaitfetch('/user',{headers:{//?如果我们发送序列化的JSON,我们应该设置Content-Type:'Content-Type':'application/json'},method:'POST',body:JSON.stringify({name:'SteveSewell',company:'Builder.io'})})现在,我们有了一个相对健壮和安全的解决方案。(可选)向我们的包装器添加自动JSON支持我们还可能决定在包装器中为这些常见情况添加一些安全措施。例如使用下面的代码:constisPlainObject=valuevalue?.constructor===ObjectexportasyncfunctionmyFetch(...options){letinitOptions=options[1]//如果我们为fetch指定一个RequestInitif(initOptions?.body){//如果我们传递一个body属性,它是一个普通对象或数组if(Array.isArray(initOptions.body)||isPlainObject(initOptions.body)){//创建一个新的选项对象来序列化body并确保我们有一个内容类型标头initOptions={...initOptions,body:JSON.stringify(initOptions.body),headers:{'Content-Type':'application/json',...initOptions.headers}}}}constres=awaitfetch(...initOptions)if(!res.ok){thrownewResponseError('Badfetchresponse',res)}returnres}现在我们可以像这样使用我们的包装器:constres=awaitmyFetch('/user',{method:'POST',body:{name:'SteveSewell',company:'Builder.io'}})简单安全。我喜欢。开源解决方案虽然定义我们自己的抽象很有趣,但让我们指出几个流行的开源项目如何自动为我们处理这些情况:"代码实际上按预期工作:constres=awaitaxios.post('/user',{name:'SteveSewell',company:'Builder.io'})Wretch同样,对于Wretch,大多数基本示例也可以作为expected:constres=awaitwretch('/user').post({name:'SteveSewell',company:'Builder.io'})(可选)使我们的包装类型Safety最后但并非最不重要,如果你想在fetch周围实现你自己的包装器,如果你正在使用它,我们至少要确保它是类型安全的TypeScript。这是我们的最终代码,包括类型定义:constisPlainObject=(value:unknown)=>value?.constructor===ObjectclassResponseErrorextendsError{response:Responseconstructor(message:string,res:Response){super(message)this.response=res}}导出异步函数myFetch(input:RequestInfo|URL,init?:RequestInit):Promise
