Nodejs/Jquery/Bootstrap搭建簡單的web服務


Nodejs/Jquery/Bootstrap搭建簡單的web服務

Contents

最近想給畢設加入一個前端界面來調用后台的一些功能,這樣展示性更好。我使用BootStrap,Jquery寫了一個很簡陋的前端界面。服務器端使用Node.js express框架開了接口。之前以為是挺簡單的一個東西,因為這些代碼網上應該有很多可以參考。然而這個過程中,踩了不少的坑,花了3天時間,才把基本流程調通...記錄一下我踩過的坑。

整體思路

  • 我的需求是:
    調用后台的Nodejs接口,進行增刪查改操作,並向前端返回操作結果。由於我對於前后端的了解非常的膚淺,所以就選擇了自己聽說過的幾個框架來寫:

    • 后端用Nodejs Express開接口
    • 前端用Bootstrap,Jquery實現基本邏輯
  • 代碼實現:
    那么根據我的需求,參考了一系列教程/博客,我抄來寫的代碼如下

  1. Nodejs端
var express = require('express');
var path = require('path')
var bodyParser = require('body-parser');

var app = express();
app.use(bodyParser.urlencoded({extended : false}));//這是一個中間件,用於解析請求內容

var SaveData = require("./SaveData.js");
var OffChainDB = require("./OffChainDB.js");

app.use(express.static(path.join(__dirname, '../privacy_frontend')));//設置靜態文件中間件,在../privacy_frontend下包含我的前端html文件

app.post('/savedata',function(request,response){
    console.log(request.body);//打印request的json內容
    
    var result = SaveData.SaveData();//調用接口,作用是向數據庫插入數據,返回一個Promise對象,並攜帶着數據庫返回的消息
    
    result.then(r => {
        console.log('success', r)
        str = JSON.stringify(r)
        response.json(str)
    }).catch(e => {
        console.log('error', e)
    })
})

var server = app.listen(3000,function(){//監聽3000端口,這樣瀏覽器直接訪問IP:3000就可以訪問到上面開的接口了
    var host = server.address().address;
    var port = server.address().port;

    console.log("app listening at port 3000");
})

process.on('unhandledRejection',function(err){//綁定對於unhandleRejection的回調函數,報錯
    console.error(err.stack);
});

process.on('uncaughtException',console.error);//跟上一句類似
  1. 前端
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>保存數據</title>
    <link href="bootstrap.min.css" rel="stylesheet">
    <script src="jquery-3.4.0.min.js"></script>
    <script src="bootstrap.min.js"></script>
</head>
<body>
    <form class="form-horizontal">
        <div class="form-group">
            <label for="inputChaincode" class="col-sm-2 control-label">鏈碼</label>
            <div class="col-sm-6">
                <input type="text" class="form-control" id="inputChaincode" placeholder="鏈碼">
            </div>
        </div>
        <div class="form-group">
            <label for="inputMethod" class="col-sm-2 control-label">鏈碼方法</label>
            <div class="col-sm-6">
                <input type="text" class="form-control" id="inputMethod" placeholder="鏈碼方法">
            </div>
        </div>
        <div class="form-group">
            <label for="inputDataType" class="col-sm-2 control-label">數據類型</label>
            <div class="col-sm-6">
                <input type="text" class="form-control" id="inputDataType" placeholder="插入數據類型">
            </div>
        </div>
        <div class="form-group">
            <label for="inputData" class="col-sm-2 control-label">數據</label>
            <div class="col-sm-6">
                <input type="text" class="form-control" id="inputData" placeholder="數據">
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <!-- <button type="submit" class="btn btn-default" id="btn_submit">保存數據</button> -->
                <a type="submit" class="btn btn-default" id="btn_submit">保存數據</a>
                <script>
                    var json = {};//對象類型的變量
                    $("#btn_submit").click(function () {
                        json['chaincode'] = $("#inputChaincode").val();//從表單的每一個輸入框獲取信息,拼接成一個json對象
                        json['method'] = $("#inputMethod").val();
                        json['datatype'] = $("#inputDataType").val();
                        json['data'] = $("#inputData").val();
                        alert(JSON.stringify(json));

                        $.post("http://192.168.253.130:3000/savedata",//向服務器發送post請求
                        json,//要發送的數據(request body)
                        function (data,status) {//成功收到消息的回調函數
                            alert("數據:\n"+data+"\n狀態: "+status);//一系列彈窗提示
                            console.log(data);
                            console.log(status);
                            alert("callback is invoked!");
                            alert(data);
                        })
                    });
                </script>
            </div>
        </div>
    </form>
</body>
</html>
  • 界面部分使用了bootstrap自帶的表單樣式,代碼基本來源於官網文檔。然而這就是坑的開始。
  • js腳本部分,也很簡單。就是為提交按鈕增加一個點擊事件回調函數。回調函數內容就是向Nodejs后台發起post請求,等待回應。

跨域訪問問題

  • 之前聽說過跨域訪問這個名詞,然而不知道具體是什么意思。這一次算是有了切身經驗,理解了這個概念的含義。

  • 我的nodejs后端運行在我的虛擬機linux上,而前端最開始放在我的本地windows里(為了方便調試)。然后發現發出的post請求怎么也收不到。

  • 最后發現因為這是跨域請求。跨域請求的概念是:

    • 請求者和被請求者的 協議,ip,端口 中的任意一個不相同,就是跨域訪問,js默認是不允許跨域訪問的(出於安全考慮)。
    • 這一點在Jquery文檔里也有寫

    文檔內容
    文檔內容

  • 解決方法

    • 將前端界面也放到linux虛擬機上,跟后端使用同一IP:端口,就解決了跨域訪問的問題
    • 這告訴我們,使用輪子之前一定要好好看文檔啊。

Jquery.post()回調函數不執行問題

  • 以上的第一個問題其實很快就發現了,然而第二個問題才是大坑。找了很久才發現問題所在。
  • 在上面的<script>腳本里邊,調用了$.post()函數,看起來挺簡單的,參考了一下菜鳥教程,然后就寫好了。
    • 運行的現象
    • 后端能收到請求,也能發送響應。但是前端收到數據的回調函數怎么也不執行,瀏覽器F12調試也根本看不到發出的post請求。
    • 用postman進行請求,沒有任何問題。
  • 然后就開始了漫長的找bug過程,由於沒有任何報錯信息,只能盲目的通過注釋代碼,修改代碼來嘗試。
    • 開始認為是后端的問題,因為嘗試發現:如果不調用SaveData.SaveData()這個函數,而是直接給一個resolved的Promise,就沒有問題。於是我各種修改SaveData()函數,將它由Promise寫法改成了普通回調的寫法。結果好像並沒有什么用。
    • 然后懷疑是response.send()參數數據類型的問題。嘗試發現:如果這個數據比較短,前端就能執行回調,否則就不行。是否能執行回調貌似變成了一個概率事件...要看運氣的...這時候我已經絕望了,這是什么玄學問題,還從沒遇到那么詭異的bug。
    • 絕望之后,再懷疑前端,把$post改成鏈式調用的方式,寫了$.post().done().fail()函數來代替作為參數的回調函數。居然就成功了。穩定的能夠執行回調函數。但是依然不知道為什么。
    • 直到看到StackOverflow上面的一個回答,問題與我類似;他說是因為他的前端會自動刷新,所以刷新過后,上一次執行script代碼時創建的jqXHR對象就沒有了,自然瀏覽器也看不到這個請求的消息,也不會執行回調函數。
    • 仔細觀察我的前端運行,貌似的確每次點擊提交按鈕就會刷新一下頁面,但是我並沒有寫這個邏輯啊...問題出在哪呢?只有可能是bootstrap,搜了一下發現點擊按鈕就刷新頁面這個邏輯是bootstrap自帶的,自帶的....發現bootstrap才是罪魁禍首,欲哭無淚啊...從始至終就沒有注意到這一點。
    • 解決方法:
      • 參考了這一篇博文
      • 修改了<button>標簽為<a>標簽,問題解決。跟nodejs后端,跟jquery.post()沒有任何關系emmmmm....
      • 以上經驗告訴我們:用輪子的時候看文檔也不一定管用啊...bootstrap文檔里沒有告訴我點擊button會自動刷新的啊...

總結

  • 回顧整個過程,還是能學到一些經驗
    • VSCode用作Nodejs的調試工具非常好用,能用調試器就不要用打印日志這種辦法了,效率極低。
    • 當你對一些語法類的東西不確定的時候,一個簡單的方法是:跳出復雜的場景,自己重新寫一個test.js來測試各種情況。比如我對promise的狀態改變,執行流程搞不清楚,那么就寫一個test的代碼來各種嘗試,這樣很快就能弄清楚一些模糊的地方。
    • Postman如果測試沒問題,那么后端的接口就一定是沒問題的。我並沒有意識到這一點,將注意力放在了后端上。其實是南轅北轍了。一開始就定位問題在前端,可能就沒有那么費勁了。
    • 用輪子之前,盡量還是把一些注意事項,文檔通讀一遍,直接上來就懟代碼可能事倍功半。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM