[微精通:網頁設計Vue + Node.js + MySQL] 4.3.網頁 & 網頁伺服器 & CORS (上)

有了API,接下來就讓我們在網頁中使用我們製作的API吧。此篇為上半部份。

 

image/svg+xml4.3. 網⾴ & 網⾴伺服器 & CORS var express = require( ‘express' ); var api = express (); var cors = require ( ' cors ' ); api.use ( cors ()); // 略過⼀些程式 <meta charset = "utf-8" > <html> <head> <!-- 略過⼀些 html --> < script src = " https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/ axios.min.js " ></script> </head> <body> <div id = 'app' > <!-- 略過⼀些 html --> <ul> < li v-for = '(zombie, index) in zombies ' > <p> {{ zombie.id }}, {{zombie.name}}, {{zombie.age}} , {{zombie.isVegetarian == true ? ' 素食者 ':' 非素食者 '}} <button class = "btn btn-primary" v-on:click = 'modifyInit(zombie)' > 修改 </button> <button class = "btn btn-danger ml-1" v-on:click = "del(index)" > 刪除 </button></p> <img v-bind:src = 'zombie.typeImgSrc' style = 'width:130px' > <hr> </ li > </ul> </div> </body> <script> new Vue({ el: '#app' , data: { // 略過⼀些程式 zombies : [] , // 略過⼀些程式 } , mounted : function () { this . loadZombies (); } , methods: { loadZombies : function () { axios . post ( ' http://127.0.0.1/api/getZombies ' , null ) . then( response => { let result = response . data ; this . zombies = result . data ; } ) ; } // 略過⼀些程式 } }); </script> </html> <meta charset = "utf-8" > <html> <!-- 略過⼀些 html --> <body> <div id = 'app' > <button type = "button" class = "btn btn-primary m-4" v-on:click = "newInit()" > 新增 </button> <div class = "modal fade" id = "newModal" > <div class = "modal-dialog" > <div class = "modal-content" > <!-- 略過⼀些 html --> <div class = "modal-body" > < form style = "margin:20px;" > <label> 名字 </label> <input type = "textbox" class = "form-control" v-model = "newName" > <label> 變異年份 </label> <input type = "textbox" class = "form-control" v-model = "newAge" > <input type = "checkbox" v-model = "newIsVegetarian" > <label class = "form-check-label" > 是否為素食者 </label> <br> <label> 類型圖片 </label> <div class = "form-check form-check-inline" v-for = "x in imgTypes" > <input class = "form-check-input" type = "radio" v-model = "newTypeImgSrc" v-bind:value = "x.src" > <label class = "form-check-label" > {{x.name}} </label> </div> <img v-bind:src = 'newTypeImgSrc' style = 'width:130px' > < /form > </div> <div class = "modal-footer" > < button class = "btn btn-primary" v-on:click = add ()" > 加入 < /button > </div> </div> </div> </div> <!-- 略過⼀些 html --> <hr> <ul> <li v-for = '(zombie, index) in zombies' > <p> {{zombie.id}}, {{zombie.name}}, {{zombie.age}} , {{zombie.isVegetarian == true ? ' 素食者 ':' 非素食者 '}}</p> <button class = "btn btn-primary" v-on:click = 'modifyInit(zombie)' > 修改 </button> <button class = "btn btn-danger ml-1" v-on:click = "del(index)" > 刪除 </button></p> <img v-bind:src = 'zombie.typeImgSrc' style = 'width:130px' > <hr> </li> </ul> </div> </body> <script> new Vue({ el: '#app' , data: { // 略過⼀些程式 zombies: [], newName: "" , newAge: 0 , newIsVegetarian: false , newTypeImgSrc: "z1.png" , // 略過⼀些程式 } , mounted: function (){ this .loadZombies(); } , methods: { loadZombies : function () { axios.post( 'http://127.0.0.1/api/getZombies' , null ) .then( response => { let result = response.data; this .zombies = result.data; } ); } // 略過⼀些程式 , add : function () { let age = parseFloat ( this . newAge ); if (isNaN(age) == true ){ alert( " 變異年份請輸入數值。 " ); return ; } axios . post (' http://127.0.0.1/api/addZombie ', { name: this .newName, age: age, isVegetarian: this .newIsVegetarian, typeImgSrc: this .newTypeImgSrc } , { headers : { " Content-Type " : text/plain " } } ).then( response => { let result = response . data ; if ( result.error != null ) { alert ( result.error ); return ; } this . loadZombies (); $( ' #newModal ' ). modal ( " hide " ); } ); } // 略過⼀些程式 } }); </script> </html> <meta charset = "utf-8" > <html> <!-- 略過⼀些 html --> <body> <div id = 'app' > <!-- 略過⼀些 html --> <div class = "modal fade" id = "modifyModal" > <div class = "modal-dialog" > <div class = "modal-content" > <div class = "modal-header" > <h5 class = "modal-title" > 修改殭屍 </h5> <button type = "button" class = "close" data-dismiss = "modal" > <span aria-hidden = "true" > &times; </span> </button> </div> <div class = "modal-body" > < form style = "margin:20px;" > <label> 名字 </label> <input type = "textbox" class = "form-control" v-model = "modifyName" > <label> 變異年份 </label> <input type = "textbox" class = "form-control" v-model = "modifyAge" > <input type = "checkbox" v-model = "modifyIsVegetarian" > <label class = "form-check-label" > 是否為素食者 </label> <br> <label> 類型圖片 </label> <div class = "form-check form-check-inline" v-for = "x in imgTypes" > <input class = "form-check-input" type = "radio" v-model = "modifyTypeImgSrc" v-bind:value = "x.src" > <label class = "form-check-label" > {{x.name}} </label> </div> <img v-bind:src = 'modifyTypeImgSrc' style = 'width:130px' > < /form > </div> <div class = "modal-footer" > < button class = "btn btn-primary" v-on:click = update ()" > 更新 < /button > </div> </div> </div> </div> <hr> <ul> < li v-for = '(zombie, index) in zombies' > <p> {{zombie.id}}, {{zombie.name}}, {{zombie.age}} , {{zombie.isVegetarian == true ? ' 素食者 ':' 非素食者 '}} <button class = "btn btn-primary" v-on:click = 'modifyInit(zombie)' > 修改 </button> <button class = "btn btn-danger ml-1" v-on:click = "del(index)" > 刪除 </button></p> <img v-bind:src = 'zombie.typeImgSrc' style = 'width:130px' > <hr> < /li > </ul> </div> </body> <script> new Vue({ el: '#app' , data: { // 略過⼀些程式 zombies: [], // 略過⼀些程式 modifyZombie: null, modifyID : - 1 , modifyName: "" , modifyAge: 0 , modifyIsVegetarian: false , modifyTypeImgSrc: "z1.png" } , mounted: function (){ this .loadZombies(); } , methods: { loadZombies : function () { axios.post( 'http://127.0.0.1/api/getZombies' , null ) .then( response => { let result = response.data; this .zombies = result.data; } ); } // 略過⼀些程式 , modifyInit : function ( x ) { this.modifyZombie = x; this . modifyID = x . id ; this .modifyName = x.name; this .modifyAge = x.age; this .modifyIsVegetarian = x.isVegetarian; this .modifyTypeImgSrc = x.typeImgSrc; $( '#modifyModal' ).modal( "show" ) } , update : function () { let age = parseFloat( this .modifyAge); if (isNaN(age) == true ){ alert( " 變異年份請輸入數值。 " ); return ; } axios . post( ' http://127.0.0.1/api/updateZombie ' , { id : this . modifyID , name: this .modifyName, age: age, isVegetarian: this .modifyIsVegetarian, typeImgSrc: this .modifyTypeImgSrc } , {headers: { "Content-Type" : "text/plain" }} ).then( response => { let result = response . data ; if ( result.error != null ) { alert ( result.error ); return ; } this . loadZombies (); // $( ' #modifyModal ' ). modal ( " hide " ); } ) ; } , // 略過⼀些程式 } }); </script> </html> 1 1 有了 API ,怎麼跟網⾴整合在⼀起 ? 1. 先前為了避免網⾴關 閉後資料就重置的問 題,我們開發了 API ,在 Vue 上寫了⼀些⽅法來管 理殭屍的新增、修改與 刪除的⽅法,如 add() update() del() 等⽅法,但接下 來怎麼連接我們開發好 API ? w4_3_1 - home.html API 安裝 cors 套件並開啟 CORS 功能 w4_3_2\api - server.js w4_3_2\api 1. ⾸先我將⽬前切換到我們的 api 錄下,以 w4_3_2 為例,未來的範例將 會存在⼀個 api ⽬錄存放我們的 api 另有⼀個 html ⽬錄存放我們的網⾴。 2. 接下來使⽤ npm install 安裝 cors 套件。 3. 引⽤ cors ,放在 cors 變數中。 4. 加上⼩括號 (()) 呼叫函式建立 cors 物件, express 物件使⽤ use() ⽅法導入 cors 功能。 CORS 其實是⼀個同源的安全性政策,因為 瀏覽器有實作這個安全政策,所以 CORS 通常發⽣在瀏覽器上,後續將 細說它什麼是。 3. 這個是我建立的 express 物件,放在 api 變數中。 1. Vue + axios ,完成 'R' 讀取殭屍資料 w4_3_3\html - home.html 1. 這份 html 是我們 copy w3_5_6 範例,其中有完整的新增修改 刪除殭屍資訊的功能,接下來我們將進⾏修改。 2. 引⽤ axios 套件, axios 如同 Postman 可以發出 HTTP 請求以呼叫 API 3. v-for zombies 變數中陣列中的殭屍物件轉為 <li>DOM 素,因為透過 api 我們會多了⼀個 id 編號屬性,因此我們多加上 id 屬性的輸出。⽽⽬前的 zombies 屬性就先改放入空陣列。 3. Vue 掛載後我們呼叫 loadZombies() 法,將從 API 取得的資料放入 zombies 屬性中。 4. 使⽤ axios 呼叫 API axios 物件上有個 post() ⽅法,表⽰以 post 的⽅式發出 HTTP 請求,這與我們 API 上的 express post() ⽅法是對應的。 5. 1 個參數寫下 getZombies API 的網址,第 2 個參數是 post 夾帶 的資料,使⽤ null 表⽰無資料。 7. 這個函式使⽤ lambda 的⽅式呈現,只是 function 不同的宣告⽅ 式是它可以直接寫下參數,接著是⼀個箭頭 (=>) 指向執⾏區塊 ({}) 8. then() 中的函式其格式可接收⼀個回傳的 Response 物件,在此宣告⼀個 response 參數。 9. Response 物件有個 data 屬性,可取得 API 回傳回 來的資料。注意,我們在 API 統⼀回傳的資料結是是 {data,error} ,因此先放在 result 變數中。 10. 在透過 result 中物件的 data 屬性取得所有殭屍物件,然後放在 Vue zombies 屬性中。 11. zombies 屬性中的資料⼀被變 更, Vue 就會依資料更新畫⾯了。 6. post() 法後直接使 .then() 呼叫 then() ⽅法。當 API 呼叫成功時會 呼叫 then() ⽅法 中的函式。 12. ⾸先,執⾏位於 api ⽬錄 下的 server.js 啟動 API 13. 接著執⾏ html ⽬錄下 home.html ,這時就 Vue 在載入後就會執⾏ mounted 中的程式,執⾏ loadZombies() ⽅法,在 透過 API 載入資料⾄ zombies 屬性,⽽後更新依 資料更新畫⾯,顯⽰從 API 取得的所有殭屍資訊。 1 2. Vue + axios ,完成 'C' 新增殭屍資料 w4_3_4\html - home.html 1. 這是我們新 增殭屍的畫⾯, 當填好資料好可 以按下加入呼叫 add() ⽅法建立 新的殭屍物件。 2. 改變 add() ⽅法,讓它呼叫 addZombie API 3. 同樣對針對變異年份特別 處理,將之轉為數值 number 4. 同樣使⽤ post() 呼叫 addZombies API 5. 在第 2 個參數中建立⼀個要新增的 殭屍資料物件,⽽屬性值除了變異年 age 外,其於則使⽤與新畫⾯同步 newName newIsVegetarian newTypeImgSrc 屬性上的值。 6. 3 個參數 ontent-Type text/plain 明確指明以存 ⽂字的⽅式上傳,這個參數與 HTTP 協定有關,它會被加入 HTTP Header 中。 7. 記住,我們回傳的 資料結構統⼀為 {data:,error:} 因此若 error 有值表⽰ 有錯誤,這時顯⽰這個 錯誤訊息並 return 束後續的執⾏,讓使⽤ 者知道且可在⽬前新增 的畫⾯上修正資料。 8. 若沒錯誤,則簡單的重新 讀取所有殭屍資料,讓 Vue 資料重新顯⽰畫⾯。 9. 然後關閉新增畫⾯。 10. 當輸入完新的殭屍資訊 後按下 加入 鈕,這時就會 呼叫 add() ⽅法,然後在透 API 將新的殭屍資料上傳並 新增⾄ API 伺服器中。 11. add() ⽅法的最後會呼 loadZombies() ⽅法,會再 次透過 API 取得所有殭屍資料, 這時你會在最下⽅看到我們剛才 新增的 ⼤隻佬 ' 殭屍。 1 1 2 2 3. Vue + axios ,完成 'U' 更新殭屍資料 w4_3_5\html - home.html 1. 這是我們更新 殭屍的畫⾯,當填 好資料好可以按下 加入呼叫 update() ⽅法來 更新殭屍物件。 2 2. 修改⼀下修改畫 ⾯初使化的的動作。 3. 刪除原先的 modifyZombie 屬性,多保 存⼀個殭屍的唯⼀編號 ID modifyID ,後續 API 會以 這個 ID 來修改特定殭屍的資 料,當然這個 modifyID 會加在 data 屬性中。 4. 修改更新 update() ⽅法,讓 它呼叫 updateZombie API 5. 修改的功能⼤致與新增功能⼀致。 只是 API 位扯改為 updateZombie 6. 資料⽅⾯多了⼀個 id 屬性,值從 modifyID 性中帶入,其餘與屬性除 age 外,皆改⽤修改資 料的屬性 modifyName modifyIsVegetarian modifyTypeImgSrc 入修改的資料。 7. error 不為空則 顯⽰錯誤,並 return 離開以停留在修改畫⾯ 讓使⽤者修正資料。 8. 若無 error 則重新 載入所有殭屍資料, Vue 會重新依資料顯⽰ 在畫⾯中。 11. 最後關閉修改畫⾯。 1 2 12. 選擇第 1 隻殭屍 瘋狂傑克 ' 將名字改為 ' 瘋狂傑克 22' 13. 按下 更新 ' ,就會呼叫 update() ⽅法,然後在呼 'updateZombie' API 修改 API 伺服器上的殭屍資料。 11. update() ⽅法的最後會 呼叫 loadZombies() ⽅法,會再 次透過 API 取得所有殭屍資料,這 時你會看到第 1 隻殭屍的名字已被 更改為 瘋狂傑克 22'

留言