只要在本地或服務器安裝了Node環(huán)境,使用 require('http')
引入http模塊,就能用http.createServer()方法創(chuàng)建一個服務器。比如我們使用VS Code新建一個app.js的文件(保存在電腦的到哪里都行),然后輸入以下代碼:
const http = require('http'); //引入內(nèi)置的http模塊
const hostname = '127.0.0.1';
const port = 3000;
const requestHandler = (req, res) => { //
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html;charset=utf-8');
res.end('Node Server創(chuàng)建成功啦');
console.log(`請求鏈接是:${req.url},請求方法是:${req.method}`)
}
const server = http.createServer(requestHandler) //使用 http.createServer() 方法創(chuàng)建服務器
server.listen(port, hostname, () => { //listen為createServer返回對象的方法,用于指定HTTP服務器監(jiān)聽的端口號
console.log(`通過此鏈接訪問服務器 http://${hostname}:${port}/`);
});
保存后,在VS Code里右鍵該文件選擇在終端中打開,然后在VS Code的終端中輸入以下代碼按Enter執(zhí)行:
node app.js
在瀏覽器里輸入http://127.0.0.1:3000/
,就能訪問我們創(chuàng)建好的服務器啦,頁面也會顯示Node Server創(chuàng)建成功啦
,可以說使用Nodejs創(chuàng)建一個HTTP服務器非常容易。
注意requestHandler有兩個參數(shù),req是request請求對象,調(diào)用request對象就可以拿到所有HTTP請求的信息,比如request.url獲取請求路徑;res是response響應對象,調(diào)用response對象的方法,就可以把HTTP響應返回給瀏覽器了。當用戶每訪問一次(比如刷新一下頁面)就會觸發(fā)requestHandler函數(shù),我們也能在終端看到打印的日志。
借助于fs 模塊: 可以對文件目錄進行創(chuàng)建、刪除、查詢以及文件的讀取和寫入以及url模塊: 可以處理與解析 URL,我們可以把服務器里的文件發(fā)送給客戶端。比如我們可以修改一下app.js的代碼為如下:
var http = require('http');
var url = require('url');
var fs = require('fs');
http.createServer( (req, res) => { //這里把上面的requestHandler給整到一起了,注意一下
const requrl = url.parse(req.url, true);
const filename = "." + requrl.pathname; //這里的.表示的是相對路徑
fs.readFile(filename, function(err, data) {
if (err) {
res.writeHead(404, {'Content-Type': 'text/html;charset=utf-8'});
return res.end("404 頁面沒有找到");
}
res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
res.write(data);
console.log(`請求鏈接是:${req.url},請求方法是:${req.method}`);
return res.end();
});
}).listen(3000);
然后再在終端執(zhí)行node app.js
(如果你之前的node server還在執(zhí)行,你可以連續(xù)按兩次Ctrl+C來終止服務器,再來執(zhí)行node app.js)。放一個文件比如tcb.jpg到app.js的相同目錄里,在瀏覽器里輸入如下地址(也就是ip地址+文件的路徑)看看:
http://127.0.0.1:3000/tcb.jpg
本地調(diào)試時,服務器和客戶端都是同一條電腦,我們使用瀏覽器打開
http://127.0.0.1:3000/
就能通過瀏覽器訪問到服務器里的文件。
那云函數(shù)是否可以搭建一個Nodejs的服務器呢,結(jié)合云接入和云函數(shù),可以很輕松地托管Nodejs服務端程序。這里就要使用到serverless-http的模塊。我們可以使用serverless-http把集成請求轉(zhuǎn)化為 Node.js Server 能接收的 IncommingMessage,同時把返回的 ServerResponse 轉(zhuǎn)化為集成請求。
使用VS Code在functions文件夾里新建一個云函數(shù),比如server,和小程序云開發(fā)的云函數(shù)一樣新建3個文件,以及assets文件夾,里面存放我們要返回的HTML文件、圖片等各種靜態(tài)資源,結(jié)構(gòu)如下:
├── server //server云函數(shù)目錄
│ └── assets
│ └── index.html
│ └── demo.jpg
│ └── index.js
│ └── config.json
│ └── package.json
并在package.json里添加serverless-http依賴,
"dependencies": {
"wx-server-sdk": "latest",
"serverless-http": "latest"
}
然后再在index.js里輸入以下代碼,我們把之前Nodejs Server里的代碼Copy過來,注意與普通云函數(shù)和集成請求寫法的不同。
const cloud = require('wx-server-sdk')
const url = require('url')
const fs = require('fs')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
const serverless = require('serverless-http');
const requestHandler = (req, res) => { //
const requrl = url.parse(req.url, true);
const filename = "." + requrl.pathname; //這里的.表示的是相對路徑
fs.readFile(filename, function(err, data) {
if (err) {
res.writeHead(404, {'Content-Type': 'text/html;charset=utf-8'});
return res.end("404 頁面沒有找到");
}
res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
res.write(data);
console.log(`請求鏈接是:${req.url},請求方法是:${req.method}`);
return res.end();
});
}
exports.main = serverless(requestHandler);
終端進入云函數(shù)目錄server文件夾,使用npm install
安裝依賴,然后再回退到項目根目錄使用CLoudbase Cli命令將云函數(shù)部署到云端并創(chuàng)建server云函數(shù)云接入的路由,再用瀏覽器或cURL命令訪問云接入鏈接:
cloudbase functions:deploy server
cloudbase service:create -p /server -f server
這樣我們就能通過云接入的鏈接來訪問托管的服務器里面的資源了,只要是云函數(shù)目錄里面的資源就都能訪問,云函數(shù)就”化身“成了一個服務器了。比較一下集成請求返回html與托管Nodejs Server的不同。
https://xly-xrlur.service.tcloudbase.com/server/assets/index.html
Koa和Express都是基于Nodejs平臺的web應用開發(fā)框架,可以對HTTP Request對象和HTTP Response對象進行封裝處理,以及生命周期的維護,路由、視圖的處理等等。云接入和云函數(shù)可以托管Nodejs Server,它也支持托管Koa和Express,下面就以Koa為例。
我們還是以server云函數(shù)為例,首先在server云函數(shù)的package.json里添加koa依賴,然后將index.js改為如下代碼,仍然讀取云函數(shù)目錄下的assets文件里的index.html文件:
const cloud = require('wx-server-sdk')
const fs = require('fs')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
const serverless = require('serverless-http');
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {//ctx是由koa傳入的封裝了request和response的變量,通過它可以訪問request和response
ctx.response.type = 'text/html;charset=utf-8'; //ctx.response就是Node的response對象
ctx.response.body = fs.createReadStream('./assets/index.html');
})
exports.main = serverless(app);
進入云函數(shù)目錄下載云函數(shù)的依賴之后,回退到項目根目錄部署上傳server云函數(shù),再使用瀏覽器打開server云函數(shù)的云接入地址就能看到解析好的index.html了。
Koa 的Context 上下文將 node 的 request 和 response 對象封裝到單個對象中,為編寫 Web 應用程序和 API 提供了許多有用的方法。為方便起見許多上下文的訪問器和方法直接委托給它們的 ctx.request或 ctx.response。ctx.response就是Node的response對象,ctx.request就是Node的request對象。
使用Koa也能讓云函數(shù)+云接入作為文件服務器,把云函數(shù)目錄下的文件都返回給瀏覽器,我們將server云函數(shù)的代碼修改為如下,這個功能和前面的托管Nodejs Server類似:
const cloud = require('wx-server-sdk')
const fs = require('fs')
const url = require('url')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
const serverless = require('serverless-http');
const Koa = require('koa');
const app = new Koa();
app.use( async ( ctx ) => {
const requrl = url.parse(ctx.request.url, true);
const filename = "." + requrl.pathname;
ctx.response.type = 'text/html;charset=utf-8';
ctx.body =fs.createReadStream(filename)
})
exports.main = serverless(app);
將Server云函數(shù)部署上傳,和前面一樣我們可以在瀏覽器里輸入以下地址來訪問server云函數(shù)目錄里的assets文件夾里的index.html頁面:
https://xly-xrlur.service.tcloudbase.com/server/assets/index.html
Koa原生路由通過解析request IncomingMessage 的 url 屬性, 利用 if...else 來判斷路徑返回不同的結(jié)果,但是如果路由過多, if...else 的分支也會越龐大, 不利于代碼的維護,具體的案例這里就不多寫了,下面直接用Koa-router解決方案。
盡管我們可以依靠ctx.request.url這種比較原生的方式來手動處理路由,但是這會寫很多處理代碼,這時候就需要對應的路由中間件對路由進行控制,這里推薦使用Koa-router,以及推薦使用koa-bodyparser中間件。對于POST請求的處理,koa-bodyparser可以把ctx的formData數(shù)據(jù)解析到ctx.request.body中。
我們?nèi)匀灰詓erver云函數(shù)為例,在package.json添加如下依賴,并進入server云函數(shù)目錄下載這些依賴:
"dependencies": {
"wx-server-sdk": "latest",
"serverless-http": "latest",
"koa":"latest",
"koa-bodyparser":"latest",
"koa-router":"latest"
}
然后將server云函數(shù)修改為如下代碼,然后部署上傳server云函數(shù),然后訪問云接入的地址,注意打開的頁面里的首頁和關于我們是可以點擊的,會跳轉(zhuǎn)到koa-router指定的路由,并返回相應的內(nèi)容:
const fs = require('fs')
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
const serverless = require('serverless-http');
const Koa = require('koa');
const bodyParser = require('koa-bodyparser')
const app = new Koa();
const Router = require('koa-router')
const router = new Router()
app.use(bodyParser())
router.get('/',async (ctx) => {
//注意這里的路徑哈,server為云接入的路由,在前面我們把server云函數(shù)的路由設置為server
let html = `
<ul>
<li><a href="/server/home">首頁</a></li>
<li><a href="/server/about">關于我們</a></li>
</ul>
ctx.body = html
})
router.get('/home',async (ctx) => {
ctx.response.type = 'text/html;charset=utf-8';
ctx.response.body = fs.createReadStream('./assets/index.html');
})
router.get('/about', async ( ctx )=>{
ctx.body = '歡迎您的關注,網(wǎng)頁還在建設當中'
})
app.use(router.routes()) // 添加路由中間件
app.use(router.allowedMethods()) // 對請求進行一些限制處理
exports.main = serverless(app);
當我們打開云接入地址/home
時,返回的是云函數(shù)目錄下的assets文件夾里的index.html頁面,而事實上云函數(shù)目錄并沒有home這個文件夾,按照之前的方式打開云接入地址/assets/index.html
也打不開了,這個就是路由重定向。
這個案例僅僅只是使用了GET方法來進行注冊路由,我們還可以使用POST、DELETE、PUT、DEL、ALL等方法。而koa-router路由也還支持變量等,這里就不展開啦。
有了路由中間件,我們就能把最常見的GET、POST請求都集成在一個云函數(shù)里,比如數(shù)據(jù)庫、云存儲的增刪改查,從而將該云函數(shù)作為API服務器,向前端返回所需數(shù)據(jù)和執(zhí)行指定的操作。在小程序端我們可以使用wx.request()接口發(fā)起HTTPS網(wǎng)絡請求(值得注意的是小程序端需要將云接入的域名添加到小程序里的域名白名單內(nèi)),在Web端則可以通過axios。
獲取數(shù)據(jù)庫里的數(shù)據(jù)
比如我們用Koa router可以添加一個路由getData,用來返回云數(shù)據(jù)庫查詢到的數(shù)據(jù)結(jié)果:
router.get('/getData',async (ctx) => {
ctx.body = await db.collection('china').where({
gdp: _.gt(3000)
})
.field({
_id:false,
city: true,
province: true,
gdp:true
})
.orderBy('gdp', 'desc')
.skip(0)
.limit(10)
.get()
})
在小程序端獲取返回數(shù)據(jù):
wx.request({
url: 'https://xly-xrlur.service.tcloudbase.com/server/getData',
success (res) {
console.log(res.data.data)
}
})
在web端獲取返回數(shù)據(jù):
const url ="https://xly-xrlur.service.tcloudbase.com/server/getData"
axios.get(url).then(res => {
console.log(res.data)
}).catch(err => {
console.log(err)
})
往數(shù)據(jù)庫里添加數(shù)據(jù)庫
我們還可以使用Koa router提供POST接口,對前端傳來的參數(shù)、數(shù)據(jù)進行處理,比如我們可以往數(shù)據(jù)庫里添加數(shù)據(jù),只需要注意Koa是如何獲取參數(shù)和數(shù)據(jù)的即可。
router.post('/addData',async (ctx) => {
const {userInfo} = await ctx.request.body
const addUser = await db.collection('china').add({
data:userInfo
})
ctx.body = addUser
})
小程序端發(fā)起POST請求的代碼如下:
wx.request({
url: 'https://xly-xrlur.service.tcloudbase.com/server/addData',
method:"POST",
data:{
userInfo:{
Name: '騰訊云云開發(fā)',
enName: 'CloudBase'
}
},
success (res) {
console.log(res)
}
})
在web端發(fā)起POST請求的代碼如下:
async function addData(){
axios.post('https://xly-xrlur.service.tcloudbase.com/server/addData', {
userInfo:{
Name: '騰訊云云開發(fā)',
enName: 'CloudBase'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
我們還可以在小程序端或Web端調(diào)用一下server云函數(shù),看看返回的數(shù)據(jù)對象和以往的有什么不同?大致了解一下后臺函數(shù)與HTTP函數(shù)的不同。
更多建議: