이것은 모두 100% VBScript로 작성된 MVC 프레임워크입니다. Classic ASP용 Spring Boot와 비슷하다고 생각하세요.
Sane은 Classic ASP에 온전한 기능을 제공하는 비교적 모든 기능을 갖춘 MVC 프레임워크입니다. .NET MVC 및 Rails와 스타일이 일부 유사하지만 둘 중 하나와 정확히 유사하지는 않습니다. 컨트롤러가 특정 폴더 위치에 존재한다고 가정하지만 해당 위치는 어느 정도 구성 가능하다는 점에서 독선적입니다.
이 프레임워크를 구별하는 주요 기능은 다음과 같습니다.
All/Any
부울 테스트, Min/Max/Sum
, 투영을 위한 Map/Select
및 필터를 위한 Where
같은 열거 가능한 메서드(기본 람다 스타일 표현식 지원)Up
및 Down
단계별 마이그레이션을 통한 데이터베이스 마이그레이션 - 버전 제어 데이터베이스 변경Scripting.Dictionary
개체를 만들지 않고도 키-값 배열 데이터 구조를 사용하여 메서드 간에 키-값 쌍을 쉽게 전달할 수 있습니다.KVArray
및 해당 도우미 메서드를 사용하여 HTML을 쉽게 작성함)이 모든 것은 VBScript로 작성되었습니다. 정말.
Sane은 GPLv3의 조건에 따라 라이센스가 부여됩니다.
참고: 이 프레임워크는 실제 내부 워크플로 라우팅 프로젝트에서 추출되었으므로 약간의 거친 부분이 있습니다.
이는 하나의 컨트롤러에 대한 코드 흐름과 컨트롤러가 사용하는 모델 및 뷰에 대한 빠른 개요를 제공합니다. 위에 나열된 기능이 실제로 얼마나 함께 사용되는지 보여줍니다.
"같은 게 많지만 이건 내 거야."
주로 Classic ASP의 한계를 뛰어넘는 흥미로운 프로젝트였기 때문입니다. 대다수의 개발자는 VBScript와 Classic ASP를 싫어하는데, 그럴만한 이유가 있습니다. Classic ASP를 괴롭히는 많은 문제는 ASP가 개발된 시기인 1990년대 중반의 제약에서 비롯됩니다. 개발자들은 오늘날 기본 관행으로 간주되는 것(광범위한 클래스 사용 등)을 사용할 수 없었습니다. 언어는 우리가 "빠른" 방식으로 실행하도록 설계되지 않았고 이러한 관행을 사용하면 애플리케이션이 중단되고 충돌할 수 있기 때문입니다. 이 때문에 ASP 커뮤니티는 PHP가 사용된 것과 동일한 방식으로 ASP를 사용해야 했습니다. 즉, 자체적으로 완전한 응용 프로그램 프레임워크가 아닌 인라인 페이지 기반 템플릿 프로세서로 ASP를 사용해야 했습니다. 게다가 솔직하게 말하면 Microsoft는 기술 수준에 관계없이 모든 사람에게 ASP를 판매했으며 온라인에서 찾은 대부분의 자습서는 끔찍하고 끔찍한 나쁜 관행을 장려했습니다.
오늘날 우리는 더 잘 알고 있으며, 무어의 법칙 덕분에 컴퓨팅 성능이 90년대 중반보다 약 500배 증가하여 몇 년 전에는 상상할 수 없었던 일을 할 수 있게 되었습니다.
이 프레임워크는 이러한 방식으로 구축된 실제 프로젝트에서 추출되었습니다. 꽤 잘 작동했고, 실행 가능한 애플리케이션 프레임워크로 작동하지 않을 이유(기능적으로 말하자면)가 있어서는 안 됩니다. 즉, 현실적으로 오늘날 애플리케이션을 개발해야 한다면 .NET MVC나 경쟁사 중 하나와 같은 최신 프레임워크를 사용하게 되므로 다른 사람에게 도움이 될 경우를 대비해 여기에 있습니다. 게다가 만드는 것도 재미있었어요. :)
종속성: 데모는 Microsoft Northwind 샘플 데이터베이스를 기반으로 구축되었습니다. 여기에서 SQL Server BAK 파일을 다운로드하여 SQL Server 인스턴스로 복원하세요. SQL 스크립트 및 MDF 파일도 사용할 수 있습니다.
File -> Open Web Site...
를 선택하고 Demo
디렉터리를 선택합니다.AppDALlib.DAL.asp
파일을 열고 데이터베이스를 가리키도록 연결 문자열을 수정합니다./index.asp
에서 웹사이트를 시작합니다(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 파일을 로드하고 컴파일하기 때문입니다. 이를 감안할 때 프레임워크는 인스턴스화를 컨트롤러 자체에 위임하여 프레임워크가 모든 컨트롤러를 로드 및 인스턴스화하고 모든 요청에 대해 모든 뷰를 로드 및 컴파일하는 대신 프레임워크 시작을 담당하게 합니다. 프레임워크는 명명 규칙을 기반으로 컨트롤러를 동적으로 인스턴스화하지만 모든 컨트롤러 대신 요청당 하나의 컨트롤러만 구문 분석되고 로드됩니다. 하나의 컨트롤러만 로드함으로써 절약된 비용을 사용하여 개발을 훨씬 더 개발자 친화적으로 만드는 많은 유용한 라이브러리를 로드할 수 있습니다.
이 접근 방식으로 인해 "라우팅 엔진"은 실제로 컨트롤러 파일에 대한 URL을 작성하는 방법을 아는 클래스이므로 Routes.UrlTo("Orders", "Show", Array("Id", order.Id))
는 URL /App/Controllers/OrdersController.asp?_A=Show&Id=123
= 123의 경우). URL은 컨트롤러를 가리키며 _A
매개변수를 통해 실행될 작업의 이름을 제공합니다. 작업 매개변수는 프레임워크 전체에서 광범위하게 사용되는 키/값 쌍의 배열인 KVArray
데이터 구조를 통해 전달됩니다. 예를 들어, 다음은 많은 HTML 도우미 중 하나에 사용되는 두 개의 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 배열에 지나지 않습니다. 즉, KVArray
를 구축하려면 요소 0이 첫 번째 키이고 요소 1이 값, 요소 2가 두 번째 키, 요소 3이 값 등으로 배열을 구축하면 됩니다.
본질적으로 .NET의 Html.ActionLink
에서 수행되는 것처럼 System.Object
스타일 호출을 사용하는 방법으로 KVArray
상상할 수 있습니다.
예를 들어:
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
현재 키(예: "Name", "Age" 또는 "FavoriteColor")가 포함되고 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
절을 쉽게 구축하는 데 매우 효과적으로 사용됩니다. 아래 예는 조건자 키-값 쌍이 포함된 KVArray
허용하고 이를 쿼리 작성에 사용되는 두 개의 개별 배열로 압축을 푸는 ProductRepository.Find()
메서드에서 가져온 것입니다.
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
필드를 설정합니다. 이는 Observer 패턴과 유사합니다. 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."
도메인 모델은 Automapper 스타일 변환을 통해 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 쿼리와 바인드 값을 포함하는 선택적 두 번째 매개 변수를 허용합니다. 두 번째 매개변수는 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
개체에는 세 가지 메서드가 포함되어 있습니다. 모든 속성을 매핑하려고 시도하는 AutoMap
; 매핑할 속성의 하위 집합을 선택할 수 있는 FlexMap
(예: Automapper.FlexMap(rs, new OrderModel_Class, array("DateOrdered", "CustomerName"))
소스 레코드 세트의 지정된 두 필드만 새 모델 인스턴스로 복사합니다. ; 값을 동적으로 다시 매핑할 수 있는 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
메서드에서 "lambda" 식의 결과를 나타내는 데 사용되는 특수 인스턴스 변수입니다. item_
은 현재 처리 중인 항목을 나타내는 또 다른 특수 인스턴스 변수입니다. 따라서 이 경우 Map
목록의 각 항목을 반복하고 전달된 "lambda" 표현식을 실행합니다. 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
에 있는 웹 인터페이스를 통해 마이그레이션을 단계별로 진행하거나 중단할 수 있습니다. Migration.Do
SQL 명령을 실행합니다. 마이그레이션은 로드된 순서대로 처리됩니다. 쉽게 주문하려면 위에 표시된 대로 구조화된 명명 체계를 따르는 것이 좋습니다. 다운 마이그레이션 진행을 중지할 수 있는 Migration.Irreversible
과 같은 몇 가지 특수 명령이 있습니다.
프레임워크가 추출된 실제 프로젝트에는 약 36개의 마이그레이션이 포함되어 있어 개발 중에 DB 버전 관리에 매우 효과적이었습니다.
참고: 마이그레이션 웹 인터페이스는 매우 기본적이고 별로 좋지 않습니다.
종속성: meta_migrations
기능을 사용하려면 먼저 [ ! Create Migrations Table.sql
](Sane/Framework/Data/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"
모두 "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
*Ext
변형이 있는 MVC.RedirectTo(controller_name, action_name)
또는 MVC.RedirectToActionPOST(action_name)
프레임워크는 OWASP Top 10에서 다음 세 가지 항목을 완화하는 데 도움이 되는 도구를 제공합니다.
Database_Class
매개변수화된 쿼리를 지원합니다.H()
메서드가 제공됩니다.HtmlSecurity
도우미는 이 위협을 완화하기 위해 양식별 및 사이트별 nonce 검사를 제공합니다.나머지 7개 취약점은 대부분 또는 전적으로 개발자 및/또는 관리자의 책임입니다.
프레임워크 전반에 걸쳐 사용되는 한 가지 관용구는 메서드 오버로드를 허용하지 않는 VBScript에 대한 해결 방법입니다. 일반적으로 두 가지 경우가 있습니다. 하나는 메서드가 여러 매개 변수로 모든 기능을 갖춘 경우이고 다른 하나는 메서드 서명이 단순화된 경우입니다. 이는 모든 기능을 갖춘 메서드의 끝에 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
접미사 및 지연 로드 전역 범위 싱글톤 함수 사용과 같은 그의 코딩 규칙 중 일부를 채택합니다.