<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
RESTFUL API 通常提供在不同實體上執行增刪改查(CRUD)操作的一組介面。我們通常在我們的前端專案中為這些每一個介面提供一個函數,這些函數的功能非常的相似,只是為了服務於不用的實體。舉個例子,假設我們有這些函數。
// api/users.js // 建立 export function createUser(userFormValues) { return fetch('users', { method: 'POST', body: userFormValues }); } // 查詢 export function getListOfUsers(keyword) { return fetch(`/users?keyword=${keyword}`); } export function getUser(id) { return fetch(`/users/${id}`); } // 更新 export updateUser(id, userFormValues) { return fetch(`/users/${is}`, { method: 'PUT', body: userFormValues }); } // 刪除 export function removeUser(id) { return fetch(`/users/${id}`, { method: 'DELETE' }); }
類似的功能可能存在於其他實體,例如:城市、產品、類別...但是我們可以用一個簡單的函數呼叫來代替這些函數:
// apis/users.js export const users = crudBuilder('/users'); // apis/cities.js export const cities = crudBuilder('/regions/cities');
然後像這樣去使用:
users.create(values); users.show(1); users.list('john'); users.update(values); users.remove(1);
getListOfUsers
, getCities
, getAllProducts
, productIndex
, fetchCategories
等, 他們都在做相同的事情,那就是“獲取實體列表”。使用這種方法,你將始終擁有entityName.list()
函數,並且團隊中的每個人都知道這一點。所以,讓我們建立crudBuilder()
函數,然後再新增一些糖。
對於上邊的簡單範例,crudBuilder()
函數將非常簡單:
export function crudBuilder(baseRoute) { function list(keyword) { return fetch(`${baseRoute}?keyword=${keyword}`); } function show(id) { return fetch(`${baseRoute}/${id}`); } function create(formValues) { return fetch(baseRoute, { method: 'POST', body: formValues }); } function update(id, formValues) { return fetch(`${baseRoute}/${id}`, { method: 'PUT', body: formValues }); } function remove(id) { return fetch(`${baseRoute}/${id}`, { method: 'DELETE' }); } return { list, show, create, update, remove }; }
假設約定 API 路徑並且給相應實體提供一個路徑字首,他將返回該實體上呼叫 CRUD 操作所需的所有方法。
但老實說,我們知道現實世界的應用程式並不會那麼簡單。在將這種方法應用於我們的專案時,有很多事情需要考慮:
formValues
物件在傳送給 API 之前需要做一些準備工作${baseRoute}/${id}
因此,我們需要可以處理更多複雜場景的 CRUD 構造器。
讓我們通過上述方法來構建一些日常中我們真正使用的東西。
首先,我們應該在 list
輸出函數中處理更加複雜的過濾。每個實體列表可能有不同的過濾器並且使用者可能應用了其中的一些過濾器。因此,我們不能對應用過濾器的形狀和值有任何假設,但是我們可以假設任何列表過濾都可以產生一個物件,該物件為不同的過濾器名稱指定了一些值。例如,我們可以過濾一些使用者:
const filters = { keyword: 'john', createdAt: new Date('2020-02-10') };
另一方面,我們不知道這些過濾器應該如何傳遞給 API,但是我們可以假設(跟 API 提供方進行約定)每一個過濾器在列表 API 中都有一個相應的引數,可以以'key=value'
URL 查詢引數的形式被傳遞。
因此我們需要知道如何將應用的過濾器轉換成相對應的 API 引數來建立我們的 list
函數。這可以通過將 transformFilters
引數傳遞給 crudBuilder()
來完成。舉一個使用者的例子:
function transformUserFilters(filters) { const params = []; if (filters.keyword) { params.push(`keyword=${filters.keyword}`); } if (filters.createdAt) { params.push(`create_at=${dateUtility.format(filters.createdAt)}`); } return params; }
現在我們可以使用這個引數來建立 list
函數了。
export function crudBuilder(baseRoute, transformFilters) { function list(filters) { let params = transformFilters(filters)?.join('&'); if (params) { params += '?'; } return fetch(`${baseRoute}${params}`); } }
從 API 接收的資料可能需要進行一些轉換才能在我們的應用程式中使用。例如,我們可能需要將 snake_case
轉換成駝峰命名或將一些日期字串轉換成使用者時區。
此外,我們還需要處理分頁。
我們假設來自 API 的分頁資料都按照如下格式(與 API 提供者約定):
{ data: [], // 實體物件列表 pagination: {...} // 分頁資訊 }
因此,我們需要知道如何轉換單個實體物件。然後我們可以遍歷列表物件來轉換他們。為此,我們需要一個 transformEntity
函數作為 crudBuilder
的引數。
export function crudBuilder(baseRoute, transformFilters, transformEntity, ) { function list(filters) { const params = transformFilters(filters)?.join('&'); return fetch(`${baseRoute}?${params}`) .then((res) => res.json()) .then((res) => ({ data: res.data.map((entity) => transformEntity(entity)), pagination: res.pagination })); } }
list()
函數我們就完成了。
對於 create
和 update
函數,我們需要將 formValues
轉換成 API 需要的格式。例如,假設我們在表單中有一個 City
的城市選擇物件。但是 create
API 只需要 city_id
。因此,我們需要一個執行以下操作的函數:
const prepareValue = formValue => ({city_id: formValues.city.id});
這個函數會根據用例返回普通物件或者 FormData
,並且可以將資料傳遞給 API:
export function crudBuilder(baseRoute, transformFilters, transformEntity, prepareFormValues) { function create(formValues) { return fetch(baseRoute, { method: 'POST', body: prepareFormValues(formValues) }); } }
在一些少數情況下,對實體執行某些操作的 API 介面不遵循相同的約定。例如,我們不能使用 /users/${id}
來編輯使用者,而是使用 /edit-user/${id}
。對於這些情況,我們應該指定一個自定義路徑。
在這裡我們允許覆蓋 crud builder 中使用的任何路徑。注意,展示、更新、移除操作的路徑可能取決於具體實體物件的資訊,因此我們必須使用函數並傳遞實體物件來獲取路徑。
我們需要在物件中獲取這些自定義路徑,如果沒有指定,就退回到預設路徑。像這樣:
const paths = { list: 'list-of-users', show: (userId) => `users/with/id/${userId}`, create: 'users/new', update: (user) => `users/update/${user.id}`, remove: (user) => `delete-user/${user.id}` };
這是建立 CRUD 函數的最終程式碼。
export function crudBuilder(baseRoute, transformFilters, transformEntity, prepareFormValues, paths) { function list (filters) { const path = paths.list || baseRoute; let params = transformFilters(filters)?.join('&'); if (params) { params += '?'; } return fetch(`${path}${params}`) .then((res) => res.json()) .then(() => ({ data: res.data.map(entity => transformEntity(entity)), pagination: res.pagination })); } function show(id) { const path = paths.show?.(id) || `${baseRoute}/${id}`; return fetch(path) .then((res) => res.json()) .then((res => transformEntity(res))); } function create(formValues) { const path = paths.create || baseRoute; return fetch(path, { method: 'POST', body: prepareFormValues(formValues) }); } function update(id, formValues) { const path = paths.update?.(id) || `${baseRoute}/${id}`; return fetch(path, { method: 'PUT', body: formValues }); } function remove(id) { const path = paths.remove?.(id) || `${baseRoute}/${id}`; return fetch(path, { method: 'DELETE' }); } return { list, show, create, update, remove } }
Saeed Mosavat: Stop writing API functions
以上就是停止編寫 API函數原因範例分析的詳細內容,更多關於停止編寫 API 函數的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45