
Dalam
posting saya
sebelumnya, saya menggambarkan poin utama ketika mengembangkan
perpustakaan opensource lain. Saya lupa menyebutkan satu hal lagi: jika Anda tidak memberi tahu siapa pun tentang perpustakaan, apa pun itu, kemungkinan besar tidak ada yang akan mengetahuinya.
Jadi, temui
trava.js - validasi juicy untuk kepentingan proyek. Ngomong-ngomong, kami telah menggunakan rumput selama lebih dari enam bulan, dan saya pikir sudah waktunya untuk memberi tahu Anda tentang manfaat menggunakannya. Bahkan sudah kering, jadi tahan napas. Dan lanjutkan.
Konsep
Sepintas, tampaknya validasi adalah topik sepele yang tidak memerlukan perhatian khusus. Nilainya benar atau tidak, yang bisa lebih sederhana:
function validate (value) {
Tetapi biasanya akan menyenangkan untuk mengetahui apa yang salah:
function validate (value) { if (!check1(value)) return 'ERROR_1'; if (!check2(value)) return 'ERROR_2'; }
Sebenarnya itu saja, masalahnya selesai.
Jika bukan karena satu "tetapi."
Dari pengalaman mengembangkan aplikasi nyata, diketahui bahwa masalah ini tidak berakhir dengan validasi. Biasanya, data ini juga perlu dikonversi ke format tertentu, karena alasan tertentu tidak didukung oleh serializer di luar kotak, misalnya tanggal, set, atau tipe data khusus lainnya. Mengingat ini terutama JSON, dalam praktiknya ternyata Anda harus melakukan double pass melalui struktur data input selama validasi dan transformasi. Idenya muncul, mengapa tidak menggabungkan dua tahap ini menjadi satu. Kemungkinan plus juga akan menjadi keberadaan skema data deklaratif eksplisit.
Untuk mendukung konversi nilai ke format tertentu, validator harus dapat mengembalikan tidak hanya kesalahan, tetapi juga nilai yang dikurangi. Di dunia js, beberapa opsi antarmuka cukup umum dengan kemungkinan pengembalian kesalahan.
- Mungkin yang paling umum adalah kembalinya tuple [error, data]:
function validate (value) { if (!check1(value)) return ['ERROR_1']; if (!check2(value)) return ['ERROR_2']; return [null, value]; }
Ada juga opsi serupa di mana bukan array dikembalikan, tetapi objek {error, data} , tetapi tidak ada perbedaan mendasar. Keuntungan dari pendekatan ini adalah kejelasan, minusnya adalah bahwa sekarang di mana pun Anda perlu mempertahankan kontrak ini. Untuk validasi, ini tidak menyebabkan ketidaknyamanan, tetapi untuk transformasi ini jelas berlebihan.
- Gunakan pengecualian. Meskipun menurut saya kesalahan validasi adalah situasi standar dalam aplikasi, tidak ada yang luar biasa. Sejujurnya, saya berpikir bahwa pengecualian paling baik digunakan hanya jika ada sesuatu yang salah. Selain itu, pengecualian dapat secara tidak sengaja disebut di validator sendiri, dan kemudian Anda mungkin tidak tahu sama sekali bahwa itu adalah kesalahan dalam kode, dan bukan dalam nilainya. Keuntungan dari pendekatan ini adalah penyederhanaan antarmuka - sekarang selalu nilainya dikembalikan dengan cara biasa, dan kesalahan dilemparkan sebagai pengecualian.
- Ada opsi untuk menempatkan kesalahan dalam variabel global. Tapi saya tidak akan menarik negara tidak perlu.
- Gunakan tipe terpisah untuk kesalahan. Sepertinya opsi dengan pengecualian, jika Anda mengambil jenis kesalahan dari mereka, tetapi jangan membuangnya.
function validate (value) { if (!check1(value)) return new Trava.ValidationError({ code: 401 }); if (!check2(value)) return new Trava.ValidationError({ code: 405 }); return parseOrTransform(value);
Saya memilih opsi yang terakhir, meskipun ini juga kompromi, tetapi secara keseluruhan tidak buruk.
Trava.ValidationError diusulkan sebagai jenis kesalahan, yang mewarisi dari
Kesalahan standar dan menambahkan kemampuan untuk menggunakan tipe data sewenang-wenang untuk melaporkan kesalahan. Tidak perlu menggunakan
Trava.ValidationError , Anda dapat menggunakan
Kesalahan standar, tetapi jangan lupa bahwa kemudian pesan kesalahan hanya string.
Untuk meringkas, kita dapat mengatakan bahwa validator adalah fungsi yang bersih dan sinkron yang, selain nilai, dapat mengembalikan kesalahan. Sangat sederhana. Dan teori ini bekerja dengan baik tanpa perpustakaan. Dalam praktiknya, validator digabungkan ke dalam rantai dan hierarki, dan di sini rumput pasti akan berguna.
Komposisi
Mungkin komposisi adalah kasus paling umum bekerja dengan validator. Implementasi komposisi mungkin berbeda. Misalnya, di
pustaka joi dan
v8n yang terkenal
, ini dilakukan melalui objek dan serangkaian metode:
Joi.string().alphanum().min(0).max(255)
Meskipun terlihat indah pada pandangan pertama, pendekatan ini memiliki beberapa kelemahan, dan satu fatal. Dan inilah masalahnya. Dalam pengalaman saya, validator selalu menjadi hal untuk aplikasi spesifik, jadi fokus utama di perpustakaan harus pada kenyamanan memperluas validator dan integrasi dengan pendekatan yang ada, dan bukan pada jumlah primitif dasar, yang, menurut saya, hanya menambah bobot ke perpustakaan, tetapi sebagian besar tidak akan digunakan. Ambil contoh validator yang sama untuk string. Kemudian ternyata Anda perlu memangkas spasi dari ujungnya, lalu tiba-tiba Anda perlu mengizinkan penggunaan karakter khusus dalam satu kasus tunggal, dan di suatu tempat Anda perlu mengarah ke huruf kecil, dll. Bahkan, mungkin ada banyak primitif seperti itu, dan saya hanya tidak melihat gunanya bahkan mulai menambahkannya ke perpustakaan. Menurut pendapat saya, penggunaan objek juga berlebihan dan mengarah pada peningkatan kompleksitas selama ekspansi, meskipun pada pandangan pertama tampaknya membuat hidup lebih mudah. Misalnya, c
joi tidak begitu mudah
untuk menulis validator Anda .
Pendekatan fungsional dan rumput di sini dapat membantu. Contoh yang sama untuk memvalidasi angka yang ditentukan dalam rentang dari 0 hingga 255:
Pernyataan
Periksa membuat validator keluar dari pemeriksaan kebenaran (nilai => benar / salah). Dan
menulis rantai validator. Ketika dieksekusi, rantai terputus setelah kesalahan pertama. Yang penting adalah bahwa fungsi-fungsi biasa digunakan di mana-mana, yang sangat mudah untuk diperluas dan digunakan. Menurut saya, kemudahan ekspansi ini adalah fitur kunci dari pustaka validasi yang valid.
Secara tradisional, tempat terpisah dalam validasi ditempati dengan memeriksa
nol dan
tidak terdefinisi . Ada operator pembantu di rumput untuk ini:
Ada beberapa operator pembantu lagi di rumput, dan mereka semua menyusun dengan indah dan mengejutkan hanya berkembang. Seperti fungsi biasa :)
Hierarki
Tipe data sederhana disusun dalam hierarki. Kasing yang paling umum adalah objek dan array. Ada operator di rumput yang membuatnya lebih mudah untuk bekerja dengan mereka:
Ketika memvalidasi objek, diputuskan untuk menekankan keparahan definisi: semua kunci diperlukan secara default (dibungkus dalam
Wajib ). Kunci yang tidak ditentukan dalam validator dibuang.
Beberapa
jsonschema , solusi
kuartet lebih suka menggambarkan validator dalam bentuk data, misalnya {x: 'number', y: 'number'}, tetapi ini mengarah pada kesulitan yang sama ketika berkembang. Keuntungan yang signifikan dari pendekatan ini adalah kemungkinan serialisasi dan pertukaran sirkuit, yang tidak mungkin dengan fungsi. Namun, ini dapat dengan mudah diimplementasikan di atas antarmuka fungsional. Tidak perlu menyembunyikan fungsi di belakang garis! Fungsi sudah memiliki nama dan hanya itu yang diperlukan.
Untuk kemudahan penggunaan di dalam validator, operator
Compose dan
Keys dapat dihilangkan, juga nyaman untuk membungkus validator root di
Trava :
const pointValidator = Trava({
Jika Anda memanggil
Trava dengan argumen kedua, maka nilai balik akan menjadi hasil penerapan validator:
const point = Trava({ x: [numberValidator, Trava.Check(v => v > 180)], y: [numberValidator, Trava.Check(v => v < 180)], },
Sejauh ini, dukungan telah diterapkan hanya untuk array dan objek, seperti pada dasarnya meracuni JSON dan itu sudah cukup. Tarik Permintaan untuk Wellcome!
Konteks
Saat menggunakan validator sebagai parameter terakhir, Anda dapat melewati konteks yang akan dapat diakses dari semua validator yang disebut sebagai parameter terakhir. Secara pribadi, kesempatan ini belum berguna bagi saya, tetapi itu mungkin.
Untuk beberapa validator yang mungkin mengembalikan kesalahan, dimungkinkan untuk menentukan pesan kesalahan di tingkat yang berbeda. Contoh:
const pos = Trava.Check(v => v > 0); pos(-1);
Ganti untuk satu kasus:
const pos = Trava.Check(v => v > 0, " "); pos(-1);
Ganti untuk semua kasus:
Trava.Check.ErrorMessage = " "; pos(-1);
Selain itu, untuk konfigurasi yang lebih terperinci, Anda dapat mentransfer fungsi di tempat kesalahan, yang seharusnya mengembalikan kesalahan dan akan dipanggil dengan parameter validator.
Gunakan kasing
Sebagian besar kita meracuni JSON di backend bersama dengan koa. Frontend juga duduk perlahan. Lebih mudah memiliki validator bersama di kedua ujungnya. Dan sekarang saya akan menunjukkan kasus penggunaan yang hampir nyata. Misalkan Anda ingin menerapkan API untuk membuat dan memperbarui data pasien.
common / errors.jsconst trava = membutuhkan ('trava');
function ValidationError (ctx, params) {
if (params instance of Error) {
params = trava.ValidationError.extractData (params);
}
ctx.body = {
kode: 'VALIDATION_ERROR',
params,
};
ctx.status = HttpStatus.BAD_REQUEST;
}
Meskipun contohnya sangat sederhana, itu tidak bisa disebut disederhanakan. Dalam aplikasi nyata, hanya validator yang akan rumit. Anda juga dapat membuat validasi di middleware - validator diterapkan sepenuhnya pada konteks atau ke badan permintaan.
Dalam proses bekerja dan menggunakan validasi, kami sampai pada kesimpulan bahwa validator sinkron sederhana dan pesan kesalahan sederhana sudah cukup. Bahkan, kami sampai pada kesimpulan bahwa kami hanya menggunakan dua pesan: "DIBUTUHKAN" dan "INVALID", yang dilokalkan di frontend bersama dengan prompt untuk bidang. Pemeriksaan lain yang memerlukan tindakan tambahan (misalnya, pada saat pendaftaran untuk memeriksa apakah surat semacam itu sudah ada) berada di luar ruang lingkup validasi. Bagaimanapun, rumput bukan tentang hal ini.
Kesimpulannya
Dalam artikel singkat ini, saya menjelaskan hampir seluruh fungsi perpustakaan, di luar ruang lingkup artikel ada beberapa pembantu yang menyederhanakan kehidupan. Saya meminta detail di github
github.com/uNmAnNeR/travajs .
Kami membutuhkan alat yang dapat disesuaikan sebanyak mungkin, di mana tidak ada yang berlebihan, tetapi pada saat yang sama ada semua yang diperlukan untuk pekerjaan sehari-hari. Dan saya pikir secara umum ini tercapai, saya berharap seseorang juga akan membuat hidup lebih mudah. Saya akan senang dengan keinginan dan saran.
Untuk kesehatan.