# 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