Ini adalah kerangka MVC yang semuanya ditulis dalam VBScript 100%. Anggap saja seperti Spring Boot untuk ASP Klasik.
Sane adalah kerangka MVC berfitur lengkap yang menghadirkan kewarasan pada ASP Klasik. Gayanya memiliki beberapa kesamaan dengan .NET MVC dan Rails, tetapi tidak persis sama dengan keduanya. Hal ini diasumsikan bahwa pengontrol akan ada di lokasi folder tertentu, tetapi lokasi tersebut agak dapat dikonfigurasi.
Fitur utama yang membedakan kerangka ini meliputi:
All/Any
, Min/Max/Sum
, Map/Select
untuk proyeksi, dan Where
untuk filter, dengan ekspresi dasar gaya lambda yang didukungUp
dan Down
-- versi yang mengontrol perubahan basis dataScripting.Dictionary
setiap kaliKVArray
dan metode pembantunya untuk mempermudah pembuatan HTMLSemua ini ditulis dalam VBScript. Benar-benar.
Sane dilisensikan berdasarkan ketentuan GPLv3.
Catatan: Kerangka kerja ini diambil dari proyek perutean alur kerja internal dunia nyata, sehingga memiliki beberapa kekurangan.
Ini memberikan gambaran singkat tentang aliran kode untuk satu pengontrol dan model serta tampilan yang digunakannya. Ini menunjukkan seberapa banyak fungsi yang tercantum di atas sebenarnya digunakan bersama.
“Ada banyak yang menyukainya, tapi ini milikku.”
Terutama karena ini adalah proyek menarik yang melampaui batas ASP Klasik. Sebagian besar pengembang membenci VBScript dan ASP Klasik, sebagian besar dengan alasan yang bagus. Banyak masalah yang mengganggu ASP Klasik berasal dari keterbatasan waktu pengembangannya, pada pertengahan tahun 1990an. Pengembang tidak dapat menggunakan apa yang saat ini dianggap sebagai praktik mendasar (menggunakan kelas secara luas, dll) karena bahasa tersebut tidak dirancang untuk mengeksekusi dengan cara yang kita sebut "cepat" dan menggunakan praktik ini akan menyebabkan aplikasi macet dan mogok. Oleh karena itu, komunitas ASP terpaksa menggunakan ASP dengan cara yang sama seperti PHP -- sebagai pemroses templat berbasis halaman inline, bukan kerangka aplikasi lengkap. Ditambah lagi, jujur saja, Microsoft memasarkan ASP kepada semua orang tanpa memandang tingkat keahliannya, dan sebagian besar tutorial yang ditemukan online sangat buruk dan mendorong praktik yang sangat buruk.
Saat ini kita lebih tahu, dan berkat Hukum Moore, kekuatan komputasi telah meningkat sekitar 500 kali lipat sejak pertengahan tahun 90an, sehingga kita mampu melakukan hal-hal yang tidak terpikirkan beberapa tahun yang lalu.
Kerangka kerja ini diambil dari proyek nyata yang dibangun dengan cara ini. Ini bekerja cukup baik, dan seharusnya tidak ada alasan (secara fungsional) bahwa itu tidak berfungsi sebagai kerangka aplikasi yang layak. Meskipun demikian, secara realistis jika kita perlu mengembangkan aplikasi saat ini, kita akan menggunakan kerangka kerja modern seperti .NET MVC atau salah satu pesaingnya, jadi ini hanya untuk berjaga-jaga jika berguna bagi orang lain. Ditambah lagi, membangunnya menyenangkan. :)
Ketergantungan: Demo dibuat berdasarkan database sampel Microsoft Northwind. Unduh file SQL Server BAK di sini dan pulihkan ke dalam contoh SQL Server. Skrip SQL dan file MDF juga tersedia.
File -> Open Web Site...
dan pilih direktori Demo
AppDALlib.DAL.asp
dan ubah string koneksi agar mengarah ke database Anda./index.asp
File index.asp
akan secara otomatis mengarahkan Anda ke pengontrol Home, /App/Controllers/HomeController.asp
dan akan memuat tindakan default, Index
.
Beberapa fitur di bawah ini memiliki pengujian ASPUnit yang sesuai. Temukan di direktori Tes.
<%
Class OrdersController
Public Model
Public Sub Show
MVC.RequirePost
dim id : id = Request ( " Id " )
set Model = new Show_ViewModel_Class
set Model.Order = OrderRepository.FindById(id)
%> <!-- #include file="../../Views/Orders/Index.asp" --> <%
End Sub
End Class
MVC.Dispatch
%>
Pengontrol hanya mengetahui tentang domain, bukan database. Sukacita!
Tindakan adalah fungsi tanpa parameter (seperti Rails dan tidak seperti .NET MVC) -- param diambil dari objek Request
seperti pada ASP tradisional. MVC.RequirePost
memungkinkan Anda membatasi tindakan agar hanya merespons permintaan POST
-- jika tidak, akan terjadi error.
MVC.Dispatch
adalah titik masuk saus ajaib ke kerangka kerja. Karena tampilan #include
d ke dalam pengontrol, dan karena aplikasi dapat memiliki 1..n
pengontrol masing-masing dengan 1..n
tindakan, memiliki operator MVC monolitik pusat tidak dapat dilakukan selain beberapa pengontrol sederhana. Ini karena ASP memuat dan mengkompilasi seluruh file #include
d untuk setiap tampilan halaman. Oleh karena itu, kerangka kerja malah mendelegasikan pembuatan instance ke pengontrol itu sendiri, menjadikan mereka bertanggung jawab untuk memulai kerangka kerja alih-alih menjadikan kerangka kerja bertanggung jawab untuk memuat dan memberi contoh semua pengontrol serta memuat dan mengompilasi semua tampilan untuk setiap permintaan. Kerangka kerja ini membuat instance pengontrol secara dinamis berdasarkan konvensi penamaan, namun hanya satu pengontrol yang diurai dan dimuat per permintaan, bukan semua pengontrol. Dengan hanya memuat satu pengontrol, kita dapat menggunakan penghematan tersebut untuk memuat banyak perpustakaan bermanfaat yang membuat pengembangan lebih ramah pengembang.
Karena pendekatan ini, "mesin perutean" sebenarnya hanyalah sebuah kelas yang mengetahui cara membuat URL ke file pengontrol, sehingga Routes.UrlTo("Orders", "Show", Array("Id", order.Id))
menghasilkan URL /App/Controllers/OrdersController.asp?_A=Show&Id=123
(untuk order.Id = 123). URL menunjuk ke pengontrol dan memberikan nama tindakan yang akan dieksekusi melalui parameter _A
. Parameter tindakan diteruskan melalui struktur data KVArray
, yang merupakan larik pasangan kunci/nilai yang digunakan secara luas di seluruh kerangka kerja. Misalnya, berikut adalah dua KVArray
yang digunakan di salah satu dari banyak pembantu HTML:
<%= HTML.LinkToExt( " View Orders " , _
" Orders " , _
" List " , _
array ( " param1 " , " value1 " , " param2 " , " value2 " ), _
array ( " class " , " btn btn-primary " , " id " , " orders-button " )) %>
Di balik layar metode ini membangun sebuah jangkar yang merutekan ke kombo pengontrol/tindakan yang benar, meneruskan parameter yang ditentukan melalui string kueri, dan memiliki atribut class
HTML dan id
yang ditentukan. KVArray
mudah ditangani berkat beberapa metode pembantu seperti KeyVal
dan KVUnzip
.
Struktur data KVArray
sangat penting bagi sebagian besar kerangka kerja dan sangat menyederhanakan pengkodean. Pada dasarnya KVArray
tidak lebih dari array VBScript standar yang harus selalu digunakan dalam kelompok dua orang. Dengan kata lain, untuk membangun KVArray
kita hanya perlu membuat array dengan elemen 0 sebagai kunci pertama dan elemen 1 nilainya, elemen 2 sebagai kunci kedua dan elemen 3 nilainya, dan seterusnya.
Intinya Anda dapat membayangkan KVArray
sebagai cara untuk menggunakan panggilan gaya System.Object
seperti yang dilakukan di Html.ActionLink
.NET.
Misalnya:
dim kvarray : kvarray = Array( 6 )
'Element 1: Name = Bob
kvarray( 0 ) = "Name"
kvarray( 1 ) = "Bob"
'Element 2: Age = 35
kvarray( 2 ) = "Age"
kvarray( 3 ) = 35
'Element 3: FavoriteColor = Blue
kvarray( 4 ) = "FavoriteColor"
kvarray( 5 ) = "Blue"
Namun kenyataannya Anda tidak akan pernah menulis seperti itu, melainkan Anda akan menggunakan konstruktor Array
inline seperti ini:
dim params : params = Array( "Name" , "Bob" , "Age" , 35 , "FavoriteColor" , "Blue" )
Atau agar lebih mudah dibaca:
dim params : params = Array( _
"Name" , "Bob" , _
"Age" , 35 , _
"FavoriteColor" , "Blue" _
)
Untuk mengulangi array ini langkah demi 2 dan menggunakan KeyVal
untuk mendapatkan kunci dan nilai saat ini:
dim idx, the_key, the_val
For idx = 0 to UBound(kvarray) step 2
KeyVal kvarray, idx, the_key, the_val
Next
Pada setiap iterasi, the_key
akan berisi kunci saat ini (misalnya "Nama", "Umur", atau "Warna Favorit") dan the_val
akan berisi nilai kunci yang sesuai.
Tapi kenapa tidak menggunakan Kamus?
Kamus memang bagus, tetapi merupakan komponen COM dan setidaknya secara historis mahal untuk dibuat instance-nya dan karena threading tidak boleh ditempatkan di sesi. Mereka juga rumit untuk digunakan dalam kasus penggunaan dalam kerangka kerja ini dan tidak ada cara mudah untuk membuat instance mereka sejalan dengan sejumlah parameter dinamis.
Yang sebenarnya kita perlukan adalah struktur data nilai kunci yang cepat dan hanya maju saja yang memungkinkan kita mengulangi nilai dan mengambil setiap kunci dan nilai untuk membuat sesuatu seperti tag HTML dengan atribut arbitrer atau klausa SQL where
dengan kolom arbitrer, bukan pencarian cepat kunci individual. Jadi kita memerlukan gabungan array dan kamus yang memenuhi kebutuhan spesifik kita dan memungkinkan deklarasi inline dari sejumlah parameter yang berubah-ubah. KVArray
memungkinkan kita menulis kode secara alami seperti contoh LinkToExt
di atas, atau membuat URL secara manual menggunakan Routes.UrlTo()
:
<%
< a href = " <%= Routes.UrlTo( " Users " , " Edit " , array( " Id " , user.Id)) %> " >
< i class = " glyphicon glyphicon-user " >< /a >
< /a >
%>
Kita juga dapat membuat metode Find
repositori generik yang dapat digunakan seperti ini:
set expensive_products_starting_with_C = ProductRepository.Find( _
array( "name like ?" , "C%" , _
"price > ?" , expensive_price _
) _
)
set cheap_products_ending_with_Z = ProductRepository.Find( _
array( "name like ?" , "%Z" , _
"price < ?" , cheap_price _
) _
)
Ada contohnya di repositori demo, di mana KVUnzip
juga digunakan dengan sangat efektif untuk membantu dengan mudah membangun klausa sql where
. Contoh di bawah ini berasal dari metode ProductRepository.Find()
yang menerima KVArray
yang berisi pasangan nilai kunci predikat dan mengekstraknya menjadi dua larik terpisah yang digunakan untuk membuat kueri:
If Not IsEmpty(where_kvarray) then
sql = sql & " WHERE "
dim where_keys, where_values
KVUnzip where_kvarray, where_keys, where_values
dim i
For i = 0 to UBound(where_keys)
If i > 0 then sql = sql & " AND "
sql = sql & " " & where_keys(i) & " "
Next
End If
...
dim rs : set rs = DAL.Query(sql, where_values)
set Find = ProductList(rs)
<%
Class OrderModel_Class
Public Validator
Public OrderNumber, DateOrdered, CustomerName, LineItems
Public Property Get SaleTotal
SaleTotal = Enumerable(LineItems).Sum( " item_.Subtotal " ) ' whaaaa?
End Property
Public Sub Class_Initialize
ValidatePattern Me, OrderNumber, " ^d{9}[d|X]$ " , " Order number format is incorrect. "
ValidateExists Me, DateOrdered, " DateOrdered cannot be blank. "
ValidateExists Me, CustomerName, " Customer name cannot be blank. "
End Sub
End Class
Class OrderLineItemModel_Class
Public ProductName, Price, Quantity, Subtotal
End Class
%>
Validasi model dengan memanggil metode pembantu Validate*
yang sesuai dari dalam konstruktor Class_Initialize
model:
Private Sub Class_Initialize
ValidateExists Me , "Name" , "Name must exist."
ValidateMaxLength Me , "Name" , 10 , "Name cannot be more than 10 characters long."
ValidateMinLength Me , "Name" , 2 , "Name cannot be less than 2 characters long."
ValidateNumeric Me , "Quantity" , "Quantity must be numeric."
ValidatePattern Me , "Email" , "[w-]+@([w-]+.)+[w-]+" , "E-mail format is invalid."
End Sub
Saat ini hanya ValidateExists
, ValidateMinLength
, ValidateMaxLength
, ValidateNumeric
, dan ValidatePattern
yang disertakan. Apa yang sebenarnya dilakukan oleh metode pembantu ini adalah membuat instance baru dari kelas validasi yang sesuai dan melampirkannya ke properti Validator
model. Misalnya, saat model mendeklarasikan validasi menggunakan ValidateExists Me, "Name", "Name must exist."
berikut adalah apa yang sebenarnya terjadi di balik layar:
Sub ValidateExists(instance, field_name, message)
if not IsObject(instance.Validator) then set instance.Validator = new Validator_Class
instance.Validator.AddValidation new ExistsValidation_Class.Initialize(instance, field_name, message)
End Sub
Here Me
adalah contoh model domain. Validator_Class
kemudian digunakan (melalui YourModel.Validator
) untuk memvalidasi semua aturan validasi terdaftar, mengatur bidang Errors
dan HasErrors
jika ditemukan kesalahan. Ini mirip dengan pola Pengamat. Alasan kami melewati Me
adalah karena ini memungkinkan kami memiliki metode yang mudah digunakan untuk setiap validasi yang memiliki makna semantik yang kuat, misalnya ValidateExists
. Dibutuhkan sedikit kode-jutsu tetapi itu sepadan.
Menambahkan validasi baru itu mudah, cukup tambahkan kelas validasi baru dan Sub
pembantu. Misalnya, untuk menambahkan validasi yang mengharuskan string dimulai dengan huruf "A" Anda akan membuat StartsWithLetterAValidation_Class
dan metode pembantu Sub ValidateStartsWithA(instance, field_name, message)
, lalu memanggilnya melalui ValidateStartsWithA Me, "MyField", "Field must start with A."
Model domain dapat dibangun dengan mengonversi ADO Recordset menjadi daftar model domain tertaut melalui transformasi gaya Automapper. Katakan apa?
Class OrderRepository_Class
Public Function GetAll()
dim sql : sql = "select OrderNumber, DateOrdered, CustomerName from Orders"
dim rs : set rs = DAL.Query(sql, empty) 'optional second parameter, can be scalar or array of binds
dim list : set list = new LinkedList_Class
Do until rs.EOF
list.Push Automapper.AutoMap(rs, new OrderModel_Class) ' keanuwhoa.jpg
rs.MoveNext
Loop
set GetAll = list
Destroy rs ' no passing around recordsets, no open connections to deal with
End Function
End Class
' Convenience wrapper lazy-loads the repository
dim OrderRepository__Singleton
Function OrderRepository()
If IsEmpty(OrderRepository__Singleton) then
set OrderRepository__Singleton = new OrderRepository_Class
End If
set OrderRepository = OrderRepository__Singleton
End Function
Penggunaan kata kunci empty
adalah pendekatan umum yang diambil oleh framework ini. Keluhan umum VBScript adalah tidak mengizinkan parameter opsional. Meskipun secara teknis hal ini benar, hal ini mudah untuk diatasi, namun hampir setiap contoh yang ditemukan online melibatkan penerusan string kosong, atau nilai null, atau pendekatan serupa. Menggunakan kata kunci VBScript bawaan empty
adalah cara yang bermakna secara semantik untuk menangani parameter opsional, memperjelas bahwa kami secara khusus bermaksud mengabaikan parameter opsional. Dalam hal ini metode DAL.Query
menerima dua parameter, kueri SQL dan parameter opsional kedua yang berisi nilai pengikatan. Parameter kedua dapat berupa nilai tunggal seperti pada DAL.Query("select a from b where a = ?", "foo")
atau array pengikat misalnya DAL.Query("select a from b where a = ? and c = ?", Array("foo", "bar")
. Dalam contoh di atas secara eksplisit diabaikan karena tidak ada variabel pengikat di SQL.
Dalam contoh ini variabel DAL
hanyalah sebuah turunan dari Database_Class
dari lib.Data.asp
. Dalam proyek aslinya, DAL adalah kelas khusus yang bertindak sebagai titik masuk untuk sekumpulan instance Database_Class
yang dimuat dengan lambat, memungkinkan data untuk dibagikan dan dipindahkan antar database selama alur kerja.
Objek Automapper
adalah kelas VBScript yang mencoba memetakan setiap bidang di objek sumber ke bidang terkait di objek target. Objek sumber dapat berupa kumpulan data atau kelas khusus. Fungsi ini dapat dipetakan ke objek baru atau yang sudah ada. Objek Automapper
berisi tiga metode: AutoMap
yang mencoba memetakan semua properti; FlexMap
yang memungkinkan Anda memilih subset properti untuk dipetakan, misalnya Automapper.FlexMap(rs, new OrderModel_Class, array("DateOrdered", "CustomerName"))
hanya akan menyalin dua kolom tertentu dari kumpulan data sumber ke instance model baru ; dan DynMap
yang memungkinkan Anda memetakan ulang nilai secara dinamis, untuk contoh yang dibuat-buat, lihat:
Automapper.DynMap(rs, new OrderModel_Class, _
array( "target.CustomerName = UCase(src.CustomerName)" , _
"target.LikedOrder = src.CustomerWasHappy" ))
Karena sumber dan target dapat berupa objek apa pun dengan metode instan, ini adalah cara yang sangat berguna untuk mengelola pengikatan model dalam metode CRUD, misalnya:
Public Sub CreatePost
dim new_product_model : set new_product_model = Automapper.AutoMap(Request.Form, new ProductModel_Class)
... etc
End Sub
Karena #include
d ke dalam aksi pengontrol, tampilan memiliki akses penuh ke instance Model
pengontrol. Di sini ia mengakses properti Order
dari model tampilan dan mengulangi properti LineItems
(yang akan menjadi instance LinkedList_Class
yang dibangun di dalam repositori) untuk membangun tampilan. Dengan menggunakan model tampilan, Anda dapat membuat tampilan kaya yang tidak terikat pada struktur kumpulan data tertentu. Lihat HomeController
di demo untuk contoh model tampilan yang berisi empat daftar objek domain terpisah untuk membuat tampilan ringkasan dasbor.
Metode MVC.RequireModel
menyediakan kemampuan untuk mengetikkan tampilan dengan kuat, meniru arahan @model
di .NET MVC.
<% MVC.RequireModel Model, " Show_ViewModel_Class " %>
< h2 >Order Summary</ h2 >
< div class = " row " >
< div class = " col-md-2 " >
Order # <%= Model.Order.OrderNumber %>
</ div >
< div class = " col-md-10 " >
Ordered on <%= Model.Order.DateOrdered %>
by <%= Model.Order.CustomerName %>
for <%= FormatCurrency (Model.Order.SaleTotal) %>
</ div >
</ div >
< table class = " table " >
< thead >
< tr >
< th >Product</ th >
< th >Price</ th >
< th >Qty</ th >
< th >Subtotal</ th >
</ tr >
<% dim it : set it = Model.Order.LineItems.Iterator %>
<% dim item %>
<% While it.HasNext %>
<% set item = it.GetNext() %>
< tr >
< td > <%= item .ProductName %> </ td >
< td > <%= item .Price %> </ td >
< td > <%= item .Quantity %> </ td >
< td > <%= item .Subtotal %> </ td >
</ tr >
<% Wend %>
</ thead >
</ table >
Menyediakan panggilan gaya lambda yang dapat dirantai dalam daftar. Dari tes unit:
Enumerable(list) _
.Where( "len(item_) > 5" ) _
.Map( "set V_ = new ChainedExample_Class : V_.Data = item_ : V_.Length = len(item_)" ) _
.Max( "item_.Length" )
V_
adalah variabel instan khusus yang digunakan oleh metode Map
untuk mewakili hasil ekspresi "lambda". item_
adalah variabel instan khusus lainnya yang mewakili item yang sedang diproses. Jadi dalam kasus ini, Map
mengulangi setiap item dalam daftar dan mengeksekusi ekspresi "lambda" yang diteruskan. Hasil dari Map
adalah instance baru dari EnumerableHelper_Class
yang berisi daftar instance ChainedExample_Class
yang dibuat oleh ekspresi tersebut. Enumerable ini kemudian diproses oleh Max
untuk mengembalikan satu nilai, panjang maksimum.
Membungkus detail koneksi dan akses ke database. Selain contoh yang telah ditunjukkan, ia juga dapat menangani:
DAL.Execute "delete from Orders where OrderId = ?", id
set rs = DAL.PagedQuery(sql, params, per_page, page_num)
DAL.BeginTransaction
, DAL.CommitTransaction
, dan DAL.RollbackTransaction
Kelas juga secara otomatis menutup dan menghancurkan koneksi yang dibungkus melalui metode Class_Terminate
yang dipanggil ketika kelas siap untuk dimusnahkan.
Class Migration_01_Create_Orders_Table
Public Migration
Public Sub Up
Migration.Do "create table Orders " & _
"(OrderNumber varchar(10) not null, DateOrdered datetime, CustomerName varchar(50))"
End Sub
Public Sub Down
Migration.Do "drop table Orders"
End Sub
End Class
Migrations.Add "Migration_01_Create_Orders_Table"
Migrasi dapat ditingkatkan dan diturunkan melalui antarmuka web yang terletak di migrate.asp
. Migration.Do
menjalankan perintah SQL. Migrasi diproses sesuai urutan dimuat. Rekomendasikan untuk mengikuti skema penamaan terstruktur seperti yang ditunjukkan di atas untuk memudahkan pemesanan. Ada beberapa perintah khusus, seperti Migration.Irreversible
yang memungkinkan Anda menghentikan proses migrasi turun, dll.
Proyek dunia nyata tempat kerangka kerja diekstraksi berisi sekitar 3 lusin migrasi, sehingga bekerja dengan sangat baik untuk membuat versi DB selama pengembangan.
Catatan: antarmuka web migrasi sangat mendasar dan tidak cantik
Ketergantungan: Untuk menggunakan fitur migrasi Anda harus membuat tabel meta_migrations
terlebih dahulu menggunakan skrip [ ! Create Migrations Table.sql
](Sane/Framework/Data/Migrations/! Buat Tabel Migrasi.sql).
Karena penggunaan proses debug bertahap tidak selalu memungkinkan di ASP Klasik, hal ini membuat proses debug dan penelusuran menjadi lebih mudah.
Dump
mengeluarkan objek dengan cara yang bermakna:
dim a : a = GetSomeArray()
Dump a
Keluaran:
[Array:
0 => «elt1»
1 => «elt2»
2 => «elt3»
]
Ia bahkan menangani kelas khusus, menggunakan bidang Class_Get_Properties
:
Dump Product
Keluaran:
{ProductModel_Class:
Id : Long => «17»,
Name : String => «Alice Mutton»,
CategoryId : Long => «6»,
Category : Empty => «»,
CategoryName : String => «Meat/Poultry»,
SupplierId : Long => «7»,
Supplier : Empty => «»,
SupplierName : String => «Pavlova, Ltd.»,
UnitPrice : Currency => «250»,
UnitsInStock : Integer => «23»,
UnitsOnOrder : Integer => «0»,
ReorderLevel : Integer => «0»,
Discontinued : Boolean => «True»
}
Dan ia menangani penyatuan, seperti yang terlihat di sini ketika panggilan ke Dump Model
ditempatkan di aksi Show
OrdersController.asp
di Demo:
{OrderModel_Class:
Id : Long => « 11074 » ,
CustomerId : String => « SIMOB » ,
OrderDate : Date => « 5 / 6 / 1998 » ,
RequiredDate : Date => « 6 / 3 / 1998 » ,
ShippedDate : Null => «» ,
ShipName : String => « Simons bistro » ,
ShipAddress : String => « Vinbæltet 34 » ,
ShipCity : String => « Kobenhavn » ,
ShipCountry : String => « Denmark » ,
LineItems : LinkedList_Class =>
[ List:
1 =>
{OrderLineItemModel_Class:
ProductId : Long => « 16 » ,
ProductName : String => « Pavlova » ,
UnitPrice : Currency => « 17.45 » ,
Quantity : Integer => « 14 » ,
Discount : Single => « 0.05 » ,
ExtendedPrice : Currency => « 232.09 »
}
] }
quit
segera menghentikan eksekusi. die "some message"
menghentikan eksekusi dan menampilkan "some message" ke layar. trace "text"
dan comment "text"
keduanya menulis komentar HTML yang berisi "teks", berguna untuk menelusuri di balik layar tanpa mengganggu tata letak.
Flash.Success = "Product updated."
, Flash.Errors = model.Validator.Errors
, dll.
Jika terjadi kesalahan saat membuat model, kita harus dapat menampilkan kembali formulir dengan konten pengguna masih terisi. Untuk menyederhanakan hal ini, kerangka kerja menyediakan objek FormCache
yang membuat serialisasi/deserialisasi data formulir melalui sesi.
Misalnya, dalam tindakan Create
kita dapat memiliki:
Public Sub Create
dim form_params : set form_params = FormCache.DeserializeForm( "NewProduct" )
If Not form_params Is Nothing then
set Model = Automapper.AutoMap(form_params, new Create_ViewModel_Class)
Else
set Model = new Create_ViewModel_Class
End If
% > <!--#include file= "../../Views/Products/Create.asp" --> < %
End Sub
Dan di CreatePost
:
Public Sub CreatePost
dim new_product_model : set new_product_model = Automapper.AutoMap(Request.Form, new ProductModel_Class)
new_product_model.Validator.Validate
If new_product_model.Validator.HasErrors then
FormCache.SerializeForm "NewProduct" , Request.Form
Flash.Errors = new_product_model.Validator.Errors
MVC.RedirectToAction "Create"
Else
ProductRepository.AddNew new_product_model
FormCache.ClearForm "NewProduct"
Flash.Success = "Product added."
MVC.RedirectToAction "Index"
End If
End Sub
put
membungkus Response.Write
dan memvariasikan outputnya berdasarkan tipe yang diteruskan, dengan output khusus untuk daftar dan array.H(string)
HTMLMengkodekan stringAssign(target, src)
menghilangkan kebutuhan untuk menggunakan set
untuk objek dalam kasus di mana kita berurusan dengan variabel bertipe arbitrerChoice(condition, trueval, falseval)
adalah iif
yang lebih berfungsiHTML.FormTag(controller_name, action_name, route_attribs, form_attribs)
HTML.TextBox(id, value)
HTML.TextArea(id, value, rows, cols)
HTML.DropDownList(id, selected_value, list, option_value_field, option_text_field)
*Ext
Edit
: HTMLSecurity.SetAntiCSRFToken "ProductEditForm"
<%= HTML.Hidden("nonce", HTMLSecurity.GetAntiCSRFToken("ProductEditForm")) %>
EditPost
: HTMLSecurity.OnInvalidAntiCsrfTokenRedirectToActionExt "ProductEditForm", Request.Form("nonce"), "Edit", Array("Id", Request.Form("Id"))
MVC.ControllerName
, MVC.ActionName
MVC.RedirectTo(controller_name, action_name)
atau MVC.RedirectToActionPOST(action_name)
dengan varian *Ext
Kerangka kerja ini menyediakan alat untuk membantu memitigasi tiga item berikut dari 10 Besar OWASP:
Database_Class
mendukung kueri berparameter.H()
disediakan untuk pengkodean sederhana dari semua keluaran lainnya.HtmlSecurity
menyediakan pemeriksaan kinerja dan nonce per situs untuk mengurangi ancaman ini.Tujuh kerentanan lainnya sebagian besar atau seluruhnya merupakan tanggung jawab pengembang dan/atau administrator.
Salah satu ungkapan yang digunakan di seluruh kerangka kerja adalah solusi untuk VBScript yang tidak mengizinkan metode kelebihan beban. Secara umum ada dua kasus, satu ketika metode memiliki fitur lengkap dengan beberapa parameter dan satu lagi ketika tanda tangan metode disederhanakan. Hal ini ditangani dengan menambahkan metode berfitur lengkap Ext
di akhir untuk menunjukkannya sebagai versi "diperluas" dari metode yang disederhanakan.
Misalnya, ini dari HTML_Helper_Class
:
Public Function LinkTo(link_text, controller_name, action_name)
LinkTo = LinkToExt(link_text, controller_name, action_name, empty, empty)
End Function
Public Function LinkToExt(link_text, controller_name, action_name, params_array, attribs_array)
LinkToExt = "<a href='" & Encode(Routes.UrlTo(controller_name, action_name, params_array)) & "'" & _
HtmlAttribs(attribs_array) & ">" & link_text & "</a>" & vbCR
End Function
Dan ini dari MVC_Dispatcher_Class
:
Public Sub RedirectTo(controller_name, action_name)
RedirectToExt controller_name, action_name, empty
End Sub
' Redirects the browser to the specified action on the specified controller with the specified querystring parameters.
' params is a KVArray of querystring parameters.
Public Sub RedirectToExt(controller_name, action_name, params)
Response.Redirect Routes.UrlTo(controller_name, action_name, params)
End Sub
Proc
dan Func
yang dibangun secara dinamis (bermaksud untuk langsung mengatasi kritik dari orang ini) tetapi tidak pernah mempublikasikannya. Brian mendorong batasan lebih jauh dari itu. LinkedList_Class
dan iteratornya diadaptasi dari karyanya, dengan kemampuan lambda yang sangat kuat yang dirancang untuk menghindari terlalu banyak menghambat ASP. Kerangka kerja ini juga mengadopsi beberapa konvensi pengkodeannya, seperti sufiks _Class
dan penggunaan fungsi singleton lingkup global yang lambat dimuat.