Apa dua method respon yang paling sering digunakan pada sebuah website yang dinamis

Sebuah aplikasi web berkomunikasi dengan perangkat lunak client melalui HTTP. HTTP, sebagai protokol yang berbicara menggunakan request dan response menjadikan aplikasi web bergantung kepada siklus ini untuk menghasilkan dokumen yang ingin diakses oleh pengguna. Secara umum, aplikasi web yang akan kita kembangkan harus memiliki satu cara untuk membaca HTTP Request dan mengembalikan HTTP Response ke pengguna.

Pada pengembangan web tradisional, kita umumnya menggunakan sebuah web server seperti Apache HTTPD atau nginx sebagai penyalur konten statis seperti HTML, CSS, Javascript, maupun gambar. Untuk menambahkan aplikasi web kita kemudian menggunakan penghubung antar web server dengan program yang dikenal dengan nama CGI (Common Gateway Interface).

CGI diimplementasikan pada web server sebagai antarmuka penghubung antara web server dengan program yang akan menghasilkan konten secara dinamis. Program-program CGI biasanya dikembangkan dalam bentuk script, meskipun dapat saja dikembangkan dalam bahasa apapun. Contoh dari bahasa pemrograman dan program yang hidup di dalam CGI adalah PHP.

Untuk melihat dengan lebih jelas cara kerja CGI, perhatikan gambar berikut:

Apa dua method respon yang paling sering digunakan pada sebuah website yang dinamis

Cara Kerja CGI dan Web Server

Yang dapat kita tarik dari gambar di atas:

  1. Web Server yang berhadapan langsung dengan pengguna, menerima HTTP Request dan mengembalikan HTTP Response.
  2. Untuk konten statis seperti CSS, Javascript, gambar, maupun HTML web server dapat langsung menyajikannya sebagai HTTP Response kepada pengguna.
  3. Konten dinamis seperti program PHP maupun Perl disajikan melalui CGI.
  4. CGI Script kemudian menghasilkan HTML atau konten statis lainnya yang akan disajikan sebagai HTTP Response kepada pengguna.

Meskipun terdapat banyak pengembangan selanjutnya dari CGI, ilustrasi sederhana di atas merupakan konsep inti ketika awal pengembangan CGI. Umumnya aplikasi web dengan CGI memiliki kelemahan di mana menjalankan script CGI mengharuskan web server untuk membuat sebuah proses baru. Pembuatan proses baru biasanya akan menggunakan banyak waktu dan memori dibandingkan dengan eksekusi script, dan karena setiap pengguna yang terkoneksi akan mengakibatkan hal ini terhadap server performa aplikasi akan menjadi kurang baik.

CGI sendiri menyediakan solusi untuk hal tersebut, misalnya FastCGI yang menjalankan aplikasi sebagai bagian dari web server. Bahasa lain juga menyediakan alternatif dari CGI, misalnya Java yang memiliki Servlet. Servlet pada Java merupakan sebuah program yang menambahkan fitur dari server secara langsung. Jadi pada pemrograman dengan Servlet, kita akan memiliki satu web server di dalam program kita, dan pada web server tersebut akan ditambahkan fitur-fitur spesifik aplikasi web kita.

Pada pengembangan aplikasi web dalam NodeJS, pendekatan yang kita gunakan lebih dekat (meskipun tidak sama persis) dengan pendekatan Servlet yang digunakan Java. Untuk mengembangkan aplikasi web pada NodeJS kita akan:

  1. Memanggil web server yang telah disediakan NodeJS.
  2. Membaca URL yang ingin diakses pengguna, dan memanggil fungsi untuk memproses URL tersebut.
  3. Memproses HTTP Request dari client.
  4. Menghasilkan HTTP Response untuk client.

Perbedaan utama antara NodeJS dengan Servlet adalah bahwa NodeJS memiliki pendekatan yang lebih low level, di mana kita harus mengaktifkan web server kita sendiri, dan melakukan penanganan URL, Request, dan Response secara manual. Begitupun, terdapat banyak framework yang telah dikembangkan di atas NodeJS yang membantu kita dalam menangani hal tersebut, misalnya ExpressJS.

Catatan: Perlu ditekankan juga bahwa model pengembangan NodeJS tidak unik terhadpa NodeJS. Banyak bahasa lain yang juga mengadopsi cara yang sama, meskipun umumnya framework pengembangan web telah menutupi detil implementasinya. Contoh bahasa lain yang menggunakan model yang serupa yaitu Java dan Scala (Play Framework), Go (Revel), dan masih banyak lainnya.

Pada bagian ini, kita akan membahas pemanggilan web server standar NodeJS, serta bagaimana memproses HTTP Request dan membuat HTTP Response. Penanganan URL dan pemetaan URL ke fungsi akan kita bahas pada bab berikutnya.

Pembuatan sebuah server HTTP dengan NodeJS sangat sederhana. Buat sebuah file dengan nama server.js pada bagian utama direktori program, dan gunakan kode berikut:

var http = require("http"); var port = process.env.port || 1337; var server = http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); }); server.listen(port, 'localhost');

Kita dapat menjalankan kode di atas dengan menggunakan perintah:

dan ketika berkunjung ke halaman http://localhost:1337 pada browser kita akan melihat sebuah teks Hello World, seperti berikut:

Apa dua method respon yang paling sering digunakan pada sebuah website yang dinamis

Implementasi Awal Web Server NodeJS

Cukup sederhana bukan? Apa yang baru saja kita lakukan?

Pada dua baris pertama:

var http = require("http"); var port = process.env.port || 1337;

kita melakukan persiapan awal. Variabel http akan menampung objek dari modul HTTP standar yang disediakan oleh NodeJS. Fungsi require merupakan implementasi module system dari NodeJS. Sederhananya, require pada NodeJS merupakan import pada Java atau C dan C++.

Kode process.env.port mengambil environment variable PORT dari sistem operasi. Jika nilai tidak ditentukan, kita memasukkan 1337 sebagai nilai standar. Nilai dari port ini nantinya akan kita gunakan sebagai nilai port yang dipantau oleh web server.

Pada bagian berikutnya:

var server = http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); });

Kita memanggil fungsi http.createServer yang akan memberikan kita objek http.Server (dokumentasi http.Server). Objek ini, seperti namanya, merupakan http server yang telah disediakan oleh NodeJS. Fungsi http.createServer menerima sebuah fungsi sebagai parameter, yaitu fungsi yang akan dijalankan setiap kali sebuah request dari client diterima. Fungsi ini memiliki dua buah parameter, yaitu:

  1. request, yang merupakan instan dari http.IncomingMessage, mewakili HTTP Request yang dikirimkan pengguna. Sebuah koneksi bisa saja menghasilkan beberapa HTTP Request. Kita akan membahas bagian ini lebih jauh nantinya.
  2. response, instan dari http.ServerResponse, mewakili HTTP Response yang akan diberikan kepada pengguna.

Tiga baris yang ada di dalam fungsi yang kita tuliskan pada intinya membangun HTTP Response secara dinamis. Pembahasan mengenai pembuatan HTTP Response akan kita bahas pada bagian berikutnya.

Terakhir, kita kemudian menjalankan server dan mendengarkan port yang disimpan pada variabel port:

server.listen(port, 'localhost');

http.Server.listen merupakan fungsi yang mulai mendengarkan dan menunggu koneksi dari client. Dengan menjalankan kode ini kita memastikan server dapat diakses oleh client pada mesin kita.

Untuk pengembangan selanjutnya, kita dapat membuat modul tersendiri untuk server, sehingga kedepannya kita dapat menggabungkan server dengan komponen lain yang pada akhirnya akan menghasilkan sebuah framework lengkap. Bagaimana kita dapat meningkatkan modularitas dari kode server ini?

Untuk bagian ini, kita terlebih dahulu akan berfokus ke bagaimana membuat server dapat dipanggil oleh modul lain melalui require. Pada bagian-bagian selanjutnya, kita baru akan kembali ke modul server untuk menambahkan fungsionalitas dan modularitas pada modul server kita.

Kita dapat memulai dengan memisahkan fungsi anonim yang dikirimkan ke http.createServer ke dalam sebuah fungsi tersendiri, agar nantinya fungsi ini dapat digantikan oleh modul lain:

var onRequest = function (request, response) { response.writeHead(200, { "Content-Type": "text/plain" }); response.write("Hello, World!"); response.end(); }; var server = http.createServer(onRequest);

Kita kemudian dapat membuat sebuah fungsi baru yang betugas menghidupkan server kita:

var start = function () { server.listen(port, 'localhost'); console.log("Server started at port " + port + "."); } exports.start = start;

Memasukkan fungsi start ke dalam property dari objek export di atas akan menyebabkan kita dapat memanggil kode untuk mulai menjalankan server ini dari luar modul. Mari kita coba. Pertama, buat sebuah file index.js dengan isi:

var server = require('./server.js'); server.start();

Kemudian jalankan index.js langsung dengan perintah:

Lalu masuk ke http://localhost:1337. Hasil yang akan kita dapatkan akan sama dengan sebelumnya. Pada bagian ini kita melihat bagaimana:

  1. Kode yang ada di dalam server.js dieksekusi ketika kita memanggil rqeuire('./serverjs').
  2. Ketika kita mengimpor sebuah modul, modul tersebut akan diberikan dalam bentuk objek, yang dapat kita simpan ke sebuah variabel.

Dengan pengetahuan ini, kita dapat menggunakan teknik-teknik perancangan kode umumnya untuk mengembangkan server kita lebih jauh lagi. Tetapi sebelum masuk ke pengembangan lebih dalam, kita akan melihat bagaimana kita dapat memproses request dan response terlebih dahulu.

Pada bagian sebelumnya kita telah melihat bagaimana sebuah HTTP Request dihasilkan dari http.Server, dan merupakan objek http.IncomingMessage. Objek http.IncomingMessage merupakan objek yang unik, karena objek ini dapat dihasilkan oleh dua buah fungsi, dan masing-masing fungsi akan memberikan property yang berbeda kepada objek. Kita akan melihat lebih jauh bagian dari objek ini ketika membahas pembuatan HTTP Request nantinya. Untuk sekarang, sebagai hasil keluaran dari http.Server, objek http.IncomingMessage memiliki beberapa property yang perlu ditelaah, yaitu:

IncomingMessage.method Mengambil method (GET, POST, dst) sebagai string.
IncomingMessage.url URL Request sebagai string (contoh: /user/jwz)
IncomingMessage.headers Sebuah objek yang read only (tidak dapat diubah). Lihat penjelasan di bawah.

Ketiga property ini memiliki kegunaan masing-masing yang sangat dapat membantu kita dalam pengembangan aplikasi web. Akses dari masing-masing property dapat langsung dilakukan dari objek yang diberikan oleh http.server.createServer, seperti berikut:

var http = require('http'); var port = process.env.port || 1337; var onRequest = function (request, response) { console.log(request.headers); }; var server = http.createServer(onRequest); server.listen(port, 'localhost');

Jika kita menjalankan kode di atas dan mengunjungi halaman pada http://localhost:1337/ maka kita akan melihat cetakan JSON pada console seperti berikut:

{ host: 'localhost:1337', connection: 'keep-alive', accept: '*/*', 'user-agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/41.0.2272.89 Safari/537.36', 'accept-encoding': 'gzip, deflate, sdch', 'accept-language': 'id,en;q=0.8,en-US;q=0.6', cookie: 'thelia_cart=546c25890d8bc7.53190152', }

Seperti yang kita lihat dari isi IncomingMessage.headers di atas, HTTP Header yang diberikan berbentuk objek, dengan nama header sebagai property dan nilai (isi) header sebagai nilai property. Dengan akses ketiga nilai ini, kita akan dapat dengan mudah mengembangkan kode dasar untuk aplikasi, misalnya untuk memanggil fungsi yang berbeda-beda untuk setiap method:

1 2 3 4 5 6 7 8 9 10 11 12

var onRequest = function (request, response) { var method = request.method; switch(method) { case "GET": // lakukan sesuatu case "POST": // lakukan sesuatu case "PUT": // lakukan sesuatu } };

Sebelum masuk lebih jauh, kita akan melihat terlebih dahulu tentang pertukaran data dalam HTTP - secara lebih spesifik bagaimana data dikirimkan dalam HTTP.

Data dikirimkan dalam HTTP Request dalam dua cara, tergantung dari method yang dikirimkan:

  1. Melalui URL, dengan parameter yang diberikan. Digunakan oleh GET.
  2. Melalui entity body dalam HTTP Request. Digunakan untuk POST dan PUT.

Pada prakteknya terdapat satu cara lagi untuk mengirimkan data, yaitu melalui cookie, tetapi penggunaan cookie tidak akan terlalu efektif karena cookie dirancang untuk menyimpan data status pengguna. Sekarang mari kita lihat bagaimana cara untuk membaca data pada URL maupun entity body.

Pembacaan data yang dikirimkan melalui URL biasanya dilakukan untuk request dengan method GET. Untuk melihat bagaimana GET mengirimkan data, kita terlebih dahlu harus mengerti tentang sintaks penulisan URL. Secara umum, sebuah URL memiliki sintaks seperti berikut (detil ada pada RFC 1738):

<scheme>://<user>:<password>@<host>:<port>/<path>?<query>#<fragment>

Apa makna dari setiap bagian dari URL di atas? Mari lihat dari tabel di bawah.

Nama Deskripsi Harus ada?
scheme Protokol yang digunakan. Ya
user Nama pengguna. Tidak
password Password untuk nama pengguna. Tidak
host Hostname atau IP. Ya
port Port yang akan diakses. Beberapa protokol memiliki port standar (misal: HTTP = 80). Tergantung Protokol
path Lokasi data pada server Tergantung Protokol
query Digunakan untuk mengirimkan parameter kepada apliikasi. Tidak
fragment Nama dari bagian tertentu pada data (mis: judul pada buku). Tidak

Misalnya jika diterapkan pada browser:

https://bert::8080/data/rahasia?id=scp-0682&access=O5#desc

Bagian utama yang akan kita gunakan untuk membaca data pada method GET yaitu bagian dari query. Sintaks pembuatan query sendiri cukup mudah, yaitu:

?nama-parameter:nilai&nama-parameter:nilai&...

Misalkan pada URL contoh di atas, terdapat dua buah data yang dikirimkan melalui URL, yaitu:

Param. Nilai
id scp-0682
access O5

Untuk pembacaan nilai query kita dapat menggunakan modul url yang telah diberikan oleh NodeJS. Modul ini memiliki tiga fungsi utama:

  1. url.parse, membaca sebuah string URL menjadi objek.
  2. url.format, kebalikan dari url.parse, mengubah objek URL menjadi string.
  3. url.resolve, melengkapi URL sesuai dengan cara browser pada tag a.

Fungsi yang akan kita gunakan untuk membaca nilai query yaitu url.parse, seperti berikut:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

var url = require('url'); var url_obj = url.parse('http://user::8080/pa/th?query=sample&data=123#desc', true); console.log(url_obj); /* { protocol: 'http:', slashes: true, auth: 'user:pass', host: 'contoh.com:8080', port: '8080', hostname: 'contoh.com', hash: '#desc', search: '?query=sample&data=123', query: { query: 'sample', data: '123' }, pathname: '/pa/th', path: '/pa/th?query=sample&data=123', href: 'http://user::8080/pa/th?query=sample&data=123#desc' } */

Seperti yang dapat dilihat pada keluaran objek diatas, property yang ingin kita akses adalah url_obj.query. Fungsi url.parse sendiri dapat menerima tiga buah argumen, yaitu:

  1. Argumen pertama, urlStr, diisikan string URL yang ingin diubah menjadi objek.
  2. Argumen kedua, parseQueryString, sebuah boolean yang digunakan untuk menentukan apakah query string akan diubah menjadi objek atau tidak. Jika tidak diisikan akan dianggap bernilai false.
  3. Argumen ketiga, slashesDenoteHost, jika diisikan dengan true akan membaca //foo/bar menjadi { host: 'foo', pathname: '/bar' } bukan nilai standar { pathname: '//foo/bar' }. Jika tidak diisi dianggap false.

Sehingga jika kita ingin melanjutkan kode di atas dengan mencoba langsung mengambil query kita dapat melakukan pemanggilan terhadap parameternya langsung:

console.log(url_obj.query); // { query: 'sample', data: '123' }

Dengan menggunakan fungsi url.parse, pembacaan nilai parameter pada GET akan menjadi sangat mudah dan sederhana. Kita juga dapat menggunakan property lain untuk berbagai keperluan. Tiga property yang paling menarik dan sering digunakan yaitu pathname (tempat penyimpanan data dalam server), query (query string), dan hash (bagian spesifik dari dokumen).

Beberapa method lain di luar GET mengirimkan data melalui entity body dari sebuah HTTP Request. Untuk mengakses data yang dikirimkan dengan cara ini, kita dapat melakukan buffering data ke dalam sebuah string. Pada NodeJS, data ini dapat dibaca melalui event data di dalam objek http.IncomingMessage. Ketika seluruh data telah selesai dibaca, maka event end akan dieksekusi. Berikut adalah contoh kode untuk pembacaan data:

var data = ''; request.on('data', function (chunck) { data = data + chucnk; }); request.on('end', function () { // lakukan sesuatu terhadap variabel data // yang sudah lengkap });

Dari kode di atas kita dapat melihat bagaimana mengambil data dari entity body cukup mudah. Untuk request seperti PUT yang biasanya dikirimkan dalam bentuk data langsung, misalnya:

PUT /data/1234 HTTP/1.1 [berbagai header] <appointmentRequest> <patient id = "jsmith"/> </appointmentRequest>

yang jika kita ambil datanya menggunakan metode di atas maka variabel data akan berisi string XML sesuai dengan yang dikirimkan. Kita kemudian dapat menggunakan fungsi atau objek khusus untuk memproses string XML tersebut sesuai kebutuhan. Cara pemrosesan ini berlaku juga untuk request POST yang dikirimkan oleh client HTTP, baik dari aplikasi desktop maupun handphone.

Untuk request POST yang dikirimkan melalui form HTML, pemrosesan harus dilakukan dengan cara yang sedikit berbeda. Pengiriman data pada form HTML dikirimkan dengan menggunakan dua format encoding, yaitu:

  1. application/x-www-form-urlencoded, untuk pengiriman data ukuran kecil yang tidak melibatkan file. Format encoding menggunakan cara yang sama dengan query pada method GET.
  2. multipart/form-data, untuk pengiriman data skala besar atau yang melibatkan file. Menggunakan format encoding khusus yang dijelaskan secara rinci dalam RFC 2045 dan RFC 1867. Pembacaan format dapat dilakukan sesuai spesifikasi pada RFC 2388.

Kedua format kode di atas dapat diproses dengan menggunakan beberapa modul yang sudah ada. Pembaca lebih disarankan menggunakan modul yang telah tersedia karena pembacaan format data sesuai dengan spesifikasi RFC merupakan hal yang cukup panjang untuk dilakukan dan dibahas pada tulisan ini. (Perlu dicatat bahwa kesulitan pembahasan terjadi karena panjangnya spesifikasi, bukan sulit atau kompleks).

Untuk application/x-www-form-urlencoded, format data sama persis dengan yang ada dalam GET. Kita dapat menggunakan modul querystring untuk mempermudah ktia dalam memproses data yang datang dengan format ini. Modul querystring memiliki dua buah fungsi utama, yaitu:

  1. querystring.parse, yang mengambil sebuah string query GET dan mengembalikan objek. Misalnya, querystring.parse('a=1&b=2') akan mengembalikan {a: 1, b: 2}.
  2. querystring.stringify, kebalikan dari parse. Mengambil objek dan menjadikannya string. Misalnya, querystring.stringify({a: 1}) akan mengembalikan a=1.

Mari kita lihat bagaimana kita dapat menggunakan querystring untuk membaca data pada entity body. Misalnya sebuah request:

POST /data/mahasiswa HTTP/1.1 [berbagai header] nama=Budi+Hartono&semester=6&jurusan=Teknik+Informatika

dapat kita proses dengan kode berikut:

var data = ""; request.on('data', function (chunck) { data += chunck; }); request.on('end', function () { var mhs = qs.parse(data); console.log(mhs); });

akan memberikan kita keluaran sebuah objek javascript seperti berikut:

{ nama: 'Budi Hartono', semester: '6', jurusan: 'Teknik Informatika' }

Konten yang dikirimkan dengan format multipart/form-data sendiri memiliki cara penyimpanan yang sedikit rumit. Misalnya untuk data yang sama dengan request sebelumnya, tetapi menggunakan format ini, request akan menjadi:

POST /data/mahasiswa HTTP/1.1 [berbagai header] Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW ----WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="nama" Budi Hartono ----WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="semester" 6 ----WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="jurusan" Teknik Informatika ----WebKitFormBoundary7MA4YWxkTrZu0gW

Karena format ini menggunakan penyimpanan yang cukup panjang (terutama jika melibatkan file), maka kita tidak dapat membaca data ini secara langsung. Kita dapat menggunakan berbagai pustaka yang sudah ada untuk melakukan pembacaan data ini, misalnya node-multiparty. Untuk menggunakan node-multiparty, kita dapat terlebih dahulu menambahkan pustaka dengan perintah:

dan kemudian dapat menggunakannya seperti berikut:

var multiparty = require('multiparty'); var form = new multiparty.Form(); form.parse(request, function (err, fields, files)) { console.log({'fields': fields, 'files': files}); });

yang jika dijalankan akan menghasilkan:

{ fields: { nama: [ 'Budi Hartono' ], semester: [ '6' ], jurusan: [ 'Teknik Informatika' ] }, files: {} }

Pembahasan mendetail tentang penggunaan node-multiparty dapat dibaca pada dokumentasi pustaka tersebut. Perlu diingat juga bahwa meskipun format application/x-www-form-urlencoded dan multipart/form-data hanya digunakan untuk HTML Form pada standar, banyak client yang mengirimkan request POST, PUT, dan bahkan DELETE dengan menggunakan kedua format ini tanpa melalui form. Jika aplikasi server yang dikembangkan memang mendukung penggunaan request ini, cara pemrosesan yang digunakan tetap sama (dengan pustaka seperti node-multiparty). Begitupun, tentu saja penggunaan seperti ini tidak disarankan, karena bertentangan dengan standar.

Sampai di sini kita telah melihat bagaimana HTTP Request disimpan oleh NodeJS, serta bagaimana membaca data yang dikirimkan oleh HTTP Request. Sebelum melihat bagaimana membuat HTTP Request secara dinamis, kita akan mempelajari tentang HTTP Response terlebih dahulu pada bagian berikutnya.

Di bagian sebelumnya kita telah melihat bagaimana membaca dan menangani HTTP Request dari server NodeJS. Sekarang kita akan melihat bagaimana membangun HTTP Response yang diberikan oleh server NodeJS. HTTP Response yang diberikan oleh server NodeJS merupakan objek bertipe http.ServerResponse, yang juga mengimplementasikan antarmuka stream.Writable (dokumentasi stream.Writable). Karenanya, penulisan HTTP Response akan menggunakan konsep penulisan stream yang sama dengan bahasa pemrograman pada umumnya.

Secara sederhana, untuk menuliskan data dengan stream.Writable, kita akan melakukan dua hal berurutan:

  1. Menulis stream dengan memanggil fungsi stream.Writable.write. Fungsi menerima dua parameter, yaitu string yang akan ditulis dan encoding dari string yang digunakan.
  2. Menutup stream dengan memanggil fungsi stream.Writable.end. Fungsi tidak menerima parameter.

Pada sebuah kode server sederhana, penulisan respon akan dilakukan seperti berikut:

var http = require("http"); var port = process.env.port || 1337; var server = http.createServer(function(request, response) { response.write("Hello World"); response.end(); }); server.listen(port, 'localhost');

Sebagai tambahan untuk HTTP, http.ServerResponse memiliki beberapa fungsi-fungsi tambahan, yaitu (semua fungsi di bawah adalah untuk objek http.ServerResponse):

  1. writeContinue, memberikan pesan HTTP/1.1 100 Continue kepada client.
  2. writeHead, menuliskan header dari HTTP Response kepada client. Menerima tiga buah argumen, yaitu sebuah angka kode status, string deskripsi status, dan objek header. Contoh penggunaan dapat dilihat pada kode di bawah (setelah daftar fungsi).
  3. setHeader, mengisikan nilai sebuah header. Jika header telah dibuat dengan writeHead atau setHeader sebelumnya maka nilai akan ditimpakan. Menerima dua buah argumen, yaitu string nama header dan string nilai header.
  4. getHeader, mengambil isi dari header (case insensitive) sebelum dikirimkan ke pengguna. Menerima satu parameter, yaitu string nama header.
  5. removeHeader, menghapus header yang telah dibuat sebelumnya.
  6. addTrailers, menambahkan HTTP Trailing Header (header yang berada di belakang message) kepada HTTP Response. Fungsi ini memiliki dua prasyarat: hanya untuk pesan HTTP 1.1 dan pesan harus memiliki header Trailer. Jika kedua syarat tidak terpenuhi, fungsi akan diabaikan. Menerima parameter berupa objek dengan format seperti pada fungsi writeHead.

Penggunaan semua fungsi-fungsi yang dijelaskan di atas cukup gamblang. Misalnya, untuk writeHead:

response.writeHead(200, 'OK', { 'Content-Length': content.length, 'Content-Type': 'text/plain' });

Daftar dari HTTP Header sendiri dapat ditemukan pada berbagai sumber, misalnya: RFC 2616, Wikipedia, dan Lampiran C (masih dalam pengembangan).

Perlu diingat juga bahwa HTTP Header harus selalu ditulis sebelum entity body dituliskan, karena http.ServerResponse merupakan stream yang harus dituliskan secara berurutan. Hal ini berarti konstruksi header HTTP dapat dilakukan dengan cukup mudah, hanya dengan mengikuti urutan penulisan pesan, yaitu:

  1. Panggil writeHeader untuk menuliskan HTTP Response Starting Line dan Header.
  2. Panggil write untuk menuliskan entity body, jika diperlukan kita juga dapat mengirimkan konten file dari sini.
  3. Panggil end untk mengirimkan kepada pengguna.

Misalnya, kita dapat menggunakan modul fs untuk membaca file dan menuliskan kontennya seperti berikut:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

var fs = require('fs'); var file = fs.createReadStream('./index.html'); var content = ''; file.on('data', function (data) { content += data; }); file.on('end', function () { result = content; response.writeHead(200, 'OK', { 'Content-Type': 'text/html', 'Content-Length': result.length }); response.write(result); response.end(); });

Seperti yang dapat dilihat pada kode di atas, penulisan HTTP Response cukup gamblang dan sederhana. Selanjutnya, kita akan melihat pembuatan sebuah HTTP Client dengan NodeJS, yang akan memerlukan kebalikan dari server NodeJS, yaitu pembuatan HTTP Request secara dinamis dan pembacaan HTTP Response.

Selain menyediakan fasilitas untuk membuat server HTTP lengkap dengan pembacaan HTTP Request dan pembuatan HTTP Response, NodeJS juga menyediakan fasilitas untuk membuat sebuah HTTP Client. Fitur-fitur yang diberikan untuk HTTP Client pada NodeJS berfokus pada pembuatan HTTP Request dan pembacaan HTTP Response, kebalikan dari yang dilakukan oleh HTTP Server.

HTTP Client sendiri memiliki sangat banyak kegunaan, misalnya untuk mengambil data dari server lain untuk diproses lebih lanjut dari sisi server. Ketika mengembangkan aplikasi dari sisi client, kegunaan HTTP Client tentunya sudah tidak perlu dipertanyakan lagi. Aplikasi modern umumnya akan selalu berhubungan dengan server dalam bentuk apapun, dari HTTP sampai ke protokol buatan sendiri.

Fungsi utama yang kita gunakan untuk HTTP Client pada NodeJS yaitu http.request. Fungsi ini menerima dua buah argumen, yaitu:

  1. options. Parameter ini dapat berupa string maupun objek. Ketika berisi string, fungsi akan membaca parameter ini sebagai URL yang ingin dituju oleh client. URL akan dibaca dengan url.parse. Jika parameter ini berisi objek, kita dapat mengisikan data query yang ingin kita lakukan. Keterangan lebih lanjut ada pada bagian berikutnya.
  2. callback. Parameter diisikan dengan fungsi yang akan dijalankan ketika HTTP Response diberikan oleh server penerima request. Fungsi memiliki satu parameter, yaitu instan http.IncomingMessage dengan dua property tambahan, yaitu http.IncomingMessage.statusCode dan http.IncomingMessage.statusMessage. Kedua property berisi data yang sama persis dengan namanya.

Objek yang dapat kita masukkan ke dalam options memiliki beberapa property yang dapat kita isikan, yaitu:

  • host, nama domain atau IP server tujuan. Nilai standar adalah “localhost”.
  • hostname, sama seperti host. Gunakan nilai ini untuk mendukung url.parse.
  • port, port dari server yang dituju. Nilai standar adalah 80.
  • localAddress, antarmuka lokal yang ingin diikatkan pada koneksi lokal.
  • socketPath, Unix Domain Socket (gunakan salah satu dari host:port atau socketPath).
  • method, method dari HTTP Request. Nilai standar adalah GET.
  • path, path dari request. Nilai standar adalah /. Jika ada, query string juga diikutkan, misalnya: index.html?data=123. Jika terdapat karakter ilegal sebuah Exception akan dilemparkan. Untuk sekarang, karakter ilegal hanya spasi.
  • headers, sebuah objek yang berisi header HTTP.
  • auth, informasi autentikasi dengan HTTP Basic Authentication (user:pass) jika server memerlukan informasi ini.
  • agent, digunakan untuk mengatur HTTP Agent. Pembahasan HTTP Agent akan dilakukan pada bagian lain.
  • keepAlive, sebuah boolean untuk menentukan apakah koneksi tetap disimpan untuk request selanjutnya. Nilai standar adalah false.
  • keepAliveMsecs, menentukan waktu koneksi akan tetap dibuka, dalam ukuran milisecond. Nilai standar adalah 1000. Property ini hanya relevan jika keepAlive bernilai true.

Catatan terakhir, fungsi http.request akan mengembalikan http.ClientRequest, sebuah objek stream.Writable. Jika kita mengirimkan file atau data melalui request POST, data akan diisikan pada stream ini.

Sekarang mari kita lihat contoh penggunaan fungsi http.request:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

var http = require('http'); var options = { hostname: 'bertzzie.com', port: 80, path: '/', method: 'GET' }; var req = http.request(options, function (response) { console.log(response.statusCode); console.log(response.statusMessage); console.log(response.headers); }); req.end();

Kode di atas terlihat cukup sederhana dan gamblang. Satu-satunya hal yang perlu dicatat adalah bahwa pada akhir request, kita harus memanggil req.end untuk menandakan bahwa request telah selesai. Hal ini terutama sangat berguna jika kita mengirimkan data tambahan ke server, seperti berikut:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

var http = require('http'); var querystring = require('querystring'); var data = querystring.stringify({ 'pesan': 'Halo' }); var options = { hostname: 'contoh.com', port: 80, path: '/upload', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': data.length } }; var req = http.request(options, function (response) { console.log(response.statusCode); console.log(response.statusMessage); console.log(response.headers); }); req.write(data); req.end();

Dari contoh kode di atas, kita dapat melihat bagaimana kita memanggil contoh.com/upload dan memasukkan data ke dalam request melalui req.write. Seperti stream pada umumnya, penulisan baru akan dieksekusi ketika kita memanggil res.end sebagai penanda stream telah habis. Hal ini lah yang mengakibatkan kita harus memanggil res.end, baik dalam keadaan data kosong maupun berisi.

Untuk menangani error pada pengiriman request, misalnya jika koneksi terputus atau terdapat pelanggaran protokol HTTP, kita dapat mendengarkan event error pada objek http.ClientRequest seperti berikut:

req.on('error', function (e) { console.log('Terjadi kesalahan ' + e.message); });

Fitur lain dari HTTP Client pada NodeJS yaitu fungsi yang membantu kita dalam membuat request GET dengan mudah, yaitu http.get. Fungsi ini memiliki parameter yang sama dengan http.request, dengan perbedaan utama yaitu:

  1. http.get akan selalu mengirimkan request GET.
  2. http.get tidak memerlukan pemanggilan req.end sebagai penanda akhir request.

Penggunaan dari http.get sama persis dengan http.request, misalnya:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

var http = require('http'); var qs = require('querystring'); var options = { host: 'www.google.com', port: 80, path: '/?' + qs.stringify({'q': 'tutorial javascript'}) }; var req = http.get(options, function(response) { var data = ''; response.on('data', function(chunk) { data += chunk; }); response.on('end', function() { console.log(data); }); }); req.on('error', function(e) { console.log("Error: " + e.message); });

Contoh di atas mengakhiri pembahasan kita mengenai HTTP Client dengan NodeJS.

Sampai pada bagian ini kita telah melihat bagaimana NodeJS berinteraksi dengan HTTP, baik sebagai HTTP Server maupun sebagai HTTP Client. Kita belum dapat membangun aplikasi web yang terlalu kompleks dengan apa yang kita pelajari sejauh ini, tetapi kita telah dapat berkomunikasi dengan berbagai HTTP Client maupun Server secara singkat. Meskipun abstraksi yang digunakan belum terlalu mendalam, pada bagian selanjutnya, yaitu routing, kita akan mempelajari lebih lanjut tentang URL, pembangunan URL dinamis, dan abstraksi (framework) aplikasi web secara lebih lanjut.