Mengatasi Isu Cross-Origin Resource Sharing (CORS)

Ceritanya kemarin saya sedang mengembangkan sebuah aplikasi web bersama teman-teman seangkatan saya. Saya kebagian in charge di tim back end. Saat itu saya sedang mengerjakan halaman “Tentang Kami” yang berisi profil-profil anggota tim pengembang.

Aplikasi ini dikembangkan dengan menggunakan WordPress. Kebetulan, profil user di WordPress ditangani oleh Globally Recognized Avatars (Gravatar) yang berarti tentunya, detail profil user yang ada seperti foto, facebook, twitter dan lainnya harus di-fetch dari situs Gravatar tersebut.

Syukurnya, Gravatar punya API yang dapat digunakan oleh publik. Jadi saya gunakan saja API tersebut untuk mengambil data.

about-us-page

Tampilan halaman “Tentang Kami” yang sedang dikerjakan.

Sebagai informasi, jumlah tim kami ada 11 orang, dengan kata lain saya harus melakukan perulangan 11 kali untuk mengambil data profil dari situs tersebut. Belum lagi untuk menampilkan profil kontributor yang nanti jumlahnya bisa banyak. Pengambilan data akan dilakukan dengan menggunakan PHP biasa. Jadi, implementasinya waktu itu seperti ini:

foreach ($users as $user) {
    $str = file_get_contents( 'http://www.gravatar.com/' . md5($user->user_email) . '.php' );
    $profile = unserialize( $str );
    if ( is_array( $profile ) && isset( $profile['entry'] ) )
        /* Action to display profile... */
}

Saat dicoba, ternyata proses loading-nya lama sekali. -_- Saya baru sadar, proses pengambilan data oleh PHP dilakukan secara berurut. Dengan kata lain, profil si user akan diambil satu-satu, dan prosesnya harus ditunggu sampai selesai baru bisa mengambil profil lainnya. Kalau misalnya ada masalah di koneksi pada saat melakukan fetching salah satu profile, tentu saja semuanya jadi ikut bermasalah. Belum lagi kalau misalnya nanti jumlah user yang ada menjadi lebih banyak seiring berjalannya waktu. Mau berapa lama loading-nya?

Karena itu, supaya aman, proses fetching informasi profile dari Gravatar harus dilakukan secara paralel. Masalahnya, PHP tidak mendukung multithread secara default. Jadi, bagaimana?

Tentu saja, JavaScript!

Dengan menggunakan jQuery, profilnya akan diambil secara asynchronous, jadi tampilan halaman akan muncul terlebih dahulu, baru kemudian profilnya akan di-append satu-satu ke halaman tersebut. Untungnya Gravatar menyediakan API yang dapat digunakan untuk mengambil data dalam format JSON. Kodenya jadi begini:

$(document).ready(function() {
    $.get('http://www.gravatar.com/' + userMd5Hash + '.json', function(data) {
        /* Operation to parse JSON (emitted). */
        $("#" + userMd5Hash + "-accounts").append(/* ... */);
    });
});

Saat dicoba dijalankan, tiba-tiba saya mendapatkan pesan error di jendela console. Tampilannya mirip seperti di bawah ini:

cors-error

Nah, ada apa lagi ini? -_-

Ternyata, operasi GET menggunakan AJAX dengan menggunakan jQuery yang saya lakukan terbentur oleh same origin policy. Ini ada apa lagi ya? o.O

Ternyata..

Jadi, saya mengerjakan halaman ini di localhost, sedangkan profil yang ingin saya ambil, ada di alamat domain gravatar.com yang tentunya berbeda lokasinya. Secara defaultweb browser tidak mengizinkan langsung operasi pengambilan data yang berbeda domain seperti ini untuk alasan keamanan.

Setelah googling beberapa saat, ternyata untuk mengatasinya, harus dilakukan penyetelan yang memungkinkan untuk melakukan Cross-Origin Resource Sharing (CORS). Untuk melakukannya, ada 2 pilihan yang dapat dilakukan:

Enable CORS di Server Tujuan

Untuk dapat melakukan CORS, pemilik server harus menambahkan header khusus di setiap response yang dikembalikan dari tiap request yang diberikan yaitu:

Access-Control-Allow-Origin: *

Jika header ini tidak didapatkan di response yang dikirimkan balik, maka browser akan memberikan pesan error seperti yang ada di atas tadi.

API di website lain seperti GitHub misalnya, sudah mengizinkan CORS secara default (lihat gambar di bawah, dan perhatikan kotak merah):

github-response-header

Sedangkan, Gravatar tidak mengizinkannya (lihat gambar di bawah):

gravatar-response-header

Karena saya tidak mungkin mengotak-atik API server milik Gravatar, ya apa boleh buat. -_- Tapi untungnya, di sisi client ada hal lain yang dapat dilakukan, yaitu:

Gunakan JSON with padding (JSONP)

Teknik yang dapat dilakukan untuk mengatasi ini adalah menggunakan JSONP. Implementasinya, cukup dengan mengganti rutin jQuery yang digunakan, dan mengubah URL request dengan menambahkan parameter callback.

$(document).ready(function() {
    $.getJSON('http://www.gravatar.com/' + userMd5Hash + '.json?callback=?', function(data) {
        /* Operation to parse JSON (emitted). */
        $("#" + userMd5Hash + "-accounts").append(/* ... */);
    });
});

Fiuh, dan akhirnya berhasil juga.😀

Syukur sekali saya mendapatkan masalah ini. Kebetulan, aplikasi yang sedang saya kerjakan dan skripsi saya saat ini banyak berhubungan dengan RESTful API, dan isu seperti CORS seperti ini pasti akan sangat membingungkan nanti untuk orang yang menggunakan API yang saya kembangkan. Jadi, untuk pengembang REST API, sepertinya isu CORS seperti ini patut untuk diperhatikan.

Demikian cerita ini, mudah-mudahan bermanfaat untuk pembaca ya.🙂

One comment

  1. cls · June 5

    Sangat membantu, terimakasih..

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s