Fitur kerja dan perangkat internal express.js

Jika Anda mengembangkan untuk platform node.js, maka Anda mungkin pernah mendengar tentang express.js . Ini adalah salah satu kerangka kerja ringan paling populer yang digunakan untuk membuat aplikasi web untuk simpul.



Penulis bahan, terjemahan yang kami terbitkan hari ini, menawarkan untuk mempelajari fitur-fitur struktur internal kerangka kerja ekspresinya melalui analisis kode sumbernya dan pertimbangan contoh penggunaannya. Dia percaya bahwa studi tentang mekanisme yang mendasari perpustakaan open source populer berkontribusi untuk pemahaman yang lebih mendalam tentang mereka, menghilangkan tirai "misteri" dari mereka dan membantu untuk membuat aplikasi yang lebih baik berdasarkan pada mereka.

Anda mungkin merasa nyaman untuk menjaga kode sumber ekspres berguna saat membaca materi ini. Versi ini digunakan di sini. Anda dapat membaca artikel ini tanpa membuka kode kilat, karena di sini, di mana pun sesuai, bagian kode dari perpustakaan ini diberikan. Di tempat-tempat di mana kode disingkat, komentar dari formulir // ...

Contoh dasar menggunakan express


Untuk mulai dengan, mari kita lihat "Hello World!" Tradisional dalam pengembangan teknologi komputer baru - sebuah contoh. Ini dapat ditemukan di situs web resmi kerangka kerja, ini akan berfungsi sebagai titik awal dalam penelitian kami.

 const express = require('express') const app = express() app.get('/', (req, res) => res.send('Hello World!')) app.listen(3000, () => console.log('Example app listening on port 3000!')) 

Kode ini memulai server HTTP baru pada port 3000 dan mengirimkan Hello World! untuk permintaan yang diterima pada GET / rute. Jika Anda tidak merinci, maka kami dapat membedakan empat tahap dari apa yang terjadi, yang dapat kami analisis:

  1. Buat aplikasi ekspres baru.
  2. Buat rute baru.
  3. Memulai server HTTP pada nomor port yang ditentukan.
  4. Memproses permintaan masuk ke server.

Membuat aplikasi ekspres baru


Perintah var app = express() memungkinkan Anda membuat aplikasi ekspres baru. Fungsi createApplication dari file lib / express.js adalah fungsi default yang diekspor; kita yang mengaksesnya dengan memanggil fungsi express() . Berikut adalah beberapa hal penting yang harus Anda perhatikan:

 // ... var mixin = require('merge-descriptors'); var proto = require('./application'); // ... function createApplication() { //    ,     . //     : `function(req, res, next)` var app = function(req, res, next) {   app.handle(req, res, next); }; // ... //  `mixin`    `proto`  `app` //     -  `get`,     . mixin(app, proto, false); // ... return app; } 

Objek app dikembalikan dari fungsi ini adalah salah satu objek yang digunakan dalam kode aplikasi kami. Metode app.get ditambahkan menggunakan fungsi mixin dari pustaka gabungan-deskriptor , yang bertanggung jawab untuk menetapkan metode app yang dinyatakan dalam proto . Objek proto sendiri diimpor dari lib / application.js .

Buat rute baru


Sekarang mari kita lihat kode yang bertanggung jawab untuk membuat metode app.get dari contoh kita.

 var slice = Array.prototype.slice; // ... /** *   `.VERB(...)` `router.VERB(...)`. */ // `methods`    HTTP, (  ['get','post',...]) methods.forEach(function(method){ //    app.get app[method] = function(path){   //     //          var route = this._router.route(path);   //        route[method].apply(route, slice.call(arguments, 1));   //   `app`,          return this; }; }); 

Sangat menarik untuk dicatat bahwa, di samping fitur semantik, semua metode yang menerapkan tindakan HTTP, seperti app.get , app.post , app.put dan sejenisnya, dalam hal fungsionalitas, dapat dianggap sama. Jika Anda menyederhanakan kode di atas, menguranginya menjadi implementasi hanya satu metode get , Anda mendapatkan sesuatu seperti berikut:

 app.get = function(path, handler){ // ... var route = this._router.route(path); route.get(handler) return this } 

Meskipun fungsi di atas memiliki 2 argumen, ini mirip dengan app[method] = function(path){...} fungsi app[method] = function(path){...} . Argumen kedua, handler , diperoleh dengan memanggil slice.call(arguments, 1) .

Singkatnya, app.<method> hanya menyimpan rute di router aplikasi menggunakan metode route , dan kemudian melewati handler ke route.<method> .

Metode route() router dideklarasikan di lib / router / index.js :

 // proto -     `_router` proto.route = function route(path) { var route = new Route(path); var layer = new Layer(path, {   sensitive: this.caseSensitive,   strict: this.strict,   end: true }, route.dispatch.bind(route)); layer.route = route; this.stack.push(layer); return route; }; 

Tidak mengejutkan, deklarasi metode route.get di lib / router / route.js mirip dengan deklarasi app.get :

 methods.forEach(function (method) { Route.prototype[method] = function () {   // `flatten`   ,  [1,[2,3]],      var handles = flatten(slice.call(arguments));   for (var i = 0; i < handles.length; i++) {     var handle = handles[i];         // ...     //   ,  ,    Layer,     //            var layer = Layer('/', {}, handle);     // ...     this.stack.push(layer);   }   return this; }; }); 

Setiap rute dapat memiliki beberapa penangan, atas dasar setiap penangan, variabel tipe Layer dibangun, yang merupakan lapisan pemrosesan data, yang kemudian masuk ke stack.

Objek Lapisan


Baik _router dan route menggunakan objek bertipe Layer . Untuk memahami esensi dari objek semacam itu, mari kita lihat konstruktornya :

 function Layer(path, options, fn) { // ... this.handle = fn; this.regexp = pathRegexp(path, this.keys = [], opts); // ... } 

Saat membuat objek bertipe Layer mereka diberi jalur, parameter tertentu, dan fungsi. Dalam kasus router kami, fungsi ini adalah route.dispatch (kami akan membicarakan lebih lanjut di bawah ini, secara umum, ini dirancang untuk mengirimkan permintaan ke rute yang terpisah). Dalam kasus rute itu sendiri, fungsi ini adalah fungsi handler yang dideklarasikan dalam kode contoh kita.

Setiap objek bertipe Layer memiliki metode handle_request , yang bertanggung jawab untuk mengeksekusi fungsi yang diteruskan ketika objek diinisialisasi.

Ingat apa yang terjadi ketika membuat rute menggunakan metode app.get :

  1. Sebuah rute dibuat di router aplikasi ( this._router ).
  2. Metode rute dispatch ditugaskan sebagai metode handler dari objek Layer sesuai, dan objek ini didorong ke stack router.
  3. Penangan permintaan diteruskan ke objek Layer sebagai metode penangan, dan objek ini didorong ke tumpukan rute.

Akibatnya, semua penangan disimpan di dalam instance app dalam bentuk objek dari tipe Layer yang ada di dalam route stack, yang metode dispatch ditugaskan ke objek Layer yang ada di stack router:


Lapisan objek pada stack router dan stack route

Permintaan HTTP yang masuk diproses sesuai dengan logika ini. Kami akan membicarakannya di bawah.

Startup server HTTP


Setelah mengkonfigurasi rute, Anda harus memulai server. Dalam contoh kita, kita beralih ke metode app.listen , meneruskannya nomor port dan fungsi panggilan balik sebagai argumen. Untuk memahami fitur-fitur metode ini, kita dapat merujuk ke file lib / application.js :

 app.listen = function listen() { var server = http.createServer(this); return server.listen.apply(server, arguments); }; 

app.listen hanya pembungkus di sekitar http.createServer . Sudut pandang seperti itu masuk akal, karena jika Anda mengingat apa yang kita bicarakan di awal, app hanyalah sebuah fungsi dengan function(req, res, next) {...} tanda tangan function(req, res, next) {...} , yang kompatibel dengan argumen yang diperlukan untuk http.createServer (tanda tangan dari metode ini adalah function (req, res) {...} ).

Setelah menyadari bahwa, pada akhirnya, segala sesuatu yang diekspresikan.

Pemrosesan permintaan HTTP


Sekarang kami tahu bahwa app hanyalah penangan permintaan, kami akan mengikuti jalur yang dilewati permintaan HTTP di dalam aplikasi ekspres. Jalan ini membawanya ke pawang yang dinyatakan oleh kami.

Pertama, permintaan pergi ke fungsi createApplication ( lib / express.js ):

 var app = function(req, res, next) {   app.handle(req, res, next); }; 

Kemudian masuk ke metode app.handle ( lib / application.js ):

 app.handle = function handle(req, res, callback) { // `this._router` -  ,    ,  `app.get` var router = this._router; // ... //     `handle` router.handle(req, res, done); }; 

Metode router.handle dideklarasikan di lib / router / index.js :

 proto.handle = function handle(req, res, out) { var self = this; //... // self.stack -  ,      // Layer (  ) var stack = self.stack; // ... next(); function next(err) {   // ...   //        var path = getPathname(req);   // ...   var layer;   var match;   var route;   while (match !== true && idx < stack.length) {     layer = stack[idx++];     match = matchLayer(layer, path);     route = layer.route;     // ...     if (match !== true) {       continue;     }     // ...      HTTP,       }  // ...      // process_params    ,          self.process_params(layer, paramcalled, req, res, function (err) {     // ...     if (route) {       //       `layer.handle_request`       //         `next`       //  ,   `next`     ,              //  ,   `next`   ,            return layer.handle_request(req, res, next);     }     // ...   }); } }; 

Singkatnya, fungsi router.handle melewati semua lapisan pada stack sampai menemukan yang cocok dengan jalur yang ditentukan dalam permintaan. Kemudian, metode layer handle_request akan dipanggil, yang akan menjalankan fungsi handler yang telah ditentukan. Fungsi pengendali ini adalah metode rute dispatch , yang dideklarasikan di lib / route / route.js :

 Route.prototype.dispatch = function dispatch(req, res, done) { var stack = this.stack; // ... next(); function next(err) {   // ...   var layer = stack[idx++];   // ...    layer.handle_request(req, res, next);   // ... } }; 

Sama seperti dalam kasus router, selama pemrosesan setiap rute, lapisan yang rute ini telah disebutkan dan metode handle_request mereka yang menjalankan metode layer handler handle_request . Dalam kasus kami, ini adalah penangan permintaan, yang dinyatakan dalam kode aplikasi.

Di sini, akhirnya, permintaan HTTP masuk ke area kode aplikasi kita.


Jalur permintaan dalam aplikasi ekspres

Ringkasan


Di sini kami hanya memeriksa mekanisme dasar dari perpustakaan express.js, yang bertanggung jawab atas pengoperasian server web, tetapi perpustakaan ini juga memiliki banyak fitur lainnya. Kami tidak berhenti pada pemeriksaan yang diminta melalui permintaan sebelum mereka mencapai penangan, kami tidak berbicara tentang metode pembantu yang tersedia ketika bekerja dengan variabel res dan req . Dan akhirnya, kami tidak menyentuh salah satu fitur ekspres yang paling kuat. Ini terdiri dari penggunaan middleware, yang dapat ditujukan untuk menyelesaikan hampir semua masalah - mulai dari penguraian permintaan hingga penerapan sistem otentikasi lengkap.

Kami berharap materi ini membantu Anda memahami fitur utama perangkat ekspres, dan sekarang, jika perlu, Anda dapat memahami semua hal lainnya dengan menganalisis secara independen bagian-bagian dari kode sumber perpustakaan ini yang menarik minat Anda.

Pembaca yang budiman! Apakah Anda menggunakan express.js?

Source: https://habr.com/ru/post/id414079/


All Articles