variables dan local: mengelak data bocor antara pengguna
Kenapa komponen yang menyimpan data pengguna dalam variables boleh bocor antara permintaan, cara mengesannya, dan corak selamat untuk keadaan objek.

Kandungan
- variables dan local: mengelak data bocor antara pengguna
- Dua tempat, dua jangka hayat
- Kebocoran yang sebenar
- Bila variables betul
- Corak selamat untuk keadaan permintaan
- Skop dalam skrip berbanding dalam fungsi
- Mengapa ujian tidak menangkapnya
- Komponen dalam application scope
- Mengapa cache dalaman selalunya selamat
- Tanda amaran dalam kod
- Satu kes yang pernah berlaku
- Soalan lazim
- Adakah var dalam cfscript sama dengan local?
- Bolehkah saya kosongkan variables pada akhir setiap panggilan?
- Adakah masalah ini berlaku dalam ColdFusion juga?
- Bagaimana menguji komponen yang menyimpan cache dalaman?
variables dan local: mengelak data bocor antara pengguna
Ini bahagian ketiga siri komponen. Bahagian pertama membincangkan asas .cfc, bahagian kedua kaedah dan argumen. Bahagian ini tentang kesilapan yang paling mahal: menyimpan data yang salah pada tempat yang salah.
Kesannya bukan ralat. Kesannya ialah pengguna A melihat data pengguna B, dan tiada apa dalam log yang memberi amaran.
Dua tempat, dua jangka hayat
Dalam komponen, variables ialah objek itu sendiri. Selagi objek hidup, nilai di dalamnya hidup. local pula hanya wujud dalam satu panggilan kaedah; apabila kaedah tamat, nilainya hilang.
Perbezaan ini tidak penting jika objek dibina baru pada setiap permintaan, kerana objek itu mati bersama permintaan. Ia menjadi penting apabila objek disimpan lebih lama, contohnya dalam application scope atau dalam cache.
Kebocoran yang sebenar
Komponen di bawah sengaja ditulis dengan cara yang salah, kemudian diuji dengan dua "pengguna".
component output="false" {
public string function prosesSalah(required string pengguna) {
variables.penggunaSemasa = arguments.pengguna;
return "diproses untuk " & variables.penggunaSemasa;
}
public string function siapaSalah() {
return variables.penggunaSemasa ?: "(kosong)";
}
}
Ujian menggunakan satu objek, sama seperti komponen dalam application scope.
b = new komponen.Bocor();
b.prosesSalah("A");
writeOutput(b.siapaSalah()); // A
b.prosesSalah("B");
writeOutput(b.siapaSalah()); // B
Output sebenar: A kemudian B. Setakat ini kelihatan normal. Yang menarik ialah apabila satu kaedah lain dipanggil selepas itu.
b.prosesBetul("C");
writeOutput(b.siapaSalah()); // masih B
Objek itu masih menyimpan B walaupun permintaan untuk C sudah selesai. Dalam aplikasi sebenar, siapaSalah() mungkin dipanggil oleh permintaan lain, dan pengguna itu akan menerima nama pengguna sebelumnya.
Bila variables betul
variables bukan musuh. Ia tempat yang betul untuk perkara yang memang milik objek sepanjang hayatnya:
- konfigurasi yang dibaca sekali, contohnya kadar cukai atau had muat naik
- cache dalaman dengan kunci yang jelas
- sambungan kepada perkhidmatan luar, jika ia selamat dikongsi
Yang salah ialah menyimpan data permintaan di situ: pengguna semasa, id sesi, kandungan borang, atau apa-apa yang berubah mengikut siapa yang memanggil.
Corak selamat untuk keadaan permintaan
Ada tiga cara yang biasa dan semuanya lebih baik daripada variables.
Pertama, hantar melalui argumen. Kaedah menerima data yang diperlukan dan tidak menyimpan apa-apa.
public struct function proses(required struct pengguna, required struct pesanan) {
return { ok: true, pemilik: arguments.pengguna.id, jumlah: arguments.pesanan.jumlah };
}
Kedua, bina objek baharu bagi setiap permintaan. Jika komponen membawa keadaan, jangan simpan dalam application. Bina dengan new di dalam pengendali.
Ketiga, gunakan struct berasingan sebagai konteks. Satu struct dicipta bagi setiap permintaan dan dihantar kepada kaedah yang memerlukannya. Ini berguna apabila banyak kaedah berkongsi beberapa nilai.
konteks = { penggunaId: session.userId, masa: now() };
hasil = svc("pesanan").senarai(konteks);
Skop dalam skrip berbanding dalam fungsi
Dalam cfscript, skop boleh menjadi kabur kerana semuanya kelihatan seperti pemboleh ubah biasa. Tiga garis panduan membantu.
Dalam fail .cfm di peringkat atas, pemboleh ubah tanpa var masuk ke variables fail itu, yang sebenarnya request-like dan dikongsi sepanjang permintaan. Dalam fungsi, var meletakkannya dalam local.
Dalam komponen, this ialah bahagian awam objek. Menulis this.pengguna = x mendedahkan nilai itu kepada sesiapa yang memegang objek, dan ia kekal seperti variables. Jika nilai itu rahsia, jangan letak dalam this.
Dalam kaedah yang memanggil kaedah lain, arguments kaedah luar tidak diwarisi. Kaedah dalaman mempunyai arguments sendiri, dan percubaan membaca arguments.x daripada kaedah luar akan gagal dengan mesej kunci tidak wujud. Hantar nilai sebagai argumen, bukan bergantung pada skop.
Mengapa ujian tidak menangkapnya
Ujian unit biasa memanggil satu kaedah dengan satu pengguna. Kebocoran hanya muncul apabila dua permintaan berturut-turut menggunakan objek yang sama. Jadi ujian mesti meniru urutan itu.
it("tidak menyimpan pengguna antara panggilan", function () {
var b = new komponen.Bocor();
b.prosesSalah("A");
b.prosesSalah("B");
expect(b.siapaSalah()).toBe("B"); // ini lulus, tetapi menunjukkan risiko
});
Ujian di atas lulus kerana ia menguji tingkah laku yang salah. Ujian yang berguna ialah memastikan komponen tidak menyimpan apa-apa langsung: selepas panggilan, tiada kunci pengguna dalam variables.
Komponen dalam application scope
Menyimpan komponen dalam application ialah cara lazim untuk mengelak pembinaan berulang. Ia selamat jika komponen itu tidak menyimpan keadaan permintaan. Ia berbahaya jika tidak.
Sebelum menyimpan komponen dalam application, tanya satu soalan: jika seratus pengguna memanggilnya serentak, adakah mana-mana nilai di dalamnya berubah mengikut pengguna? Jika ya, jangan simpan, atau pastikan nilai itu dihantar sebagai argumen.
Untuk komponen yang hanya membaca konfigurasi, menyimpannya dalam application mengurangkan kerja. Untuk komponen yang memproses pesanan, ia mengundang masalah.
Mengapa cache dalaman selalunya selamat
Cache ialah contoh variables yang betul, dengan satu syarat: kunci mesti lengkap.
public struct function kadar(required string negara) {
if (!structKeyExists(variables, "kadarCache")) variables.kadarCache = {};
if (structKeyExists(variables.kadarCache, arguments.negara)) {
return variables.kadarCache[arguments.negara];
}
var nilai = svc("cukai").ambil(arguments.negara);
variables.kadarCache[arguments.negara] = nilai;
return nilai;
}
Cache ini selamat kerana hasilnya bergantung hanya kepada negara, bukan kepada siapa yang bertanya. Jika kunci cache melibatkan pengguna, contohnya variables.terakhirDilihat, barulah ia menjadi data permintaan yang menyamar sebagai cache.
Ujian ringkas: jika dua pengguna bertanya soalan yang sama, patutkah jawapannya sama? Jika ya, cache selamat. Jika tidak, ia bukan cache.
Tanda amaran dalam kod
Beberapa corak yang patut membuat anda berhenti dan menyemak:
variables.pengguna,variables.session,variables.formdalam komponen yang dikongsi- kaedah yang menetapkan
variables.xpada awal dan membacanya dalam kaedah lain - komponen yang "lupa" nilai antara dua permintaan, kemudian dibetulkan dengan menetapkan semula pada setiap panggilan
- ujian yang lulus apabila dijalankan bersendirian tetapi gagal apabila dijalankan bersama
Tanda terakhir itu paling kuat. Kebocoran keadaan hampir selalu muncul sebagai kegagalan yang bergantung kepada urutan.
Satu kes yang pernah berlaku
Sebuah laporan inventori dibina sebagai komponen yang disimpan dalam application scope untuk mengelak pembinaan semula. Laporan itu menyimpan variables.cawangan semasa memproses setiap baris, kemudian mencetak pengepala menggunakan nilai yang sama.
Pada mesin pembangun, semuanya betul kerana hanya satu pengguna menguji. Pada pelayan, dua pengguna membuka laporan hampir serentak, dan pengepala laporan pengguna kedua muncul dalam laporan pengguna pertama. Tiada ralat, tiada pengecualian, dan tiada log. Yang ada hanya aduan bahawa "laporan kadang-kadang salah".
Pembetulannya kecil: cawangan dijadikan argumen kepada kaedah yang mencetak pengepala, dan nilai itu tidak lagi disimpan pada objek. Perubahan tiga baris, tetapi ia menghapuskan kelas peputipan yang sukar diulang.
Kesimpulannya bukan "jangan guna application scope". Kesimpulannya ialah keadaan yang berubah mengikut pemanggil tidak boleh tinggal pada objek yang dikongsi.
Soalan lazim
Adakah var dalam cfscript sama dengan local?
Ya. var x = 1 di dalam fungsi meletakkan x dalam skop local. Menulis local.x = 1 memberi hasil yang sama, dan sesetengah pasukan memilihnya kerana lebih jelas dibaca.
Bolehkah saya kosongkan variables pada akhir setiap panggilan?
Boleh, tetapi itu membetulkan gejala, bukan punca. Jika nilai tidak sepatutnya disimpan, jangan simpan sejak awal.
Adakah masalah ini berlaku dalam ColdFusion juga?
Ya. Peraturan skop ini sama dalam Adobe ColdFusion dan Lucee. Yang berbeza hanya beberapa butiran pelaksanaan.
Bagaimana menguji komponen yang menyimpan cache dalaman?
Uji dengan dua set data berbeza dan pastikan kunci cache tidak bertembung. Jika cache menggunakan kunci pengguna, pastikan kunci itu sentiasa dihantar, bukan diambil daripada keadaan objek.
Bahagian keempat membincangkan warisan, extends dan implements, termasuk bila mewarisi membantu dan bila ia hanya memindahkan masalah ke tempat lain. Bahagian pertama siri ini ada di Komponen CFML: apa itu .cfc.