日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Express 的使用

發布時間:2024/8/23 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Express 的使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

以下內容,基于 Express 4.x 版本

Node.js 的 Express

Express?估計是那種你第一次接觸,就會喜歡上用它的框架。因為它真的非常簡單,直接。

在當前版本上,一共才這么幾個文件:

lib/ ├── application.js ├── express.js ├── middleware │?? ├── init.js │?? └── query.js ├── request.js ├── response.js ├── router │?? ├── index.js │?? ├── layer.js │?? └── route.js ├── utils.js └── view.js

這種程度,說它是一個“框架”可能都有些過了,幾乎都是工具性質的實現,只限于 Web 層。

當然,直接了當地實現了 Web 層的基本功能,是得益于?Node.js?本身的 API 中,就提供了?net?和?http?這兩層,?Express?對?http?的方法包裝一下即可。

不過,本身功能簡單的東西,在?package.json?中卻有好長一串?dependencies?列表。

Hello World

在跑?Express?前,你可能需要初始化一個?npm?項目,然后再使用?npm?安裝?Express:

mkdir p cd p npm init npm install express --save

新建一個?app.js?:

const express = require('express'); const app = express(); app.all('/', (req, res) => res.send('hello') ); app.listen(8888);

調試信息是通過環境變量?DEBUG?控制的:

const process = require('process'); process.env['DEBUG'] = 'express:*';

這樣就可以在終端看到帶顏色的輸出了,嗯,是的,帶顏色控制字符,vim 中直接跑就 SB 了。

應用 Application

Application?是一個上層統籌的概念,整合“請求-響應”流程。?express()?的調用會返回一個?application?,一個項目中,有多個?app?是沒問題的:

const express = require('express');const app = express(); app.all('/', (req, res) => res.send('hello')); app.listen(8888);const app2 = express(); app2.all('/', (req, res) => res.send('hello2')); app2.listen(8889);

多個?app?的另一個用法,是直接把某個?path?映射到整個?app?:

const express = require('express');const app = express();app.all('/', (req, res) => {res.send('ok'); });const app2 = express(); app2.get('/xx', (req, res, next) => res.send('in app2') ) app.use('/2', app2)app.listen(8888);

這樣,當訪問?/2/xx?時,就會看到?in app2?的響應。

前面說了?app?實際上是一個上層調度的角色,在看后面的內容之前,先說一下?Express?的特點,整體上來說,它的結構基本上是“回調函數串行”,無論是?app?,或者?route,?handle,?middleware這些不同的概念,它們的形式,基本是一致的,就是?(res, req, next) => {}?,串行的流程依賴?next()?的顯式調用。

我們把?app?的功能,分成五個部分來說。

路由 - Handler 映射

app.all('/', (req, res, next) => {}); app.get('/', (req, res, next) => {}); app.post('/', (req, res, next) => {}); app.put('/', (req, res, next) => {}); app.delete('/', (req, res, next) => {});

上面的代碼就是基本的幾個方法,路由的匹配是串行的,可以通過?next()?控制:

const express = require('express');const app = express();app.all('/', (req, res, next) => {res.send('1 ');console.log('here');next(); });app.get('/', (req, res, next) => {res.send('2 ');console.log('get');next(); });app.listen(8888);

對于上面的代碼,因為重復調用?send()?會報錯。

同樣的功能,也可以使用?app.route()?來實現:

const express = require('express');const app = express();app.route('/').all( (req, res, next) => {console.log('all');next(); }).get( (req, res, next) => {res.send('get');next(); }).all( (req, res, next) => {console.log('tail');next(); });app.listen(8888);

app.route()?也是一種抽象通用邏輯的形式。

還有一個方法是?app.params?,它把“命名參數”的處理單獨拆出來了(我個人不理解這玩意兒有什么用):

const express = require('express');const app = express();app.route('/:id').all( (req, res, next) => {console.log('all');next(); }).get( (req, res, next) => {res.send('get');next() }).all( (req, res, next) => {console.log('tail'); });app.route('/').all( (req, res) => {res.send('ok')});app.param('id', (req, res, next, value) => {console.log('param', value);next(); });app.listen(8888);

app.params?中的對應函數會先行執行,并且,記得顯式調用?next()?。

Middleware

其實前面講了一些方法,要實現?Middleware?功能,只需要?app.all(/.*/, () => {})?就可以了,?Express?還專門提供了?app.use()?做通用邏輯的定義:

const express = require('express');const app = express();app.all(/.*/, (req, res, next) => {console.log('reg');next(); });app.all('/', (req, res, next) => {console.log('pre');next(); });app.use((req, res, next) => {console.log('use');next(); });app.all('/', (req, res, next) => {console.log('all');res.send('/ here');next(); });app.use((req, res, next) => {console.log('use2');next(); });app.listen(8888);

注意?next()?的顯式調用,同時,注意定義的順序,?use()?和?all()?順序上是平等的。

Middleware?本身也是?(req, res, next) => {}?這種形式,自然也可以和?app?有對等的機制——接受路由過濾,?Express?提供了?Router?,可以單獨定義一組邏輯,然后這組邏輯可以跟?Middleware一樣使用。

const express = require('express'); const app = express(); const router = express.Router();app.all('/', (req, res) => {res.send({a: '123'}); });router.all('/a', (req, res) => {res.send('hello'); });app.use('/route', router);app.listen(8888);

功能開關,變量容器

app.set()?和?app.get()?可以用來保存?app?級別的變量(對,?app.get()?還和?GET?方法的實現名字上還沖突了):

const express = require('express');const app = express();app.all('/', (req, res) => {app.set('title', '標題123');res.send('ok'); });app.all('/t', (req, res) => {res.send(app.get('title')); });app.listen(8888);

上面的代碼,啟動之后直接訪問?/t?是沒有內容的,先訪問?/?再訪問?/t?才可以看到內容。

對于變量名,?Express?預置了一些,這些變量的值,可以叫?settings?,它們同時也影響整個應用的行為:

  • case sensitive routing
  • env
  • etag
  • jsonp callback name
  • json escape
  • json replacer
  • json spaces
  • query parser
  • strict routing
  • subdomain offset
  • trust proxy
  • views
  • view cache
  • view engine
  • x-powered-by

(上面這些值中,干嘛不放一個最基本的?debug?呢……)

除了基本的?set() / get()?,還有一組?enable() / disable() / enabled() / disabled()?的包裝方法,其實就是?set(name, false)?這種。?set(name)?這種只傳一個參數,也可以獲取到值,等于?get(name)?。

模板引擎

Express?沒有自帶模板,所以模板引擎這塊就被設計成一個基礎的配置機制了。

const process = require('process'); const express = require('express'); const app = express();app.set('views', process.cwd() + '/template');app.engine('t2t', (path, options, callback) => {console.log(path, options);callback(false, '123'); });app.all('/', (req, res) => {res.render('demo.t2t', {title: "標題"}, (err, html) => {res.send(html)}); });app.listen(8888);

app.set('views', ...)?是配置模板在文件系統上的路徑,?app.engine()?是擴展名為標識,注冊對應的處理函數,然后,?res.render()?就可以渲染指定的模板了。?res.render('demo')?這樣不寫擴展名也可以,通過?app.set('view engine', 't2t')?可以配置默認的擴展名。

這里,注意一下?callback()?的形式,是?callback(err, html)?。

端口監聽

app?功能的最后一部分,?app.listen()?,它完成的形式是:

app.listen([port[, host[, backlog]]][, callback])

注意,?host?是第二個參數。

backlog?是一個數字,配置可等待的最大連接數。這個值同時受操作系統的配置影響。默認是 512 。

請求 Request

這一塊倒沒有太多可以說的,一個請求你想知道的信息,都被包裝到?req?的屬性中的。除了,頭。頭的信息,需要使用?req.get(name)?來獲取。

GET 參數

使用?req.query?可以獲取 GET 參數:

const express = require('express'); const app = express();app.all('/', (req, res) => {console.log(req.query);res.send('ok'); });app.listen(8888);

請求:

# -*- coding: utf-8 -*- import requests requests.get('http://localhost:8888', params={"a": '中文'.encode('utf8')})

POST 參數

POST 參數的獲取,使用?req.body?,但是,在此之前,需要專門掛一個 Middleware ,?req.body才有值:

const express = require('express'); const app = express();app.use(express.urlencoded({ extended: true })); app.all('/', (req, res) => {console.log(req.body);res.send('ok'); });app.listen(8888); # -*- coding: utf-8 -*-import requestsrequests.post('http://localhost:8888', data={"a": '中文'})

如果你是整塊扔的 json 的話:

# -*- coding: utf-8 -*-import requests import jsonrequests.post('http://localhost:8888', data=json.dumps({"a": '中文'}),headers={'Content-Type': 'application/json'})

Express?中也有對應的?express.json()?來處理:

const express = require('express'); const app = express();app.use(express.json()); app.all('/', (req, res) => {console.log(req.body);res.send('ok'); });app.listen(8888);

Express?中處理?body?部分的邏輯,是單獨放在?body-parser?這個 npm 模塊中的。?Express?也沒有提供方法,方便地獲取原始 raw 的內容。另外,對于 POST 提交的編碼數據,?Express?只支持 UTF-8 編碼。

如果你要處理文件上傳,嗯,?Express?沒有現成的 Middleware ,額外的實現在?github.com/expressjs/multer?。( Node.js 天然沒有“字節”類型,所以在字節級別的處理上,就會感覺很不順啊)

Cookie

Cookie 的獲取,也跟 POST 參數一樣,需要外掛一個?cookie-parser?模塊才行:

const express = require('express'); const cookieParser = require('cookie-parser'); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.use(cookieParser()) app.all('/', (req, res) => {console.log(req.cookies);res.send('ok'); });app.listen(8888);

請求:

# -*- coding: utf-8 -*-import requests import jsonrequests.post('http://localhost:8888', data={'a': '中文'},headers={'Cookie': 'a=1'})

如果 Cookie 在響應時,是配置?res?做了簽名的,則在?req?中可以通過?req.signedCookies?處理簽名,并獲取結果。

來源 IP

Express?對?X-Forwarded-For?頭,做了特殊處理,你可以通過?req.ips?獲取這個頭的解析后的值,這個功能需要配置?trust proxy?這個?settings?來使用:

const express = require('express'); const cookieParser = require('cookie-parser'); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.use(cookieParser()) app.set('trust proxy', true); app.all('/', (req, res) => {console.log(req.ips);console.log(req.ip);res.send('ok'); });app.listen(8888);

請求:

# -*- coding: utf-8 -*-import requests import json#requests.get('http://localhost:8888', params={"a": '中文'.encode('utf8')}) requests.post('http://localhost:8888', data={'a': '中文'},headers={'X-Forwarded-For': 'a, b, c'})

如果?trust proxy?不是?true?,則?req.ip?會是一個 ipv4 或者 ipv6 的值。

響應 Response

Express?的響應,針對不同類型,本身就提供了幾種包裝了。

普通響應

使用?res.send?處理確定性的內容響應:

res.send({ some: 'json' }); res.send('<p>some html</p>'); res.status(404); res.end(); res.status(500); res.end();

res.send()?會自動?res.end()?,但是,如果只使用?res.status()?的話,記得加上?res.end()?。

模板渲染

模板需要預先配置,在?Request?那節已經介紹過了。

const process = require('process'); const express = require('express'); const cookieParser = require('cookie-parser'); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.use(cookieParser())app.set('trust proxy', false); app.set('views', process.cwd() + '/template'); app.set('view engine', 'html'); app.engine('html', (path, options, callback) => {callback(false, '<h1>Hello</h1>'); });app.all('/', (req, res) => {res.render('index', {}, (err, html) => {res.send(html);}); });app.listen(8888);

這里有一個坑點,就是必須在對應的目錄下,有對應的文件存在,比如上面例子的?template/index.html?,那么?app.engine()?中的回調函數才會執行。都自定義回調函數了,這個限制沒有任何意義,?path, options?傳入就好了,至于是不是要通過文件系統讀取內容,怎么讀取,又有什么關系呢。

Cookie

res.cookie?來處理?Cookie?頭:

const process = require('process'); const express = require('express'); const cookieParser = require('cookie-parser'); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.use(cookieParser("key"))app.set('trust proxy', false); app.set('views', process.cwd() + '/template'); app.set('view engine', 'html'); app.engine('html', (path, options, callback) => {callback(false, '<h1>Hello</h1>'); }); app.all('/', (req, res) => {res.render('index', {}, (err, html) => {console.log('cookie', req.signedCookies.a);res.cookie('a', '123', {signed: true});res.cookie('b', '123', {signed: true});res.clearCookie('b');res.send(html);}); });app.listen(8888);

請求:

# -*- coding: utf-8 -*-import requests import jsonres = requests.post('http://localhost:8888', data={'a': '中文'},headers={'X-Forwarded-For': 'a, b, c','Cookie': 'a=s%3A123.p%2Fdzmx3FtOkisSJsn8vcg0mN7jdTgsruCP1SoT63z%2BI'}) print(res, res.text, res.headers)

注意三點:

  • app.use(cookieParser("key"))?這里必須要有一個字符串做?key?,才可以正確使用簽名的 cookie 。
  • clearCookie()?仍然是用“設置過期”的方式來達到刪除目的,cookie()?和?clearCookie()?并不會整合,會寫兩組?b=xx?進頭。
  • res.send()?會在連接上完成一個響應,所以,與頭相關的操作,都必須放在?res.send()?前面。

頭和其它

res.set()?可以設置指定的響應頭,?res.rediect(301, 'http://www.zouyesheng.com')?處理重定向,?res.status(404); res.end()?處理非 20 響應。

const process = require('process'); const express = require('express'); const cookieParser = require('cookie-parser'); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.use(cookieParser("key"))app.set('trust proxy', false); app.set('views', process.cwd() + '/template'); app.set('view engine', 'html'); app.engine('html', (path, options, callback) => {callback(false, '<h1>Hello</h1>'); });app.all('/', (req, res) => {res.render('index', {}, (err, html) => {res.set('X-ME', 'zys');//res.redirect('back');//res.redirect('http://www.zouyesheng.com');res.status(404);res.end();}); });app.listen(8888);

res.redirect('back')?會自動獲取?referer?頭作為?Location?的值,使用這個時,注意?referer為空的情況,會造成循環重復重定向的后果。

Chunk 響應

Chunk?方式的響應,指連接建立之后,服務端的響應內容是不定長的,會加個頭:?Transfer-Encoding: chunked?,這種狀態下,服務端可以不定時往連接中寫入內容(不排除服務端的實現會有緩沖區機制,不過我看?Express?沒有)。

const process = require('process'); const express = require('express'); const cookieParser = require('cookie-parser'); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.use(cookieParser("key"))app.set('trust proxy', false); app.set('views', process.cwd() + '/template'); app.set('view engine', 'html'); app.engine('html', (path, options, callback) => {callback(false, '<h1>Hello</h1>'); });app.all('/', (req, res) => {const f = () => {const t = new Date().getTime() + '\n';res.write(t);console.log(t);setTimeout(f, 1000);}setTimeout(f, 1000); });app.listen(8888);

上面的代碼,訪問之后,每過一秒,都會收到新的內容。

大概是?res?本身是 Node.js 中的?stream?類似對象,所以,它有一個?write()?方法。

要測試這個效果,比較方便的是直接 telet:

zys@zys-alibaba:/home/zys/temp >>> telnet localhost 8888 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET / HTTP/1.1 Host: localhostHTTP/1.1 200 OK X-Powered-By: Express Date: Thu, 20 Jun 2019 08:11:40 GMT Connection: keep-alive Transfer-Encoding: chunkede 1561018300451e 1561018301454e 1561018302456e 1561018303457e 1561018304458e 1561018305460e 1561018306460

每行前面的一個字節的?e?,為 16 進制的 14 這個數字,也就是后面緊跟著的內容的長度,是?Chunk?格式的要求。

Tornado 中的類似實現是:

# -*- coding: utf-8 -*-import tornado.ioloop import tornado.web import tornado.gen import timeclass MainHandler(tornado.web.RequestHandler):@tornado.gen.coroutinedef get(self):while True:yield tornado.gen.sleep(1)s = time.time()self.write(str(s))print(s)yield self.flush()def make_app():return tornado.web.Application([(r"/", MainHandler),])if __name__ == "__main__":app = make_app()app.listen(8888)tornado.ioloop.IOLoop.current().start()

Express?中的實現,有個大坑,就是:

app.all('/', (req, res) => {const f = () => {const t = new Date().getTime() + '\n';res.write(t);console.log(t);setTimeout(f, 1000);}setTimeout(f, 1000); });

這段邏輯,在連接已經斷了的情況下,并不會停止,還是會永遠執行下去。所以,你得自己處理好:

const process = require('process'); const express = require('express'); const cookieParser = require('cookie-parser'); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.use(cookieParser("key"))app.set('trust proxy', false); app.set('views', process.cwd() + '/template'); app.set('view engine', 'html'); app.engine('html', (path, options, callback) => {callback(false, '<h1>Hello</h1>'); });app.all('/', (req, res) => {let close = false;const f = () => {const t = new Date().getTime() + '\n';res.write(t);console.log(t);if(!close){setTimeout(f, 1000);}}req.on('close', () => {close = true;});setTimeout(f, 1000); });app.listen(8888);

req?掛了一些事件的,可以通過?close?事件來得到當前連接是否已經關閉了。

req?上直接掛連接事件,從?net?http?Express?這個層次結構上來說,也很,尷尬了。 Web 層不應該關心到網絡連接這么底層的東西的。

我還是習慣這樣:

app.all('/', (req, res) => {res.write('<h1>123</h1>');res.end(); });

不過?res.write()?是不能直接處理 json 對象的,還是老老實實?res.send()?吧。

我會怎么用 Express

先說一下,我自己,目前在?Express?運用方面,并沒有太多的時間和復雜場景的積累。

即使這樣,作為技術上相對傳統的人,我會以我以往的 web 開發的套路,來使用?Express?。

我不喜歡日常用?app.all(path, callback)?這種形式去組織代碼。

首先,這會使?path?定義散落在各處,方便了開發,麻煩了維護。

其次,把?path?和具體實現邏輯?callback?綁在一起,我覺得也是反思維的。至少,對于我個人來說,開發的過程,先是想如何實現一個?handler?,最后,再是考慮要把這個?handle?與哪些?path?綁定。

再次,單純的?callback?缺乏層次感,用?app.use(path, callback)?這種來處理共用邏輯的方式,我覺得完全是扯談。共用邏輯是代碼之間本身實現上的關系,硬生生跟網絡應用層 HTTP 協議的?path?概念抽上關系,何必呢。當然,對于?callback?的組織,用純函數來串是可以的,不過我在這方面并沒有太多經驗,所以,我還是選擇用類繼承的方式來作層次化的實現。

我自己要用?Express?,大概會這樣組件項目代碼(不包括關系數據庫的?Model?抽象如何組織這部分):

./ ├── config.conf ├── config.js ├── handler │?? ├── base.js │?? └── index.js ├── middleware.js ├── server.js └── url.js
  • config.conf?是 ini 格式的項目配置。
  • config.js?處理配置,包括日志,數據庫連接等。
  • middleware.js?是針對整體流程的擴展機制,比如,給每個請求加一個 UUID ,每個請求都記錄一條日志,日志內容有請求的細節及本次請求的處理時間。
  • server.js?是主要的服務啟動邏輯,整合各種資源,命令行參數?port?控制監聽哪個端口。不需要考慮多進程問題,(正式部署時?nginx?反向代理到多個應用實例,多個實例及其它資源統一用?supervisor?管理)。
  • url.js?定義路徑與?handler?的映射關系。
  • handler?,具體邏輯實現的地方,所有?handler?都從?BaseHandler?繼承。

BaseHandler?的實現:

class BaseHandler {constructor(req, res, next){this.req = req;this.res = res;this._next = next;this._finised = false;}run(){this.prepare();if(!this._finised){if(this.req.method === 'GET'){this.get();return;}if(this.req.method === 'POST'){this.post();return;}throw Error(this.req.method + ' this method had not been implemented');}}prepare(){}get(){throw Error('this method had not been implemented');}post(){throw Error('this method had not been implemented');}render(template, values){this.res.render(template, values, (err, html) => {this.finish(html);});}write(content){if(Object.prototype.toString.call(content) === '[object Object]'){this.res.write(JSON.stringify(content));} else {this.res.write(content);}}finish(content){if(this._finised){throw Error('this handle was finished');}this.res.send(content);this._finised = true;if(this._next){ this._next() }}}module.exports = {BaseHandler};if(module === require.main){const express = require('express');const app = express();app.all('/', (req, res, next) => new BaseHandler(req, res, next).run() );app.listen(8888); }

要用的話,比如?index.js?:

const BaseHandler = require('./base').BaseHandler;class IndexHandler extends BaseHandler {get(){this.finish({a: 'hello'});} }module.exports = {IndexHandler};

url.js?中的樣子:

const IndexHandler = require('./handler/index').IndexHandler;const Handlers = [];Handlers.push(['/', IndexHandler]);module.exports = {Handlers};

日志

后面這幾部分,都不屬于?Express?本身的內容了,只是我個人,隨便想到的一些東西。

找一個日志模塊的實現,功能上,就看這么幾點:

  • 標準的級別: DEBUG,INFO,WARN, ERROR 這些。
  • 層級的多個?logger?。
  • 可注冊式的多種?Handler?實現,比如文件系統,操作系統的?rsyslog?,標準輸出,等。
  • 格式定義,一般都帶上時間和代碼位置。

Node.js 中,大概就是?log4js?了,github.com/log4js-node/log4js-node?。

const log4js = require('log4js');const layout = {type: 'pattern',pattern: '- * %p * %x{time} * %c * %f * %l * %m',tokens: {time: logEvent => {return new Date().toISOString().replace('T', ' ').split('.')[0];}} }; log4js.configure({appenders: {file: { type: 'dateFile', layout: layout, filename: 'app.log', keepFileExt: true },stream: { type: 'stdout', layout: layout }},categories: {default: { appenders: [ 'stream' ], level: 'info', enableCallStack: false },app: { appenders: [ 'stream', 'file' ], level: 'info', enableCallStack: true }} });const logger = log4js.getLogger('app'); logger.error('xxx');const l2 = log4js.getLogger('app.good'); l2.error('ii');

總的來說,還是很好用的,但是官網的文檔不太好讀,有些細節的東西沒講,好在源碼還是比較簡單。

說幾點:

  • getLogger(name)?需要給一個名字,否則?default?的規則都匹配不到。
  • getLogger('parent.child')?中的名字,規則匹配上,可以通過?.?作父子繼承的。
  • enableCallStack: true?加上,才能拿到文件名和行號。

ini 格式配置

json 作配置文件,功能上沒問題,但是對人為修改是不友好的。所以,個人還是喜歡用 ini 格式作項目的環境配置文件。

Node.js 中,可以使用?ini?模塊作解析:

const s = ` [database] host = 127.0.0.1 port = 5432 user = dbuser password = dbpassword database = use_this_database[paths.default] datadir = /var/lib/data array[] = first value array[] = second value array[] = third value `const fs = require('fs'); const ini = require('ini');const config = ini.parse(s); console.log(config);

它擴展了?array[]?這種格式,但沒有對類型作處理(除了?true?false),比如,獲取?port?,結果是?"5432"?。簡單夠用了。

WebSocket

Node.js 中的 WebSocket 實現,可以使用?ws?模塊, github.com/websockets/ws?。

要把?ws?的 WebSocket Server 和?Express?的?app?整合,需要在?Express?的?Server?層面動手,實際上這里說的?Server?就是 Node.js 的?http?模塊中的?http.createServer()?。

const express = require('express'); const ws = require('ws');const app = express();app.all('/', (req, res) => {console.log('/');res.send('hello'); });const server = app.listen(8888);const wss = new ws.Server({server, path: '/ws'}); wss.on('connection', conn => {conn.on('message', msg => {console.log(msg);conn.send(new Date().toISOString());}); });

對應的一個客戶端實現,來自:?github.com/ilkerkesen/tornado-websocket-client-example/blob/master/client.py

# -*- coding: utf-8 -*-import time from tornado.ioloop import IOLoop, PeriodicCallback from tornado import gen from tornado.websocket import websocket_connectclass Client(object):def __init__(self, url, timeout):self.url = urlself.timeout = timeoutself.ioloop = IOLoop.instance()self.ws = Noneself.connect()PeriodicCallback(self.keep_alive, 2000).start()self.ioloop.start()@gen.coroutinedef connect(self):print("trying to connect")try:self.ws = yield websocket_connect(self.url)except Exception:print("connection error")else:print("connected")self.run()@gen.coroutinedef run(self):while True:msg = yield self.ws.read_message()print('read', msg)if msg is None:print("connection closed")self.ws = Nonebreakdef keep_alive(self):if self.ws is None:self.connect()else:self.ws.write_message(str(time.time()))if __name__ == "__main__":client = Client("ws://localhost:8888/ws", 5)


原文鏈接
本文為云棲社區原創內容,未經允許不得轉載。

總結

以上是生活随笔為你收集整理的Express 的使用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。