83998 连接服务器出错_新生福利 | 使用 Node.Js 开发服务器
點擊藍字關注,創智助你長姿勢
Node.js 是一個 Javascript 運行環境 (runtime),發布于 2009 年 5 月,由 Ryan Dahl 開發。實際上它是對 Google V8 引擎進行了封裝。V8 引擎執行 Javascript 的速度非常快,性能非常好。Node.js 對一些特殊用例進行了優化,提供了替代的 API ,使得 V8 在非瀏覽器環境下運行得更好。
Node.js 是一個基于 Chrome JavaScript 運行時建立的平臺, 用于方便地搭建響應速度快、易于擴展的網絡應用。Node.js ?使用事件驅動, 非阻塞 I/O 模型而得以輕量和高效,非常適合在分布式設備上運行數據密集型的實時應用。
一、引例描述
一個展示主流寵物和寵物新聞的首頁,不僅新聞內容要不時地更新,主流寵物也要跟著潮流更新,所以數據要不時地進行更換。
通過本次項目學習使用 Node.Js 開發屬于自己的服務器,本次項目將實現一個通過接收用戶的請求后,后臺查詢數據庫,獲得數據后,通過這些數據渲染一個主流的寵物頁面呈現給用戶,用戶點擊頁面中的新聞列表可跳轉到對應的新聞頁面,如果需要更換寵物和新聞頁面的內容,只需讓數據庫管理員更換數據庫內容即可。
二、?任務陳述
三、 項目結構
使用 js 文件書寫后臺程序,libs 用來放置自己編寫的模塊, template 用來放置用戶請求后返回的頁面,www 用來放置 HTML、jade、ejs 渲染頁面時使用到的外部樣式文件或者圖片素材。
四、知識準備
本次項目能實現的效果為:當用戶請求一個頁面時,后臺服務器接收到請求后,查詢數據庫得到數據,用這些數據作為 ejs 文件的內容并渲染成 HTML 頁面返回給用戶。
由以上分析可知需求:
1.?搭建屬于自己的服務器
2.?服務器接受到請求后查詢數據庫
3.?使用數據作為 ejs 頁面的內容并渲染成 HTML 頁面返回給用戶
4.1 搭建 Node.Js 服務器
使用模塊搭建服務器,這有助于快速開發,許多功能的實現不需要全部由自己編寫,Node.Js 提供許多開源的模塊,只需下載后,在程序中聲明一下即可使用,例如通過 const express=require('express') ,即引入了 express 模塊。
選擇使用 express 網絡框架搭建服務器,下載并在程序中聲明后,指定在6868端口監聽:
const express=require('express');//express 網絡框架var server=express();server.listen(6868); //在6868端口監聽用戶的請求當服務器接收到用戶的請求后,返回一個頁面,一般來說,沒有純靜態的頁面,往往頁面的某塊信息要發生變化,例如新聞的更新。選擇使用模板引擎后臺渲染頁面,好處就是頁面是可以變化的,根據程序的數據生成。
Jade 和 ejs 是兩大主流模板引擎,jade書寫起來簡潔方便,但不能與其他 HTML、CSS 共存,ejs 的書寫跟普通的 HTML 頁面書寫結構一樣,因此也能跟 HTML、CSS 共存,各有各的好處,根據實際開發的需求選擇合適的模板引擎。這里選擇 ejs 模板引擎為例進行開發,引入模板引擎,并配置模板引擎,配置模板引擎需告訴程序輸出什么東西,模板文件放在哪里以及使用哪種模板引擎:
const consolidate=require('consolidate');//模塊引擎合并庫//配置模板引擎//輸出什么東西server.set('view engine','html');//模板文件放在哪兒server.set('views','./template');//哪種模板引擎server.engine('html',consolidate.ejs);最后編寫接收用戶的請求,并返回的結果:
server.get('/',(req,res)=>{ //接收 url 為 localhost:6868 的請求//執行操作 if(err){ //提示錯誤信息 }else{ //返回頁面 }});在開發中往往服務器在接收到用戶的請求后,需要處理的步驟是比較多的,所有的步驟都寫在一起,出錯了往往比較難找,使用鏈式操作,處理完第一個步驟后,傳遞給下一級處理,下一級繼承上一級的處理結果,例如上一級創建了一個字符串變量,下一級就能使用此變量,這樣有便于調錯,也使得程序的可讀性增加了許多,改寫 get 請求:
server.get('/',(req,res,next)=>{ //接收請求的 url 為 localhost:6868//執行操作 if(err){ //提示錯誤信息 }else{ next(); //鏈式操作,傳遞給下一級處理 }});server.get('/',(req,res)=>{ //接收請求的 url 為 localhost:6868//執行操作 if(err){ //提示錯誤信息 }else{ //返回頁面 }});以上代碼只傳遞了一次,根據步驟的復雜性,決定使用的傳遞次數,只需要在第二個操作中繼續使用 next() 傳遞到下一級,直到最后一步操作即可。
4.2 查詢數據庫返回數據
在 node.js 中連接數據庫需要引入 mysql 模塊,接著連接數據庫,使用數據庫連接池能提高安全性以及連接效率:
const mysql=require('mysql');//一個用于 mysql 的 node.js 程序。它是用 JavaScript 編寫的,不需要編譯
//使用數據庫連接池,連接名為 blog 的數據庫
const db=mysql.createPool({host:'localhost',user:'root',password:'',database:'blog'});連接數據庫后便可以對數據庫進行增刪改查的操作,使用的語句與 mysql 完全一樣,例如 select * from banner_table 即表示查詢表名為 banner_table 所有數據。在 node.js 中執行查詢數據庫并返回結果:
server.get('/',(req,res,next)=>{ //查詢 banner 的東西 db.query("select * from banner_table",(err,data)=>{ if(err){ res.status(500).send('database error').end(); //提示數據庫操作出錯 }else{ res.banners=data; //將查詢到的結果賦值給 banners next(); //鏈式操作,傳遞給下一級處理 } });});query 用來執行語句,擁有兩個參數,第一個參數代表要執行的語句,第二個參數為一個函數,代表執行完語句后的一個反饋行為,這個反饋行為也有兩個參數,如果出錯,提示錯誤信息 err ,如果成功,獲取查詢到的結果 data 。
4.3 使用數據渲染頁面
使用數據渲染頁面,即一個頁面的內容并不是在 ejs 文件中寫好的,是非靜態的,由數據來決定。
例如:想在一個頁面中顯示一張圖片,如果按照平時的做法,會直接使用 插入一張圖片,但如果想要用數據決定顯示哪張圖片,就需要對 src 屬性值的書寫做一些改變:,此種寫法,是將 src 屬性值賦值為服務器返回來的一個 json 中的 src 值,而這個值是從數據庫查詢得來的,所以事先也需要在數據庫中存放好該值,而該值一般存放的是圖片的路徑名,數據庫一般不存放圖片文件,那樣子很耗費資源并且效率低,存放圖片路徑名,渲染頁面時,程序會根據路徑名去相應的文件夾找到圖片并渲染。
4.4?完成寵物首頁和新聞頁面的響應
寵物首頁的結構為:左部擺放 4 個主流寵物的相框,相框內包含對寵物的說明和圖片。右部是關于寵物的一個新聞列表。
左部內容存放于 banner_table 表中,右部內容存放于 artide_table 表中。
4.5.1?創建數據庫
創建一個名為 blog 的數據庫,在此數據庫中創建兩個表,分別為 banner_table 和 artide_table ,表 banner_table 用來存放主流的寵物信息,結構為:
ID 代表第幾條數據,title 代表標題,sub_title 代表寵物的描述,src 代表寵物圖片。
表 artide_table 來存放新聞結構的信息,結構為:
ID 代表第幾條數據,author 代表作者,zuthor_src 代表作者的圖像,title 代表標題,post_time 代表發布時間,content 代表新聞內容,summary 代表新聞內容的摘要,n_like 代表新聞被點贊的次數。
4.5.2 搭建 Node.js 服務器
引入搭建此次項目服務器需要的模塊:
const express=require('express');//express 網絡框架const static=require('express-static');//提供靜態文件const consolidate=require('consolidate');//模塊引擎合并庫const mysql=require('mysql');//一個用于 mysql 的 node.js 驅動程序。它是用 JavaScript 編寫的,不需要編譯const common=require('./libs/common'); //使用自己寫的模塊指定監聽的端口號為 6868:
var server=express();server.listen(6868); //在6868端口監聽用戶的請求配置模板引擎,指定輸出的東西為 HTML 文件,模板文件放在 /template 目錄下,使用 ejs 模板引擎:
//配置模板引擎//輸出什么東西server.set('view engine','html');//模板文件放在哪兒server.set('views','./template');//哪種模板引擎server.engine('html',consolidate.ejs);使用數據庫連接池,連接名稱為 blog 的數據庫:
//使用數據庫連接池,連接名為 blog 的數據庫const?db=mysql.createPool({host:'localhost',user:'root',password:'',database:'blog'});接收用戶請求并使用鏈式操作:
//接收用戶請求server.get('/',(req,res,next)=>{ //查詢 banner 的東西 db.query("select * from banner_table",(err,data)=>{ if(err){ res.status(500).send('database error').end(); }else{ res.banners=data; next(); //鏈式操作,傳遞給下一級處理 } });});server.get('/',(req,res,next)=>{ //查詢文章列表 db.query('select ID,title,summary from article_table',(err,data)=>{ if(err){ res.status(500).send('database error').end(); }else{ res.articles=data; next(); //鏈式操作,傳遞給下一級處理 } });});server.get('/',(req,res)=>{ //返回一個頁面,并附帶兩個 Json res.render('index.ejs',{banners:res.banners,articles:res.articles});});以上為查詢 banner_table 表的所有數據和 article_table 表的部分數據返回給 ejs 模板引擎渲染寵物首頁的左部,其中查詢 article_table 表的 ID 是有特殊用意的,ID 指代特定的新聞,一同攜帶給 ejs 文件,ejs 文件便可以通過 a 標簽發送請求指定 ID 號的新聞頁面:
?//將新聞主題變成一個鏈接,發送鏈接地址的時候攜帶一個新聞的 ID 編號
點擊跳轉到指定新聞頁面的頁面效果大致為:
頁面效果為在手機端訪問時的效果,新聞內容比較多,折疊了起來。可以發現底部有一個點贊數,用戶閱讀新聞的時候,如果喜歡此篇新聞,可以為此新聞點贊,每次點擊,點贊數就會增加1,原理與以上訪問指定新聞頁面一樣,點擊的時候多攜帶一個 act 參數,告訴程序用戶點贊了一次:
"/article?id=&act=like">程序就會修改數據庫中指定新聞的 n_lick 字段數值,并重新渲染,sql 語句格式如下:
db.query('update article_table set n_like=n_like+1 where ID='+req.query.id,(err,data)=>{});新聞頁面中有兩個地方是需要進行轉化的,一個是日期時間,后臺存放的日期一般都是以秒為單位的時間戳,所以取出來的時候需要做一下轉換,轉換的程序可以寫成一個模塊,這又回顧到以上說的下載模塊并在程序中引入模塊,即可使用模塊的功能,不管是下載別人的模塊還是自己寫的模塊,都是一樣的使用原理,而自己寫的模塊就不需要下載了,告訴程序去哪里找這塊模塊即可,本項目有一個 libs 文件夾,即是存放自己編寫的模塊,例如日期格式的轉化寫成一個 js 文件 common,并將它放置在 libs 中,在程序中引入:
const common=require('./libs/common'); //使用自己寫的模塊日期轉化模塊中的代碼如下:
function toDou(n){ //月和日如果小于10,在前面加個0 return n<10?'0'+n:''+n;}module.exports={ //輸出模塊 time2date:function(timestamp){ //執行日期轉化并返回結果 var oDate=new Date();????oDate.setTime(timestamp*1000); return oDate.getFullYear()+'-'+toDou(oDate.getMonth()+1)+'-'+toDou(oDate.getDate())+' '+toDou(oDate.getHours())+":"+toDou(oDate.getMinutes())+':'+toDou(oDate.getSeconds()); }};調用的時候只需要傳入一個日期數據:
common.time2date(articleData.post_time);第二個是新聞內容的轉換,新聞內容都是會分段的,假如直接輸出,內容會全部堆在一起,那么就得想辦法讓程序遇到段首符時轉換成一個
標簽,遇到段尾符時轉換成一個
,這樣就可以讓每個段落放置在一個塊級元素中,便可達到分段的效果,但是程序執行下來可以發現,新聞內容依然全部堆在一起,并且每個段落的首尾確實也分別加上了、
便簽,和
被程序當做內容輸出了,其實這并不奇怪,思考一下,如果程序遇到標簽就解析,那么是一件多么可怕的事情,加入一個不良用戶在發送一封郵件的時候,在郵件中加入一段無關的內容 //=改為-,告訴程序解析數據中攜帶的標簽以下是服務器查詢 article_table 表的內容返回給 ejs 模板引擎的完整代碼:
server.get('/article',(req,res)=>{ if(req.query.id){ if(req.query.act=='like'){ //增加一個贊 db.query('update article_table set n_like=n_like+1 where ID='+req.query.id,(err,data)=>{ //修改 article_table 表中的 n_like 值 if(err){ res.status(500).send('數據庫有小問題').end(); console.error(err); }else{ //顯示文章 db.query('select * from article_table where ID='+req.query.id,(err,data)=>{ if(err){ res.status(500).send('數據有問題:'+ err ).end(); }else{ if(data.length==0){ res.status(404).send('您請求的文章找不到').end(); }else{ var articleData=data[0]; articleData.sDate=common.time2date(articleData.post_time); //轉換日期格式 articleData.content=articleData.content.replace(/^/gm,''
).replace(/$/gm,''); //使段落能夠分段 res.render('conText.ejs',{ article_data:articleData }); } }????????????});????????} }); }else{ //顯示文章 db.query('select * from article_table where ID='+req.query.id,(err,data)=>{ if(err){ res.status(500).send('數據有問題:'+err).end(); }else{ if(data.length==0){res.status(404).send('您請求的文章找不到').end();}else{var articleData=data[0];articleData.sDate=common.time2date(articleData.post_time); //轉換日期格式articleData.content=articleData.content.replace(/^/gm,''
).replace(/$/gm,''); //使段落能夠分段 res.render('conText.ejs',{ //返回一個 ejs 文件和一個 json article_data:articleData });} } });}}else{ res.status(404).send('您請求的文章找不到').end();}});并在最后告訴程序靜態數據存放在哪個文件夾,使用靜態數據:
//static 數據server.use(static('./www'));4.5.3 創建寵物首頁以及新聞頁面
寵物頁面和新聞頁面的布局可根據自身的喜好進行編寫。寵物首頁和新聞頁面的內容需要書寫成以數據來填充的形式,通過遍歷 json 獲取所有左部的內容:
for( <%= banners[i].title %> //獲取標題 <p>圖片描述:<%= banners[i].sub_title %>p> //獲取圖片描述 <img src=""> //獲取圖片 div> <% } %>遍歷 json 獲取右部的內容:
for("/article?id="><%= articles[i].summary %>p><br/> //獲取新聞摘要
??????a 標簽中的 href 屬性,?后面的內容為攜帶的請求參數,攜帶一個 ID 參數的目的是讓服務器能夠辨析用戶點擊了哪篇新聞,并返回相應的新聞頁面。
新聞頁面的內容:
<p>作者:<%=article_data.author%>p> //獲取作者名稱<p>圖片:<img src="" width=250px height=200px alt=""/>p> //獲取作者圖像<p>主題:<%=article_data.title%>p> //獲取新聞主題<p>時間:<%=article_data.sDate%>p> //獲取發布日期<p>文章:<%-article_data.content%>p> //獲取新聞內容<p>點贊數:<a href="/article?id=&act=like"><img src="images/zan.png" />a><%=article_data.n_like%>p> //獲取點贊次數a 標簽中的 href 屬性,?后面的 id 為攜帶的請求參數,攜帶一個 ID 參數的目的是讓服務器能夠辨析用戶點擊了哪篇新聞,act 告訴服務器用戶對該新聞進行了一次點贊。
4.5.4 不同請求的處理流程圖
當用戶請求的 url 為 http://localhost:6868 ?時,程序的處理流程圖為:
當用戶請求的 url 為 http://localhost:6868/article?id=1 時 ,程序的處理流程圖為:
當用戶請求的 url 為 http://localhost:6868/article?id=1&act=like ?時,程序的處理流程圖為:
4.6 與用戶互動
一個點贊功能難以讓用戶感受到與頁面的互動性,一般用戶在閱讀一篇新聞的時候,喜歡評論一下,發表自己的意見。接收用戶發送的數據,可使用 post 請求,在新聞頁面的底部增加一個歷史評論區域,利用 post 請求收集所有用戶對新聞的評論。
先準備一個 comment_table 表來存放所有用戶的評論:
其中 ID 是編號,comment 代表用戶的評論。
服務器解析 post 數據,需要下載 body-parser 模塊,并在程序中聲明一下:
// bodyParser.urlencoded 解析 form 表單提交的數據server.use(bodyParser.urlencoded({extended: false}));// bodyParser.json 解析 json 數據格式server.use(bodyParser.json());編寫接收 post 請求,當接收到 post 請求時,向表中插入評論語句:
server.post('/add_comment',function(req,res){ //post 請求 let stmt = `INSERT INTO comment_table(comment) //準備 sql 預執行語句 VALUES(?)`; db.query(stmt,req.body.text,(err,data)=>{ //執行 sql 語句,req.body.text 代表用戶的評論,也即是 sql 語句中的?值 if(err){ res.status(500).send('database error').end(); }else{ console.log ("數據增加到 comment_table 表成功"); } });新聞頁面中,實現用戶提交評論后頁面的歷史評論就追加自己發表的評論,同時在 comment_table 表中增加該評論信息。本過程的原理是當用戶點擊提交按鈕時,程序通過 js 語句獲取輸入框中的評論語句,將該語句追加到歷史評論區域,同時發送 post 請求,使服務器將該評論語增加到 conment_table 表中,以便其他用戶看到自己的評論。此做法將這兩個過程單獨執行,是為了頁面在用戶發表了評論后不需要重新發送一個頁面過來達到刷新的效果,并且頁面不會閃一下,減少性能損耗和增加用戶體驗感。
但因為此種做法是獨立執行的,容易造成一種錯誤,新聞頁面發送評論后,評論語增加到歷史評論尾部,但服務器執行向 comment_table 表中插入該評論語時出錯了,插入失敗,其他用戶登錄此新聞頁面時看不到此評論語,這就會造成數據不一致的錯誤,所以需要讓這兩個單獨開來執行的過程最后要有一個交流,確保數據已經添加到數據庫了才顯示出評論語,始終以數據庫的數據為標準來渲染頁面,所以 post 請求中要做修改,在執行完成數據庫操作后,成功與否,都要有一個反饋信息,反饋一個 json 即可,響應給新聞頁面的 post 請求,讓新聞頁面知道評論語是否已經添加到數據庫中,服務器的 post 請求修改如下:
server.post('/add_comment',function(req,res){ //post請求 let stmt = `INSERT INTO comment_table(comment) //準備sql預執行語句 VALUES(?)`; db.query(stmt,req.body.text,(err,data)=>{ //執行 sql 語句,req.body.text代表用戶的評論,也即是 sql 語句中的?值 if(err){ res.status(500).send('database error').end(); res.send('{"ok":false,"msg":"數據增加到 comment_table 表失敗"}');//發送一個 json 作為反饋信息 }else{ console.log("數據增加到 comment_table 表成功"); res.send('{"ok":true,"msg":"數據增加到 comment_table 表成功"}'); //發送一個 json 作為反饋信息 } });在新聞頁面上編寫一個展示歷史評論的區域,可用表格形式等方式展示,并且用戶可以提交評論,提交后利用 jq 中的 ajax 發送 post 請求到服務器,如果數據庫操作成功,則將評論語追加到歷史評論尾部,post 請求代碼如下:
var comment_text=document.getElementById('in_text').value; //獲取輸入框中的評論語var comment={ //創建一個 json text:comment_text } $.post('http://localhost:6868/add_comment',comment,function(data, status){// post 請求,攜帶一個 json var json=eval('('+data+')'); //取得服務器反饋的 json//如果評論語已經插入comment_table 表中,將評論語追加到歷史評論尾部 if(json.ok){ var tr=table.insertRow(); for(var i=0;i<1;i++){ var tr=table.insertRow(); if(i==0){ var td=tr.insertCell(i); td.innerText=comment_text; } } }});大致效果如下圖所示:
五、 總結
本次項目介紹了 Node.Js 服務器,并通過例子講解了如何使用 express 框架搭建自己的一個服務器,并接收用戶的請求、連接數據庫返回數據、以數據動態渲染頁面返回給用戶。
?創智俱樂部
微信:sziitlSA
一個讓你漲姿勢的社團
長按二維碼關注
總結
以上是生活随笔為你收集整理的83998 连接服务器出错_新生福利 | 使用 Node.Js 开发服务器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 正则不等于一个字符串_更正一个观念:“积
- 下一篇: @autowired注解原理_相见恨晚,