# axios {%hackmd BJrTq20hE %} https://github.com/axios/axios axios 是一個基於 Promise 的 HTTP 客戶端,專門為瀏覽器和 node.js 服務 ```javascript= <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> ``` ## NPM in Vue: axios外部套件: [https://www.npmjs.com/package/vue-axios](https://www.npmjs.com/package/vue-axios) 1. ```javascript= npm install --save axios vue-axios ``` 2. ```javascript= import axios from 'axios' import VueAxios from 'vue-axios' ``` 3. ```javascript= const app = Vue.createApp(...) app.use(VueAxios, axios) ``` ```javascript= //You can use axios like this(三種寫法): Vue.axios.get(api).then((response) => { console.log(response.data) }) this.axios.get(api).then((response) => { console.log(response.data) }) this.$http.get(api).then((response) => { console.log(response.data) }) ``` 參數設定方式: - 一次性設定: ```jsx= let config = { headers: { header1: value, } } let data = { 'field': value } axios.post(URL, data, config).then(...) ``` - 預設值方式(永久): ```jsx= axios.defaults.baseURL="http://192.168.1.1:5000"; axios.defaults.headers.common["Authorization"] = token; //把token放在表頭 axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; ``` ## API登入: ```javascript= const url = "https://vue3-course-api.hexschool.io/"; // 請加入站點 const path = "onedog"; // 請加入個人 API Path const user = { username, password, }; // #2 發送 API 至遠端並登入(並儲存 Token,就可以不用一直登入) //把token存在cookie: axios .post(`${url}/admin/signin`, user) //請求的方法,帳密資料會夾帶在config裡面 .then((res) => { const token = res.data.token; //取得token const expired = res.data.expired; //取得到期日 console.log(token, expired); document.cookie = `hexToken=${token}; expires=${new Date(expired)}`; //儲存token到cookie }); 登入後的回覆訊息: data: expired: 1621447084551 //登入到期日 message: "登入成功" success: true token: "eyJhbGciOiJSUzI1NiIs…" //憑證(有到期日) uid: "Fb7Xdv4DPmUpfjRxd9OsshuNrLI2" //登入者身分 ``` ## 確認登入(不用再登入一次): ```javascript= function checkLogin(params) { // #3 取得 Token(Token 僅需要設定一次) const token = document.cookie.replace( //從cookie裡面取得token,hexToken為自訂名稱 /(?:(?:^|.*;\s*)hexToken\s*\=\s*([^;]*).*$)|^.*$/, "$1" ); console.log(token); axios.defaults.headers.common["Authorization"] = token; //把token放在表頭 // #4 確認是否登入(確認的API) axios.post(`${url}/api/user/check`).then((res) => { console.log(res); }); } ``` ## 登出:(清除cookie) ```javascript= document.cookie = 'hexToken=;expires=;'; ``` ## 取得產品列表: ```javascript= function getProducts() { // #5 取得後台產品列表 axios.get(`${url}/api/${path}/admin/products`).then((res) => { console.log(res); }).catch((error) => console.log(error)); } ``` ## post新增物件:前為網址,後面接物件(需{}) ```javascript= axios .post(`${url}/api/${path}/admin/product`, { data: this.tempProducts }) .then((res) => { … }); ``` ## get網址帶參數: ```javascript= axios.get(`http://localhost:5000/api/tasks/${id}`, { params: { id: id } }) //or config形式 export function getAvailableHaoma(data) { return request({ url: '/sms/haoma/getAvailable', method: 'get', params: { mobile: data.code, projectId: data.favorite, cardType: data.cardType } }) } ``` ## API管理:當有多個API(10支以上)需要管理的時候 [Mike blog](https://medium.com/i-am-mike/%E4%BD%BF%E7%94%A8axios%E6%99%82%E4%BD%A0%E7%9A%84api%E9%83%BD%E6%80%8E%E9%BA%BC%E7%AE%A1%E7%90%86-557d88365619) - 實戰: [https://ithelp.ithome.com.tw/articles/10263166?sc=iThelpR](https://ithelp.ithome.com.tw/articles/10263166?sc=iThelpR) ```javascript= //在 api/index.js整合所有的api import axios from 'axios'; // User相關的 api const userRequest = axios.create({ baseURL: 'https://api/user/' }); // 文章相關的 api const articleRequest = axios.create({ baseURL: 'https://api/article/' }); // 搜尋相關的 api const searchRequest = axios.create({ baseURL: 'https://api/search/' }); // User 相關的 api export const apiUserLogin = data => userRequest.post('/signIn', data); export const apiUserLogout = data => userRequest.post('/signOut', data); export const apiUserSignUp = data => userRequest.post('/signUp', data); // 文章相關的 api export const apiArticleItem = () => articleRequest.get('/ArticleItem'); export const apiArticleMsg = data => articleRequest.post('/ArticleMsg', data); export const apiArticleLink = data => articleRequest.post('/ArticleLink', data); // 搜尋相關的 api export const apiSearch = data => searchRequest.get(`/Search?searchdata=${data}`); export const apiSearchType = () => searchRequest.get(`/SearchType`); ``` //要使用的時候只要import api這個資料夾就好了,可以確保你 API 來源都是同一個進入點 ```javascript= import { apiGetArticleItem, apiGetSearch } from "../api"; ``` ## async 寫法 - 使用 try catch ```jsx= getUsers() async function getUsers() { try { const post = await postUsers() const get = await axios.get('http://localhost:3000/users') const { data } = get // 資料在 data 屬性 console.dir(get) // 回傳類似 RequestObject console.table(data) } catch (error) { throw new Error(error) } } ``` ## axios 柯里化 - https://segmentfault.com/a/1190000042048395 ```jsx= // axios柯里化 function get(url) { return function(params) { return axios.get(url + params) .then(res => { console.log('res ', res.data) }) } } ``` 使用: ```jsx= const baseUrl = 'https://www.fastmock.site/mock/320dcea3c1fbc2abb8bc0f60c25569f7/api'; const getBlog = get(`${baseUrl}/blog/`) getBlog(1) ``` ## 上傳檔案 formData完整解說: [https://blog.kalan.dev/2021-03-13-form-and-form-data/](https://blog.kalan.dev/2021-03-13-form-and-form-data/) - 檔案上傳接api範例(官方文檔): [https://apidocs.imgur.com/#de179b6a-3eda-4406-a8d7-1fb06c17cb9c](https://apidocs.imgur.com/#de179b6a-3eda-4406-a8d7-1fb06c17cb9c) DEMO: [https://codesandbox.io/s/upload-h0xe3?file=/index.html:1120-1146](https://codesandbox.io/s/upload-h0xe3?file=/index.html:1120-1146) ```javascript= <input id="inputFile" type="file" name="file" ref="file"> <input id="inputSubmit" type="submit" value="上傳圖片"> <img id="image"> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> let file = []; let formData = new FormData(); //用JS模擬表單傳送資料時的物件 console.log(formData) const inputFile = document.getElementById('inputFile'); const image = document.getElementById('image'); const inputSubmit = document.getElementById('inputSubmit'); inputFile.addEventListener('change', handleFileUpload); inputSubmit.addEventListener('click', submitFile); function handleFileUpload() { console.log(inputFile) file = inputFile.files[0]; //要上傳的檔案 console.log(file) //formData.append('鍵', 值); formData.append('image', file); //required,image為欄位名稱 image.src = URL.createObjectURL(file); //產生預覽圖片網址 }; function submitFile (){ axios({ method: 'POST', url: 'https://api.imgur.com/3/image', data: formData, headers: { Authorization: "Client-ID 4ef4ca52de4b42c" }, mimeType: 'multipart/form-data' }).then(res => { console.log(res.data); // console.log(res.data.data.link); image.src = res.data.data.link; }).catch(e => { console.log(e) }) } </script> ``` imgbb版本: url:https://api.imgbb.com/1/upload?key=f99d4a2d3b49f26b811466c87b55e2ba - base64轉換formData:[https://www.itread01.com/article/1477278294.html](https://www.itread01.com/article/1477278294.html) - 六角的img API: ```javascript= const fileInput = document.querySelector("#file"); fileInput.addEventListener("change", upload); function upload() { // #1 撰寫上傳的 API 事件 // console.dir(fileInput); const file = fileInput.files[0]; //圖片存放位置 const formData = new FormData(); //用傳統表單的形式發送 formData.append("file-to-upload", file); //在formData新增一個欄位(需對應api需求格式) axios.post(`${url}/api/${path}/admin/upload`, formData) .then((res) => { console.log(res); }); } ``` # node-fetch `npm i node-fetch` [node-fetch](https://www.npmjs.com/package/node-fetch) 使用: - Get: ```javascript= import fetch from 'node-fetch'; const response = await fetch('https://api.github.com/users/github'); const data = await response.json(); console.log(data); ``` 網址參數轉post: ```javascript= import fetch from 'node-fetch'; const params = new URLSearchParams(); params.append('a', 1); const response = await fetch('https://httpbin.org/post', {method: 'POST', body: params}); const data = await response.json(); console.log(data); ``` - Post: ```javascript= import fetch from 'node-fetch'; const body = {a: 1}; const response = await fetch('https://httpbin.org/post', { method: 'post', body: JSON.stringify(body), headers: {'Content-Type': 'application/json'} }); const data = await response.json(); console.log(data); ``` # 原生fetch ## get: - 階段一: ==需先將資料轉成特定格式回傳給階段二==才能使用,例如: - `response.json()`:把資料轉成JSON格式 - `response.text()`:把資料轉成text格式(變成純字串) - `response.blob()`:把資料轉成Blob物件 - `response.formData()`:把資料轉成FormData物件 - `response.arrayBuffer()`:把資料轉成二進制數組 ```javascript= fetch('https://randomuser.me/api/') .then((response) => { return response.json(); // 階段一 }) .then( (response) => { console.log(response); // 階段二 }) .catch((error) => { console.log(`Error: ${error}`); }) ``` 使用`await`改寫: ```javascript= let getJSON = async(url) => { let response = await fetch(url); let JSON = await response.json(); console.log(JSON) } // 上面可再簡化為: const async response = await (await fetch(url)).json(); ``` ## 建立連結:`URL.createObjectURL` ### 建立空檔案 - ```jsx= const emptyFile = new File([], "null.pdf", { type: "application/pdf" }); ``` ### 圖片預覽 - 建立圖片預覽網址 [ink](https://blog.csdn.net/weixin_48353638/article/details/130153825) ```jsx= <input type="file" onchange="fileSelected">upload</input> const fileSelected = (e) => { picPreview.value = URL.createObjectURL(new Blob([e], { type: e.type })); }; ``` ### 下載文件 - Excel 文件:[link](https://happy9990929.github.io/2022/12/22/web-excel/) 如果用 axios 打完 api 要用 new Blob 來轉換成 blob 物件,但如果出現檔案毀損狀況,可用原生 fetch 的 `.blob()` 方法。 [文件處理工具](https://dyb881.github.io/seasoning/common/tools/file-tool) (參考函式庫原始碼[github](https://github.com/dyb881/seasoning/blob/master/src/file-tool/index.ts)) ```jsx= const url = `${ window.__env[process.env.NODE_ENV]["SYS"] }api/ENRA4001/postExport`; const data = { method: "post", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ acadYear: search.value.enrollYear, }), responseType: "blob", }; const res = await fetch(url, data); const blob = await res.blob(); // 轉成 blob 物件 const createDownloadAction = async (blobObject, filename = 'file.pdf') => { // 轉成下載連結 const href = URL.createObjectURL(blobObject); // 建立 a 的元素並點擊來觸發下載 const link = document.createElement("a"); link.href = href; link.download = filename; document.body.appendChild(link); link.click(); // 清除臨時建立的 元素與link物件 document.body.removeChild(link); URL.revokeObjectURL(href); // 清除記憶體暫存 } ``` ## post 當發出POST請求時,就需要用到`fetch`裏的第二個参數,包括: - method (HTTP請求方法) - request body (請求主體),可傳三種結構: - 字串(用JSON.stringify轉成的字串) - FormData物件 - Blob所傳送的二進制資料 - ```javascript= const headers = { "Content-Type": "application/json", "Accept": "application/json", "Authorization": `Bearer ${token}`, } //以下是API文件中提及必寫的主體参數。而以下這個產品資料是六角學院提供的。 const body = { "title": "Abysswalker", "category": "T-Shirts", "content": "Its wearer, like Artorias himself, can traverse the Abyss.", "description": "This official Dark Souls shirt was designed by Nina Matsumoto and printed on soft 100% cotton shirts by Forward. Each one comes with a bonus sticker.", "imageUrl": ["https://images.unsplash.com/photo-1529374255404-311a2a4f1fd9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1349&q=80"] } fetch(url, { method: "POST", headers: headers, //別忘了把主體参數轉成字串,否則資料會變成[object Object],它無法被成功儲存在後台 body: JSON.stringify(body) }) .then(response => response.json()) .then(json => console.log(json)); ``` ⭐==注意==:上傳文件時不要加上 [Content-Type](https://muffinman.io/blog/uploading-files-using-fetch-multipart-form-data/) ## 把 fetch 抽成共用 ```jsx= export async function oApi(method = 'get', url = '', data = null, config = {}) { const baseURL = config.station ? baseUrlSet[config.station] : baseUrlSet['SYS']; const defaultConfig = { headers: { programno: store.state.programAuth.programNo, "user-token": store.state.auth.token, "Content-Type": "application/json; charset=utf-8" }, responseType: 'json' }; if (config.isUpload) { // 如果要上傳檔案要拿掉 Content-Type delete defaultConfig.headers['Content-Type'] } else { data = JSON.stringify(data) } if (!_isEmpty(config.headers)) { const { headers, ...otherConfig } = defaultConfig config = { headers: { ...defaultConfig.headers, ...config.headers }, ...otherConfig } } else { config = { ...defaultConfig, ...config } } const res = await fetch(baseURL + url, { method: method, body: data, ...config }) switch (config.responseType) { case 'blob': return res.blob(); // return { data: await res.blob() }; case 'formData': return { data: await res.formData() }; default: return { data: await res.json() }; } } ``` # TanStack Query 可快取API https://tanstack.com/query/latest/docs/vue/overview