一文詳解JavaScript如何安全的進(jìn)行數(shù)據(jù)獲取
獲取數(shù)據(jù)的方式
fetch可能是我們?cè)?JavaScript 獲取數(shù)據(jù)最常見(jiàn)的方式。
但是,我們用fetch獲取數(shù)據(jù)的的代碼很有可能存在安全問(wèn)題:
代碼示例:
const res = await fetch('/user')
const user = await res.json()
上面這段代碼雖然簡(jiǎn)單好用,但存在許很多問(wèn)題。
- 問(wèn)題一、缺少“錯(cuò)誤處理”
當(dāng)然你可能會(huì)說(shuō)這個(gè)問(wèn)題很好解決嘛,給她添加一個(gè)try/catch就好了,遇到錯(cuò)誤就會(huì)拋出了嘛!
代碼示例:
try {
const res = await fetch('/user')
const user = await res.json()
} catch (err) {
// 處理錯(cuò)誤的代碼
}
當(dāng)然,這樣可以確實(shí)可以對(duì)我們的錯(cuò)誤進(jìn)行處理了。遇到錯(cuò)誤的時(shí)候也會(huì)拋出,但是就算這樣寫(xiě)了還是存在很多的問(wèn)題,對(duì)錯(cuò)誤的覆蓋能力不全面。
- 問(wèn)題二:無(wú)法識(shí)別部分錯(cuò)誤代碼
在這里,我們假設(shè)user實(shí)際上是一個(gè)用戶對(duì)象。我們假設(shè)我們得到了響應(yīng)200。
但是fetch不會(huì)針對(duì)非 200 的狀態(tài)拋出錯(cuò)誤,因此如果你收到了400(錯(cuò)誤請(qǐng)求)、401(未授權(quán))、404(未找到)、500(內(nèi)部服務(wù)器錯(cuò)誤)或各種其他問(wèn)題都不會(huì)進(jìn)行錯(cuò)誤拋出。
那你可能有會(huì)說(shuō)了那我們用個(gè)if進(jìn)行判斷然后對(duì)不同的錯(cuò)誤碼進(jìn)行分類(lèi)處理不就好了!
于是我們就有了下面的代碼
try {
const res = await fetch('/user')
if (!res.ok) {
switch (res.status) {
case 400: /* 錯(cuò)誤處理 */ break
case 401: /* 錯(cuò)誤處理 */ break
case 404: /* 錯(cuò)誤處理 */ break
case 500: /*錯(cuò)誤處理 */ break
}
}
// User 已經(jīng)是我們最新的數(shù)據(jù)了
const user = await res.json()
} catch (err) {
// 錯(cuò)誤處理
}
現(xiàn)在,我們算是基本實(shí)現(xiàn)了fetch對(duì)數(shù)據(jù)的安全獲取了. 但是這樣寫(xiě)是很臃腫且笨重的,因?yàn)槊看味急仨氈貙?xiě)寫(xiě)一次錯(cuò)誤的處理邏輯,而如果是團(tuán)隊(duì)開(kāi)發(fā)的話對(duì)每個(gè)成員的要求會(huì)更高,要求每個(gè)同事都要按照同樣的邏輯來(lái)處理請(qǐng)求。而且在可讀性方面,也是很差的,維護(hù)起來(lái)很麻煩。
那么我們可不可以換一種更優(yōu)雅的方式來(lái)處理我們的邏輯代碼呢?
更優(yōu)雅的方式
我們可以使用throw來(lái)處理我們的不同的錯(cuò)誤響,而不是使用switch/case.
try {
const res = await fetch('/user')
if (!res.ok) {
throw new Error('錯(cuò)誤的響應(yīng)')
}
const user = await res.json()
} catch (err) {
// 錯(cuò)誤處理
}
但是我們還剩下最后一個(gè)問(wèn)題——就是當(dāng)我們需要處理錯(cuò)誤時(shí),我們丟失了很多有用的上下文。我們無(wú)法在 catch 塊中訪問(wèn),因此查看處理錯(cuò)誤時(shí)我們上并不知道響應(yīng)的狀態(tài)代碼或錯(cuò)誤的詳細(xì)信息。
這會(huì)讓我們debug變的很困難,很難去查錯(cuò)。那我們要怎么才能拿到error的上下文呢?
最好的方法可能是創(chuàng)建我們自己的自定義錯(cuò)誤類(lèi),并且在錯(cuò)誤類(lèi)中轉(zhuǎn)發(fā)響應(yīng)的詳細(xì)信息:
代碼:
class ResponseError extends Error {
constructor(message, res) {
super(message)
this.response = res
}
}
try {
const res = await fetch('/user')
if (!res.ok) {
throw new ResponseError('錯(cuò)誤的響應(yīng)信息(error的上下文信息)', res)
}
const user = await res.json()
} catch (err) {
//我們可以拿到錯(cuò)誤的詳細(xì)信息,也就是error的上下文
switch (err.response.status) {
case 400: /* 錯(cuò)誤處理 */ break
case 401: /* 錯(cuò)誤處理 */ break
case 404: /* 錯(cuò)誤處理 */ break
case 500: /* 錯(cuò)誤處理 */ break
}
}
現(xiàn)在我們保留狀態(tài)代碼等error信息,這樣可以讓我們的用戶了解錯(cuò)誤的原因的也能讓我們更好的處理錯(cuò)誤。
例如,我們可以提醒用戶500我們遇到了問(wèn)題,并可以讓客戶聯(lián)系我們的進(jìn)行解決。
或者如果狀態(tài)為401,則他們當(dāng)前未授權(quán),可能需要重新登錄等。
封裝類(lèi)
雖然上面的代碼可以解決我們的所有問(wèn)題,但是它仍然存在一個(gè)不穩(wěn)定性,就是代碼的健壯性取決于開(kāi)發(fā)人員的個(gè)人素質(zhì)和能力。我們的的請(qǐng)求安全并不能等到統(tǒng)一的保障。
我們可以對(duì)我們代碼進(jìn)行封裝,然后使用時(shí)進(jìn)行導(dǎo)出引用就行了
class ResponseError extends Error {
constructor(message, res) {
this.response = res
}
}
export async function myFetch(...options) {
const res = await fetch(...options)
if (!res.ok) {
throw new ResponseError('錯(cuò)誤響應(yīng)的信息', res)
}
return res
}
然后我們可以按下面的方式去使用它:
try {
const res = await myFetch('/user')
const user = await res.json()
} catch (err) {
// 錯(cuò)誤的處理代碼
}
在我們的封裝代碼中,最好確保有一個(gè)統(tǒng)一的方式來(lái)處理錯(cuò)誤。因?yàn)檫@里面包括給用戶的警報(bào)、日志記錄等。
開(kāi)源的解決方案
當(dāng)然如果我們的水平還沒(méi)有達(dá)到可以自己封裝一個(gè)完善的請(qǐng)求類(lèi)時(shí)我們也可以去網(wǎng)上使用一些別封裝好的請(qǐng)求類(lèi),
axios
- axios是一個(gè)非常流行的 JS 請(qǐng)求數(shù)據(jù)的庫(kù),它已經(jīng)幫我們解決了上面我們探討的幾個(gè)問(wèn)題。
try {
const { data } = await axios.get('/user')
} catch (err) {
// 錯(cuò)誤處理代碼
}
我覺(jué)得 Axios 的唯一缺點(diǎn)是包太大,如果我們只是在一個(gè)項(xiàng)目獲取一個(gè)很簡(jiǎn)單的數(shù)據(jù)時(shí)使用axios需要引入一個(gè)11kb的包,,反而會(huì)使我們的項(xiàng)目變的臃腫。
Redaxios
如果你覺(jué)得項(xiàng)目的大小對(duì)你更重要是你可以選擇Redaxios
- Redaxios使用有 Axios 一樣的 API,但不到大小卻不到[1kb]
import axios from 'redaxios' // use as you would normally
Wretch
還有一個(gè)不錯(cuò)的選項(xiàng)是Wretch,它是 Fetch封裝成的一個(gè)非常小的包,和 Redaxios 一樣。Wretch 的特別之處在于它在很大程度上還原了原生的數(shù)據(jù)請(qǐng)求方法,但是它幫我們封裝了很多的錯(cuò)誤處理代碼。
const user = await wretch("/user")
.get()
// Handle error cases in a more human-readable way
.notFound(error => { /* ... */ })
.unauthorized(error => { /* ... */ })
.error(418, error => { /* ... */ })
.res(response => /* ... */)
.catch(error => { /* uncaught errors */ })以上就是一文詳解在JavaScript中如何安全的進(jìn)行數(shù)據(jù)獲取的詳細(xì)內(nèi)容,更多關(guān)于JavaScript數(shù)據(jù)安全獲取的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Javascript 靜態(tài)頁(yè)面實(shí)現(xiàn)隨機(jī)顯示廣告的辦法
最近在做私服發(fā)布站時(shí),客戶要求實(shí)現(xiàn)廣告隨機(jī)排序,而且要求在html頁(yè)面實(shí)現(xiàn),也就是說(shuō)必須使用javascript來(lái)完成了。2010-11-11
javascript 中模板方法單例的實(shí)現(xiàn)方法
這篇文章主要介紹了javascript 中模板方法單例的實(shí)現(xiàn)方法的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-10-10
Firefox和IE瀏覽器兼容JS腳本寫(xiě)法小結(jié)
window.event兼容腳本 1.2.屏蔽Form提交事件 3.獲取事件源 4.添加事件兼容寫(xiě)法 5.Firefox注冊(cè)innerText寫(xiě)法 6.長(zhǎng)度 7.父控件下的子控件 8.XmlHttp2008-07-07
js 動(dòng)態(tài)生成html 觸發(fā)事件傳參字符轉(zhuǎn)義的實(shí)例
下面小編就為大家?guī)?lái)一篇js 動(dòng)態(tài)生成html 觸發(fā)事件傳參字符轉(zhuǎn)義的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02
js實(shí)現(xiàn)倒計(jì)時(shí)秒殺效果
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)倒計(jì)時(shí)秒殺效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12

