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