1. 程式人生 > >[Vue 牛刀小試]:第十五章 - 傳統開發模式下的 axios 使用入門

[Vue 牛刀小試]:第十五章 - 傳統開發模式下的 axios 使用入門

 一、前言

  在沒有接觸 React、Angular、Vue 這類 MVVM 的前端框架之前,無法拋棄 Jquery 的重要理由,除了優秀的前端 DOM 元素操作性以外,能夠非常便捷的發起 http 請求也佔有非常重要的地位。

  既然我們已經開始使用 Vue 進行前端開發,拋棄了對頁面 DOM 元素的操作,難道,為了方便的發起 http 請求,還需要在專案中載入 jquery 或者是手動建立 http 請求嗎?

  答案當然是不用的,作為目前主流的前端框架,開發者、社群早已經為我們提供好了解決方案。隨著 Vue 作者尤雨溪宣佈不再維護 vue-resource,轉而推薦大家使用 axios,目前在 Vue 社群中 axios 開始佔據 http 庫的主導地位,所以這一章我們就介紹下如何使用 axios 發起 http 請求。

  學習系列目錄地址:https://www.cnblogs.com/danvic712/p/9549100.html

  倉儲地址(前端):https://github.com/Lanesra712/VueTrial/blob/master/chapter02-bronze/front/axios.html

  倉儲地址(後端):https://github.com/Lanesra712/VueTrial/tree/master/chapter02-bronze/rear/Sample

 二、乾貨合集

  axios 是一個基於 Promise 的 http 客戶端,可以用於瀏覽器和 node.js。官方文件對於 axios 庫的使用方法已經寫的很清楚了,所以這裡只介紹如何與 Vue 進行結合,從而使用 axios 發起 http 請求。

  這一章會涉及到簡單的前後端的資料互動,作為一名 .NETer,本篇文章將採用 ASP.NET Core Web API 作為後端服務,你可以根據自己的喜好進行選擇。當然,如果你之前並沒有接觸過後端,不知道怎麼選擇的話,推薦你嘗試 .NET Core,示例的後端專案我也會同步放在 Github 上。

   PS:在後端模板專案上,我會新增對於 Swagger API 文件的支援,以及在後端程式中進行配置跨域請求,從而允許後端介面可以接受跨域請求的訪問。這些內容並不會在本篇文章中展現,如果你需要詳細瞭解,你可以檢視另一個系列的文章(ASP.NET Core 專案實戰)那裡會介紹一些關於 ASP.NET Core 專案的開發,兩個系列相輔相成,後續所有涉及到前後端的互動開發,全部在此係列(ASP.NET Core 專案實戰)中進行呈現。

  1、載入

  同目前的前端發展方向相同,axios 可以從 npm、yarn、bower 這種前端包管理工具中進行下載,同時,也提供了 cdn 連結,方便我們在傳統的專案中進行使用。

// 使用 npm 載入
npm install axios

// 使用 bower 載入
bower install axios

// 使用 yarn 載入
yarn add axios

  在這篇文章中,我還是與之前一樣,採用下載原始檔的方式進行使用,在後續的前端框架搭建完成後再改用前端包管理工具進行載入。

// 使用 cdn 載入
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

  2、get & post

  相比於 put 請求和 delete 請求,get 請求和 post 請求是我們最常用的兩個方法,一個很常見的使用場景,我們通過 get 請求來搜尋資料,通過 post 請求來提交資料。

  在示例的後端介面中,提供了五個介面方法,分別對應了 get、post、put、delete 這四個 HTTP 謂詞。put 和 delete 對應的介面方法這裡並沒有實現,這篇文章主要使用的是 get 和 post 謂詞對應的三個介面。

  2.1、獲取所有的使用者資料(/api/user)

  get 請求,載入全部的使用者資料,按照建立時間進行降序排列。

  2.2、根據搜尋條件搜尋使用者資料(/api/user/query)

  get 請求,根據使用者輸入框輸入的資料,從全部的使用者資料中查找出符合條件的資料,因為這裡會存在多個查詢條件,其實並不太符合 Restful 的介面設計,所以這裡我採用 ASP.NET Core 中的特性路由的方式,指定此路由為專門的資料查詢介面。

  2.3、新增使用者資料(/api/user)

  post 請求,提交一條新的使用者資料,因為是採用 Restful 風格的介面設計,所以請求的地址與獲取所有的使用者資料相同,僅僅是 http 謂詞的不同。

  最終實現的前端頁面如下所示,頁面第一次載入時會載入全部的使用者資料;當用戶點選搜尋按鈕時,會根據頂部的三個輸入框中的值,從使用者資料中進行篩選;當點選新增按鈕時,則會根據 Name 和 Email 輸入框中的值新增一條新的使用者資料。

  首先我們需要在頁面載入的時候請求後端介面,去獲取我們的使用者資料,這裡我們在 Vue 例項的 methods 中定義一個 getList 方法,在這個方法中我們去請求後端介面。

  在之前學習 Vue 的生命週期鉤子函式時我們瞭解到,在 created 鉤子函式中,對於 Vue 例項的 data 和 methods 已經初始化完成,此時,整個 Vue 例項已經初始化完成。但是,初始化完成的 Vue 例項沒有與 DOM 進行繫結。所以,如果我們想要在頁面初始載入時就渲染出整個使用者資訊表格,created 函式是能夠呼叫 getList 方法最早的一個鉤子函式。

  在 axios 中,我們發起一個 http 請求後,在 then 回掉方法中進行請求成功後的資料處理,在 catch 回掉方法中捕獲請求失敗的資訊。這裡的 then 方法就相當於我們在 Jquery 中使用 ajax 時的 success 回撥方法,而 catch 方法則是 error 回撥。

axios.get('http://localhost:5000/api/user')
    .then(function (response) {
        console.log(response)
    }).catch(function (error) {
        console.log(error)
    })

  從介面打印出的返回結果可以看到,介面返回的 response 中包含了五部分的資訊。這裡 data 屬性顯示的就是整個的使用者資料集合,在實際使用中,你需要與 http 響應狀態碼進行結合,考慮如果後端出現錯誤如何使前端知曉,從而相對友好的通知使用者。

{
  // 後端介面返回的資料
  data: {},

  // 服務端介面返回的 HTTP 狀態碼
  status: 200,

  // 服務端介面返回的 HTTP 狀態資訊
  statusText: 'OK',

  // 後端介面返回的響應 header 資訊
  headers: {},

  // axios 發起的介面請求時的配置資訊
  config: {},

  // 介面響應的請求資訊
  request: {}
}

  針對 axios 發起請求時的配置資訊,我們可以自己進行配置。例如我們可以設定請求的介面域名是什麼,設定 post 請求時的 Content-Type,或者針對前後端資料互動時經常使用的 Jwt Token 驗證,我們可以在請求的 header 中新增 token 資訊,從而通過後端的許可權驗證。

axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

  當我們獲取到所有的使用者資料後,我們就可以將結果值賦值給我們 Vue 例項中的 users 資料集合物件(這個 users 需要你事先在 Vue 例項的 data 中進行提前定義好)。

axios.get('http://localhost:5000/api/user')
    .then(function (response) {
        console.log(response)
        this.users = response.data
    }).catch(function (error) {
        console.log(error)
    })

  如果你按照上面的寫法進行賦值,你會發現頁面上並沒有按照我們的想法渲染出資料。既然我們已經獲取到了後端介面返回的資料值,那麼這裡的問題就可能出現在賦值這上面。我們可以在 then 回撥中列印 this.users 看看。

  可以看到,這裡的 this 指向的其實是瀏覽器的 window 物件,因此我們給 this.users 賦值最終是賦值到 window 物件上了。因為是 this 指向出了問題,所以這裡我就直接採用箭頭函式的方式進行資料賦值,最終的實現程式碼如下所示。

var vm = new Vue({
    el: '#app',
    data: {
        id: '',
        name: '',
        email: '',
        users: []
    },
    created() {
        this.getList()
    },
    methods: {
        getList() {
            axios.get('http://localhost:5000/api/user')
                .then(response => {
                    this.users = response.data
                }).catch(error => {
                    console.log(error)
                })
        }
    },
});

  搜尋按鈕的功能與獲取所有使用者資訊的程式碼比較相似,這裡我們需要將搜尋的引數新增到 get 請求中。從下圖的瀏覽器控制檯中可以看到,當點選查詢按鈕之後,我們新增的引數會以 query 查詢字串的方式新增到請求的 url 地址上。

var vm = new Vue({
    el: '#app',
    data: {
        id: '',
        name: '',
        email: '',
        users: []
    },
    methods: {
        search() {
            axios.get('http://localhost:5000/api/user/query', {
                params: {
                    id: this.id,
                    name: this.name,
                    email: this.email,
                }
            }).then(response => {
                this.users = response.data
            }).catch(error => {
                console.log(error)
            })
        }
    },
});

  與 get 請求相似,使用 axios 發起 post 請求也是在 then 回掉方法中獲取介面返回值,在 catch 回掉方法中捕獲錯誤資訊。

var vm = new Vue({
    el: '#app',
    data: {
        id: '',
        name: '',
        email: '',
        users: []
    },
    methods: {
        getList() {
            axios.get('http://localhost:5000/api/user')
                .then(response => {
                    this.users = response.data
                }).catch(error => {
                    console.log(error)
                })
        },
        add() {
            axios.post('http://localhost:5000/api/user', {
                name: this.name,
                email: this.email,
            }).then(response => {
                console.log(response)
                this.getList()
            }).catch(error => {
                console.log(error)
            })
        }
    },
});

  就像我們使用 jquery 發起 ajax 請求一樣,我們可以使用 $.ajax/$.post 方法去發起一個 get/post 請求,也可以在 $.ajax 方法中通過指定請求的 type 型別來確定我們是以 get 請求還是 post 請求的方式執行,在 axios 中也提供了相似的功能。

// get 請求
axios({
  method: 'get',
  url: 'http://localhost:5000/api/user'
})

// post 請求
axios({
  method: 'post',
  url: 'http://localhost:5000/api/user',
  data: {
    name: this.name,
    email: this.email,
  }
}); 

  整個前端頁面完整的示例程式碼以及實現的效果如下所示。

<div id="app">
    <div class="card border-info mb-3" style="margin-top: 20px;">
        <div class="card-header text-info">
            <b>使用者資訊</b>
        </div>
        <div class="card-body text-info form-inline">
            <div class="form-row">
                <div class="form-group">
                    <div class="input-group mb-2 mr-sm-2">
                        <div class="input-group-prepend">
                            <div class="input-group-text text-info"> Id </div>
                        </div>
                        <input type="text" class="form-control" id="id" v-model="id" autocomplete="off">
                    </div>
                </div>
                <div class="form-group">
                    <div class="input-group mb-2 mr-sm-2">
                        <div class="input-group-prepend">
                            <div class="input-group-text text-info"> Name </div>
                        </div>
                        <input type="text" class="form-control" id="name" v-model="name" autocomplete="off">
                    </div>
                </div>
                <div class="form-group">
                    <div class="input-group mb-2 mr-sm-2">
                        <div class="input-group-prepend">
                            <div class="input-group-text text-info"> Email </div>
                        </div>
                        <input type="email" class="form-control" id="email" v-model="email" autocomplete="off">
                    </div>
                </div>
                <div class="form-group">
                    <a class="btn btn-info" href="#" role="button" @click="search">搜尋</a>
                    <a class="btn btn-success" href="#" role="button" @click="add">新增</a>
                </div>
            </div>

        </div>
    </div>

    <table class="table table-striped table-bordered table-hover text-info">
        <thead class="thead-inverse">
            <tr>
                <th>Id</th>
                <th>Name</th>
                <th>Email</th>
                <th>Created On</th>
            </tr>
        </thead>
        <tbody>
            <tr v-for="item in users" :key="item.id">
                <td scope="row">{{item.id}}</td>
                <td>{{item.name}}</td>
                <td>{{item.email}}</td>
                <td>{{item.createdTime}}</td>
            </tr>
        </tbody>
    </table>

</div>

<script>
    var vm = new Vue({
        el: '#app',
        data: {
            id: '',
            name: '',
            email: '',
            users: []
        },
        created() {
            this.getList()
        },
        methods: {
            getList() {
                axios.get('http://localhost:5000/api/user')
                    .then(response => {
                        this.users = response.data
                    }).catch(error => {
                        console.log(error)
                    })
            },
            search() {
                axios.get('http://localhost:5000/api/user/query', {
                    params: {
                        id: this.id,
                        name: this.name,
                        email: this.email,
                    }
                }).then(response => {
                    this.users = response.data
                }).catch(error => {
                    console.log(error)
                })
            },
            add() {
                axios.post('http://localhost:5000/api/user', {
                    name: this.name,
                    email: this.email,
                }).then(response => {
                    console.log(response)
                    this.getList()
                }).catch(error => {
                    console.log(error)
                })
            }
        },
    });
</script>

  3、攔截器

  在前後端分離的專案中,我們一般採用 Jwt token 的方式進行許可權控制。前端在獲取資料之前,需要從後端獲取到 token 令牌。當前端獲取到後端回傳的 token 資訊後,我們需要將此 token 資訊儲存下來,此後所有的請求都需要在請求的 header 資訊中新增此 token 資訊。那麼,能不能有一種方式可以在觸發後端驗證之前,統一的進行 token 資訊校驗,當判斷沒有包含 token 資訊之後,前端直接跳轉到登入頁面。

  在 axios 中,我們可以將此類操作放置到攔截器中。你可以將 axios 中的攔截器看成是 ASP.NET Core 中的 Filters 過濾器,例如,這裡的需求,我們完全可以將獲取到的 token 資訊置於 request 請求攔截器中,在發起的每一次 http 請求時去校驗是否包含 token 資訊,當沒有包含 token 資訊時,就可以直接跳轉到登入頁面。

  這裡因為我並沒有實現後端 token 驗證,所以這裡就只是進行一個演示,你可以從瀏覽器的控制檯中看到只要我們發起一個 http 請求,就會輸出的我們列印的資訊。

// request 請求攔截
axios.interceptors.request.use(function (request) {
    // 對 request 進行攔截
    if(true){
        console.log('跳轉到登入頁面')
    }
    return request;
}, function (error) {
    // 在錯誤請求時進行操作
    return Promise.reject(error);
});
  

  既然有針對發起 request 請求時的攔截器,毫無疑問,對於獲取到介面返回的 response 資訊,我們同樣可以使用攔截器進行攔截。例如,在定義 restful 介面時,我們一般會根據 http 響應狀態碼去反映介面的呼叫是否成功。在每一個通過 axios 發起請求的 then 回掉方法中,我們都需要對獲取到響應狀態碼進行判斷,判斷介面的呼叫是否成功。

  當我們使用攔截器後,我們完全可以在針對 response 的攔截器中進行統一的判斷。例如,當呼叫介面不成功時,http 響應狀態碼為 400,同時返回錯誤資訊,我們完全可以在攔截器中進行判斷,當所有的介面響應狀態碼為 400 時,彈出後端返回的錯誤資訊。

// response 請求攔截
axios.interceptors.response.use(function (response) {
    // 對 response 進行攔截
    switch (response.status) {
        case 200:
            console.log('介面訪問成功')
            break
        case 400:
            console.log('提示錯誤資訊')
            break
        case 401:
            console.log('重定向到登入頁面')
            break
    }

    return response;
}, function (error) {
    // 在錯誤請求時進行操作
    return Promise.reject(error);
});

 三、總結

   這篇文章主要是簡單介紹如何使用 axios 去實現發起一個 http 請求。至此,在現階段的 Vue 學習使用中,對於一些基礎知識點就已經完成了一個初步的梳理,接下來,從下一章開始,我會從 0 開始通過 Vue CLI 去搭建一個前端的專案模板,因為自己並不是一個前端開發人員,個人的關注點還在於 .NET Core 後端,所以這裡可能並不會涉及到 webpack 相關的知識點。同時,一些在之前的學習中沒有涉及到的知識點也會在後續的文章中進行補充。之後,就像開篇時所說的那樣,Vue.js 牛刀小試 和 ASP.NET Core 專案實戰 相輔相成,後期的關注點將聚焦於如何通過 ASP.NET Core 和 Vue 進行前後端開發,歡迎持續關