これは、すべて 100% VBScript で書かれた MVC フレームワークです。これは、クラシック ASP の Spring Boot のようなものだと考えてください。
Sane は、クラシック ASP に健全性をもたらす比較的フル機能の MVC フレームワークです。 .NET MVC と Rails の両方にスタイルの類似点がいくつかありますが、どちらにも完全に似ているわけではありません。コントローラーが特定のフォルダーの場所に存在することを前提としているという点で意見が分かれていますが、その場所はある程度構成可能です。
このフレームワークを区別する主な機能は次のとおりです。
All/Any
ブール テスト、 Min/Max/Sum
、投影のMap/Select
、フィルターのWhere
などの列挙可能なメソッド (基本的なラムダ スタイルの式がサポートされています)Up
およびDown
の段階的な移行によるデータベース移行 -- データベース変更のバージョン管理Scripting.Dictionary
オブジェクトを作成することなく、メソッド間でキーと値のペアを簡単に渡すことができます。KVArray
とそのヘルパー メソッドを使用して、HTML の構築を容易にします。これらはすべて VBScript で書かれています。本当に。
Sane は GPLv3 の条件に基づいてライセンスされています。
注:このフレームワークは実際の内部ワークフロー ルーティング プロジェクトから抽出されたものであるため、荒削りな部分がいくつかあります。
これにより、1 つのコントローラーのコード フローと、コントローラーで使用されるモデルとビューの概要がわかります。これは、上に挙げた機能のうちどれだけが実際に一緒に使用されているかを示します。
「同じようなものはたくさんありますが、これは私のものです。」
その主な理由は、これがクラシック ASP の限界を押し上げる興味深いプロジェクトだったからです。大多数の開発者は VBScript とクラシック ASP を嫌いますが、そのほとんどには正当な理由があります。 Classic ASP を悩ませている問題の多くは、それが開発された 1990 年代半ばの制約に起因しています。言語はいわゆる「高速」な方法で実行するように設計されておらず、これらのプラクティスを使用するとアプリケーションが行き詰まり、クラッシュする可能性があるため、開発者は今日基本的なプラクティスと考えられているもの (クラスなどを広く使用すること) を使用できませんでした。このため、ASP コミュニティは、ASP を PHP と同じように、それ自体が完全なアプリケーション フレームワークとしてではなく、インライン ページベースのテンプレート プロセッサとして使用することを余儀なくされました。さらに、正直に言うと、Microsoft はスキル レベルに関係なくすべての人を対象に ASP を販売しており、オンラインで見つかるチュートリアルのほとんどはひどいもので、ひどく悪い慣行を奨励していました。
今日、私たちはよりよく知っており、ムーアの法則のおかげで、コンピューティング能力は 90 年代半ばから約 500 倍に向上しており、数年前には考えられなかったようなことを実行する余裕ができています。
このフレームワークは、この方法で構築された実際のプロジェクトから抽出されたものです。これは非常にうまく機能し、実行可能なアプリケーション フレームワークとして機能しない理由は (機能的には) ありません。そうは言っても、現実的には、今日アプリケーションを開発する必要がある場合は、.NET MVC またはその競合他社の 1 つなどの最新のフレームワークを使用することになるため、これは他の人の役に立つ場合に備えてここに置いたものです。あと、作るのが楽しかったです。 :)
依存関係:デモは Microsoft Northwind サンプル データベースに対して構築されました。ここから SQL Server BAK ファイルをダウンロードし、SQL Server インスタンスに復元します。 SQL スクリプトや MDF ファイルも利用できます。
File -> Open Web Site...
選択し、 Demo
ディレクトリを選択します。AppDALlib.DAL.asp
を開き、データベースを指すように接続文字列を変更します。/index.asp
で Web サイトを開始します (F5 または CTRL-F5)。ファイルindex.asp
、自動的にホーム コントローラー/App/Controllers/HomeController.asp
にリダイレクトされ、デフォルトのアクションIndex
読み込みます。
以下のいくつかの機能には、対応する ASPUnit テストがあります。 Tests ディレクトリでそれらを見つけます。
<%
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
%>
コントローラーはドメインについてのみ認識し、データベースについては認識しません。喜び!
アクションはパラメーターのない関数です (Rails と同様、.NET MVC とは異なります)。パラメーターは従来の ASP と同様にRequest
オブジェクトから取得されます。 MVC.RequirePost
、 POST
リクエストにのみ応答するようにアクションを制限できます。それ以外の場合はエラーが発生します。
MVC.Dispatch
フレームワークへのマジック ソースのエントリ ポイントです。ビューはコントローラーに#include
され、アプリにはそれぞれ1..n
個のアクションを持つ1..n
のコントローラーを持つことができるため、中央のモノリシック MVC ディスパッチャーを持つことは、いくつかの単純なコントローラーを超えると実現できません。これは、ASP がページ ビューごとに#include
d ファイル全体をロードしてコンパイルするためです。そのため、フレームワークはインスタンス化をコントローラー自体に委任し、フレームワークにすべてのコントローラーのロードとインスタンス化、リクエストごとのすべてのビューのロードとコンパイルを担当させるのではなく、コントローラー自体にフレームワークのキックオフを担当させます。フレームワークは、命名規則に基づいてコントローラーを動的にインスタンス化しますが、すべてのコントローラーではなく、リクエストごとに 1 つのコントローラーのみが解析およびロードされます。 1 つのコントローラーをロードするだけで、その節約分を利用して、開発をより開発者にとって使いやすいものにする多くの便利なライブラリを代わりにロードできます。
このアプローチにより、「ルーティング エンジン」は実際にはコントローラー ファイルへの URL を構築する方法を認識する単なるクラスになるため、 Routes.UrlTo("Orders", "Show", Array("Id", order.Id))
はURL /App/Controllers/OrdersController.asp?_A=Show&Id=123
(order.Id = 123 の場合)。 URL はコントローラーを指し、 _A
パラメーターを介して実行されるアクションの名前を提供します。アクション パラメーターは、 KVArray
データ構造を介して渡されます。KVArray データ構造は、フレームワーク全体で広範囲に使用されるキーと値のペアの配列にすぎません。たとえば、ここでは、多くの HTML ヘルパーの 1 つで使用される 2 つのKVArray
を示します。
<%= HTML.LinkToExt( " View Orders " , _
" Orders " , _
" List " , _
array ( " param1 " , " value1 " , " param2 " , " value2 " ), _
array ( " class " , " btn btn-primary " , " id " , " orders-button " )) %>
このメソッドはバックグラウンドで、正しいコントローラー/アクションのコンボにルーティングするアンカーを構築し、クエリ文字列を介して指定されたパラメーターを渡し、指定された HTML class
とid
属性を持ちます。 KVArray
は、 KeyVal
やKVUnzip
などのいくつかのヘルパー メソッドのおかげで簡単に処理できます。
KVArray
データ構造はフレームワークの多くの基礎であり、コーディングを大幅に簡素化します。基本的に、 KVArray
は標準の VBScript 配列にすぎず、常に 2 つのグループで使用する必要があります。つまり、 KVArray
構築するには、要素 0 が最初のキー、要素 1 がその値、要素 2 が 2 番目のキー、要素 3 がその値などの配列を構築するだけです。
本質的に、 KVArray
、.NET のHtml.ActionLink
で行われているように、 System.Object
スタイルの呼び出しを使用する方法として想像できます。
例えば:
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"
しかし、実際にはそのように書くことはなく、代わりに次のようにインラインArray
コンストラクターを使用します。
dim params : params = Array( "Name" , "Bob" , "Age" , 35 , "FavoriteColor" , "Blue" )
または、さらに読みやすくするには:
dim params : params = Array( _
"Name" , "Bob" , _
"Age" , 35 , _
"FavoriteColor" , "Blue" _
)
この配列を 2 ステップずつ繰り返し、 KeyVal
使用して現在のキーと値を取得するには、次のようにします。
dim idx, the_key, the_val
For idx = 0 to UBound(kvarray) step 2
KeyVal kvarray, idx, the_key, the_val
Next
各反復で、 the_key
は現在のキー (「名前」、「年齢」、「お気に入りの色」など) が含まれ、 the_val
にはキーの対応する値が含まれます。
しかし、なぜ辞書を使わないのでしょうか?
ディクショナリは優れていますが、それらは COM コンポーネントであり、少なくとも歴史的にはインスタンス化にコストがかかり、またスレッドをセッションに配置すべきではありませんでした。また、このフレームワークのユースケースでこれらを扱うのは面倒で、動的な数のパラメータを使用してインラインでインスタンス化する簡単な方法はありません。
実際に必要なのは、値を反復処理して各キーと値を取り出して、任意の属性を持つ HTML タグや任意の列を持つ SQL where
句のようなものを構築できる高速な前方専用のキーと値のデータ構造です。個々のキーの高速検索。したがって、特定のニーズを満たし、任意の数のパラメーターのインライン宣言を可能にする、配列と辞書のハイブリッドが必要です。 KVArray
使用すると、上記のLinkToExt
例のようなコードを非常に自然に記述したり、 Routes.UrlTo()
を使用して URL を手動で構築したりできます。
<%
< a href = " <%= Routes.UrlTo( " Users " , " Edit " , array( " Id " , user.Id)) %> " >
< i class = " glyphicon glyphicon-user " >< /a >
< /a >
%>
次のように使用できる汎用リポジトリのFind
メソッドを作成することもできます。
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 _
) _
)
デモ リポジトリにはこの例があり、 KVUnzip
も非常に効果的に使用されており、SQL where
句を簡単に構築できます。以下の例は、 ProductRepository.Find()
メソッドからのものです。このメソッドは、述語のキーと値のペアを含むKVArray
を受け取り、それをクエリの構築に使用される 2 つの別々の配列に解凍します。
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
%>
モデルのClass_Initialize
コンストラクター内から適切なValidate*
ヘルパー メソッドを呼び出して、モデルを検証します。
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
現在、 ValidateExists
、 ValidateMinLength
、 ValidateMaxLength
、 ValidateNumeric
、およびValidatePattern
のみが含まれています。これらのヘルパー メソッドが実際に行うことは、対応する検証クラスの新しいインスタンスを作成し、それをモデルのValidator
プロパティにアタッチすることです。たとえば、モデルがValidateExists Me, "Name", "Name must exist."
実際に舞台裏で何が起こっているかは次のとおりです。
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
ここでMe
ドメイン モデル インスタンスです。次に、 Validator_Class
を ( YourModel.Validator
経由で) 使用して、登録されているすべての検証ルールを検証し、エラーが見つかった場合はErrors
フィールドとHasErrors
フィールドを設定します。これはオブザーバー パターンに似ています。 Me
渡す理由は、これにより、強い意味論的な意味を持つ各検証に対して、例えばValidateExists
など、便利な言葉で表現されたメソッドを使用できるためです。少しコード術が必要ですが、それだけの価値はあります。
新しい検証の追加は簡単です。新しい検証クラスとヘルパーSub
を追加するだけです。たとえば、文字列が「A」で始まることを必要とする検証を追加するには、 StartsWithLetterAValidation_Class
とヘルパー メソッドSub ValidateStartsWithA(instance, field_name, message)
を作成し、それをValidateStartsWithA Me, "MyField", "Field must start with A."
で呼び出します。 ValidateStartsWithA Me, "MyField", "Field must start with A."
ドメイン モデルは、オートマッパー スタイルの変換を介して ADO レコードセットをドメイン モデルのリンク リストに変換することで構築できます。えっ?
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
empty
キーワードの使用は、このフレームワークで採用される一般的なアプローチです。 VBScript に関する一般的な不満は、オプションのパラメーターが許可されていないことです。これは技術的には真実であり、回避するのは簡単ですが、オンラインで見つかるほぼすべての例には、空の文字列、NULL 値、または同様のアプローチを渡すことが含まれています。組み込みの VBScript キーワードempty
の使用は、オプションのパラメーターを処理する意味的に意味のある方法であり、オプションのパラメーターを無視することを明確に意図していたことは明らかです。この場合、 DAL.Query
メソッドは、SQL クエリとバインド値を含むオプションの 2 番目のパラメーターという 2 つのパラメーターを受け入れます。 2 番目のパラメーターはDAL.Query("select a from b where a = ?", "foo")
のような単一の値、またはDAL.Query("select a from b where a = ? and c = ?", Array("foo", "bar")
。上記の例では、SQL にバインド変数がないため、明示的に無視されます。
この例では、 DAL
変数は単にlib.Data.asp
のDatabase_Class
のインスタンスです。元のプロジェクトでは、DAL は、遅延ロードされるDatabase_Class
インスタンスのセットのエントリ ポイントとして機能するカスタム クラスで、ワークフロー中にデータベース間でデータを共有および移動できるようにします。
Automapper
オブジェクトは、ソース オブジェクトの各フィールドをターゲット オブジェクトの対応するフィールドにマップしようとする VBScript クラスです。ソース オブジェクトはレコードセットまたはカスタム クラスにすることができます。この関数は、新規または既存のオブジェクトにマップできます。 Automapper
オブジェクトには 3 つのメソッドが含まれています。AutoMap AutoMap
すべてのプロパティのマッピングを試みます。マッピングするプロパティのサブセットを選択できるFlexMap
。たとえば、 Automapper.FlexMap(rs, new OrderModel_Class, array("DateOrdered", "CustomerName"))
指定された 2 つのフィールドのみをソース レコードセットから新しいモデル インスタンスにコピーします。 ; DynMap
は値を動的に再マップできるようにします。不自然な例については、以下を参照してください。
Automapper.DynMap(rs, new OrderModel_Class, _
array( "target.CustomerName = UCase(src.CustomerName)" , _
"target.LikedOrder = src.CustomerWasHappy" ))
ソースとターゲットの両方がインスタンス メソッドを持つ任意のオブジェクトにできるため、これは CRUD メソッドでモデル バインディングを管理する非常に便利な方法です。次に例を示します。
Public Sub CreatePost
dim new_product_model : set new_product_model = Automapper.AutoMap(Request.Form, new ProductModel_Class)
... etc
End Sub
コントローラー アクションに#include
されているため、ビューはコントローラーのModel
インスタンスに完全にアクセスできます。ここでは、ビュー モデルのOrder
プロパティにアクセスし、 LineItems
プロパティ (リポジトリ内に構築されるLinkedList_Class
インスタンス) を反復してビューを構築します。ビュー モデルを使用すると、特定のレコードセット構造に関連付けられていないリッチなビューを作成できます。ダッシュボードの概要ビューを構築するためのドメイン オブジェクトの 4 つの個別のリストを含むビュー モデルの例については、デモのHomeController
参照してください。
MVC.RequireModel
メソッドは、.NET MVC の@model
ディレクティブを模倣して、ビューを厳密に型指定する機能を提供します。
<% 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 >
リスト上でチェーン可能なラムダスタイルの呼び出しを提供します。単体テストから:
Enumerable(list) _
.Where( "len(item_) > 5" ) _
.Map( "set V_ = new ChainedExample_Class : V_.Data = item_ : V_.Length = len(item_)" ) _
.Max( "item_.Length" )
V_
、「ラムダ」式の結果を表すためにMap
メソッドによって使用される特別なインスタンス変数です。 item_
処理されている現在の項目を表す別の特殊なインスタンス変数です。したがって、この場合、 Map
リスト内の各項目を反復処理し、渡された「ラムダ」式を実行します。 Map
の結果は、式によって構築されたChainedExample_Class
インスタンスのリストを含むEnumerableHelper_Class
の新しいインスタンスです。この列挙可能値はMax
によって処理され、単一の値 (最大長) が返されます。
接続の詳細とデータベースへのアクセスをラップします。すでに示した例に加えて、次のものも処理できます。
DAL.Execute "delete from Orders where OrderId = ?", id
set rs = DAL.PagedQuery(sql, params, per_page, page_num)
DAL.BeginTransaction
、 DAL.CommitTransaction
、およびDAL.RollbackTransaction
また、クラスは、クラスの破棄の準備ができたときに呼び出されるClass_Terminate
メソッドを介して、ラップされた接続を自動的に閉じて破棄します。
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"
移行は、 migrate.asp
にある Web インターフェイスを介して段階的に行うことができます。 Migration.Do
SQL コマンドを実行します。移行はロードされた順序で処理されます。注文を容易にするために、上記のような構造化された命名スキームに従うことをお勧めします。 Migration.Irreversible
など、ダウン移行の進行を停止できる特別なコマンドがいくつかあります。
フレームワークが抽出された実際のプロジェクトには約 34 個の移行が含まれていたため、開発中の DB のバージョン管理には非常にうまく機能しました。
注: 移行 Web インターフェイスは非常に基本的で、あまり美しくありません。
依存関係:移行機能を使用meta_migrations
には、まずスクリプト [ ! Create Migrations Table.sql
](Sane/Framework/Data/Migrations/! Migrations Table.sql の作成)。
Classic ASP ではステップスルー デバッグの使用が常に可能であるとは限らないため、これによりデバッグとトレースがはるかに簡単になります。
Dump
意味のある方法でオブジェクトを出力します。
dim a : a = GetSomeArray()
Dump a
出力:
[Array:
0 => «elt1»
1 => «elt2»
2 => «elt3»
]
Class_Get_Properties
フィールドを使用してカスタム クラスも処理します。
Dump Product
出力:
{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»
}
また、デモのOrdersController.asp
のShow
アクションにDump Model
への呼び出しが配置されたときに見られるように、ネストも処理されます。
{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
実行を直ちに停止します。 die "some message"
実行を停止し、"some message" を画面に出力します。 trace "text"
とcomment "text"
どちらも "テキスト" を含む HTML コメントを書き込みます。これは、レイアウトを中断することなく舞台裏をトレースするのに役立ちます。
Flash.Success = "Product updated."
、 Flash.Errors = model.Validator.Errors
など。
モデルの作成時にエラーが発生した場合は、ユーザーのコンテンツが入力された状態でフォームを再表示できる必要があります。これを簡略化するために、フレームワークはセッション経由でフォーム データをシリアル化/逆シリアル化するFormCache
オブジェクトを提供します。
たとえば、 Create
アクションでは次のようにすることができます。
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
そして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
Response.Write
をラップし、渡された型に基づいて出力を変更し、リストと配列の特別な出力を使用します。H(string)
文字列をHTMLエンコードしますAssign(target, src)
任意の型の変数を扱う場合にオブジェクトにset
を使用する必要性を抽象化します。Choice(condition, trueval, falseval)
はより機能的なiif
HTML.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)
または*Ext
バリアントを使用したMVC.RedirectToActionPOST(action_name)
このフレームワークは、OWASP トップ 10 の次の 3 つの項目を軽減するのに役立つツールを提供します。
Database_Class
パラメータ化されたクエリをサポートします。H()
メソッドが提供されます。HtmlSecurity
ヘルパーは、この脅威を軽減するためにフォームごとおよびサイトごとの nonce チェックを提供します。残りの 7 つの脆弱性の大部分または完全な責任は、開発者および/または管理者にあります。
フレームワーク全体で使用される 1 つのイディオムは、VBScript がメソッドのオーバーロードを許可しない場合の回避策です。一般に、メソッドがいくつかのパラメーターを備えたフル機能である場合と、メソッド シグネチャが簡略化されている場合の 2 つのケースがあります。これは、フル機能のメソッドの末尾にExt
追加して、簡略化されたメソッドの「拡張」バージョンとして示すことで処理されます。
たとえば、これは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
これは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
クラスとFunc
クラスを使用して VBScript でラムダを実装しましたが (この人からの批判に直接対処するつもりでした)、公開することはありませんでした。ブライアンは限界をはるかに超えました。 LinkedList_Class
とそのイテレータは、ASP の行き詰まりを避けるために調整された非常に強力なラムダ機能を備えた彼の作品から採用されています。このフレームワークは、 _Class
サフィックスや遅延ロードされたグローバル スコープのシングルトン関数の使用など、彼のコーディング規約の一部も採用しています。