MVC4 多了什麼新東西 – Single Page Application (SPA)

ASP.NET MVC4 Beta 除了 Web API 也同樣新增了另一個專案範本,Single Page Application (SPA),為什麼會有只要一頁的專案,那應該只是拿來測試的吧,有必要特地新增一個專案範本嗎。
image
原來這邊的 Single Page 指的是用單一頁面完成整個網站的瀏覽,利用 javascript 直接建立起 Client 的系統,再透過 javascript 來處理 Request & Response ,好處是可以讓網頁動作更流暢.也可以結合 Web API 的概念,用同樣的 controller 同時對不同 Client 支援(當然 client 還是要分好幾套來寫),這樣想起來其實 SPA 根本就應該跟 Web API 放一起就好了啊。
spa.clientstack
建立的專案其實重點就是在 javascript 上面,這邊 .net 跟 Visual Studio 能提供的支援就少了很多了,但是我覺得將來漸漸這種類型的網站會愈來愈多,更重視在使用者互動上。
image


Steve Sanderson’s Knockout Blog

MVC4 多了什麼新東西 – Web API

image
Microsoft 在前幾天發表了 ASP.NET MVC4 Beta,長久以來 .net 在 Layout 方面都顯得較為弱勢,畢竟系統邏輯才是 Microsoft 的強項,從 ASP.NET WebPage > ASP.NET MVC > ASP.NET Web API 都一直在做著輕量化的工程,讓整個 Framework 不要去包裝太多。
Web API 這個 Framework 結合了 ASP.NET MVC 還有 WCF 的概念而成,讓 Web API 的 Service 變得更 RESTful 。也是 Microsoft 放手讓 .net developer 可以更加便利選擇 Client 端的技術,Web 的 jQuery , Extjs , Flash, iOS 的 Cocoa , Android Java , WindowsPhone Sliverlight …..

建立 Web API 專案

在安裝完 ASP.NET MVC4 Beta 之後,建立新的 ASP.NET MVC 4 Web Application
image
在選單中可以發現多了一個新的 Template – Web API
image
建立之後的檔案結構目錄依然是保持 ASP.NET MVC 的樣子,但是可以發現多了一個 ValueController ,這個就是 Web API 的重點了。
image
ValueController 裡面的程式碼,改由繼承 ApiController ,Method 名稱就直接對應 HttpMethod

public class ValuesController : ApiController{
// GET /api/values
public IEnumerable Get()
{
return new string[] { "value1", "value2" };
}

// GET /api/values/5
public string Get(int id)
{
return "value";
}

// POST /api/values
public void Post(string value)
{
}

// PUT /api/values/5
public void Put(int id, string value)
{
}

// DELETE /api/values/5
public void Delete(int id)
{
}
}

另外在 Global.asax Route 也可以看到多註冊了一組

routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

利用 Chrome REST Console 測試

image

image

回傳的結果

image

也支援 Xml

image


Tutorial: Your First Web API

Video: Your First Web API

Building HTTP services with ASP.NET Web API in MVC 4 Beta

Chrome 擴充功能 – REST Console

ModelStateValue 常遇到的同名衝突

ASP.NET MVC Model Binder 在 mvc 中處理掉所有 Client Side 跟 Server Side 的對應,處理掉很多繁瑣的小細節,寫起來變得非常的直覺。但是就是因為太直覺了,讓人往往忘記它的存在,Model Binder 發生的問題也是非常容易鬼打牆的地方。遇到這種問題我之前也是非常相信直覺的判斷,但是事實證明不能太鐵齒,遇到問題還是趕快把 .pdb 加進來一起偵錯吧
我個人很喜歡在 View 裡面用強型別 Model 配合 HTml.Helper 來做輸出的動作,不過有時候會遇到 Helper 輸出結果跟預設的想像不同的狀況。預設的想法就是 Helper 應該會輸出 Model 當下狀態的結果,不過反覆檢查下才知道 Helper 動作並不是如此。

ModelStateValue 的同名衝突

Html.Helper 在強型別配合下並不是直接輸出 Model 裡面的值,他一樣會同時參考其他資料來源,所以當使用 HtmlHelper 的時候,會容易因為呼叫的方式而預想成是直接根據強型別 Model 來作輸出。

Binding 進 ModelStateValue 裡面的資料

ModelState 在外部存取的時候是 readOnly 所以沒有辦法直接加入值,最常遇到的狀況就是在 get 或 post 的 Data 資料被 binding 進去 ModelStateValue 裡面,為了重現這個狀況
首先我建立一個當作強型別的 Model

public class HomeIndexModel{
public string Phone { get; set; }
}
相關的 Controller
public ActionResult Index(HomeIndexModel model)
{
model.Phone = "0";
return View(model);
}
View
@model ModelStateValue.Models.HomeIndexModel<section class="features">
@using (Html.BeginForm())
{
@Html.TextBoxFor(it => it.Phone)
<input type="submit" value="submit" />
}
</section>

首先在一開始進入頁面的時候,helper 顯示出來是 controller 設定的 0。

image

之後輸入其他值並且 Submit 回 Server Side。

image

Model 的值也確實被改變了。

image

但是之後回傳頁面卻還是 Post 回傳的值。

image

這個問題當初確實困擾我一陣子,當然有很多方法可以避免,但是跟我原本對 MVC 的想法有一些衝突,其實也就是對整個 Framework 不夠熟悉才會這樣。雖然已經被設置為強型別的 View ,但是 Html Helper 的資料來源並不是強型別 Model 而是 ModelState

image

而因為 Post 回 Server 的時候 PostData 已經綁定進入 ModelState 所以在輸出的時候抓到的並不是強型別 Model 的值,而是一開始被 binding 的部分。


http://stackoverflow.com/questions/9529730/view-does-not-affect-models-changes

ASP.NET MVC 開發心得分享 (6):小心使用 FormCollection

ASP.NET MVC 開發心得分享 (4):微調 Model Binder 屬性

ASP.NET MVC 開發心得分享 (12):Model Binder 的陷阱

設定 Visual Studio 偵錯 Microsoft .NET 元件

之前想要對 ASP.NET MVC Framework 元件做偵錯的時候我都是下載原始碼專案,然後把參考的部分改掉,還要額外設定 Config 的部分(我都是參考這篇)。後來才發現,如果只是想要看到逐步偵錯的程式碼,其實可以不必要這麼麻煩,微軟的部分元件有開放 .pdb 可以看看程式碼到底了些什麼事,到底是元件的 bug 還是根本就是使用的方式錯了。

Visual Studio 2010 > Debug > Options and Settings > Debugging > General

  1. 取消 Enable Just My Code
  2. 勾選 Enable Source Server Support

image

Visual Studio 2010 > Debug > Options and Settings > Debugging > Symbols

image

在這邊我只想要設定 System.Web.Mvc.DLL 的話,就選擇 Only specifieds modules > Specify Modules

image

按下確定之後就會從 Server 上面下載可用的 .pdf 。也會出現要你同意 License 的部分

image

之後一樣設定中斷點在想進入的地方,如果要看 Htm.TextBoxFor 到底幫你做了些什麼

image

一樣 F11 就可以進入逐步偵錯了

image

不過比起加入原始碼參考還是有些不足的地方,尋找至定義在這裡就沒辦法使用囉,如果要看更詳細的資料還是下載原始碼吧,或是另外開起來當作手動參考,就可以不用修改 Web.Config 那些設定,也可以直接 Release 發佈。

image


Stepping into ASP.NET MVC source code with Visual Studio debugger
Setting up Visual Studio 2010 to step into Microsoft .NET Source

Address Editor for ASP.NET MVC3 – Format the Address String by Google Map

image
輸入地址
image
自動查詢 google map 顯示查詢結果,將地址字串轉為 api 回應的結果

[Required]
[Display(Name = "地址")]
[UIHint("MvcAddressEditor")]
public string Address { get; set; }

利用 UIHint 自動套入 Template,另外新增一個 MvcAddressEditor.cshtml

@model String
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
$(function () {
var geocoder;
var map;
$(document).ready(function () {
geocoder = new google.maps.Geocoder();
//default location
//可以新增查詢當地位置當成預設值
//http://code.google.com/intl/zh-TW/apis/maps/documentation/javascript/basics.html
var latlng = new google.maps.LatLng(40.69847032728747, -73.9514422416687);
var myOptions = {
zoom: 12,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
//first show map
map = new google.maps.Map($("#" + "@(Html.ClientIdFor(p => p) + "-google-map-display")")[0], myOptions);
});

$("#" + "@(Html.ClientIdFor(p => p))").live("change", function () {
var address = $(this).val();
geocoder = new google.maps.Geocoder();
if (geocoder) {
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[0].types[0] != "street_address") {
//the result address type
//alert("Please enter more detailed");
}
map.setCenter(results[0].geometry.location);
$("#"+"@(Html.ClientIdFor(p => p))").val(results[0].formatted_address);
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location
});
}
else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
});
});
</script>
@Html.TextBoxFor(it => Model, new { Style = "width: 500px;" })
<div id='@(Html.ClientIdFor(p => p) + "-google-map-display")' style="width: 300px; height: 300px">
</div>

可以從這裡下載 Download 用的是 Google Maps Javascript API 第 3 版

Reference

Google Maps Javascript API 第 3 版服務

ClientIdFor Mvc Helper

ASP.NET MVC – AllowHtml & FormCollection 發生的問題

[AllowHtml]
public string html { get; set; }

前幾天遇到一個問題,在 edit 頁面 post 的時候,一直遇到說安全性的問題,回傳具有危險性的字串而造成錯誤,的確回傳的欄位中有一個是具有 Html 的內容,但是已經在 Model 部分加上 [AllowHtml] 的屬性,而且同樣一個 Model 被不同的 Action 一起使用。確實在加上[AllowHtml] 屬性之後是可以回傳 html 的內容,但是錯誤訊息也指向是 html 內容回傳的錯誤。

具有潛在危險 Request.Form 的值已從用戶端 (html="<a></a>") 偵測到。 
描述: 要求驗證偵測到具有潛在危險的用戶端輸入值,對這個要求的處理已經中止。
這個值可能表示有人嘗試危害應用程式的安全性,例如跨站台的指令碼處理攻擊。若要
允許頁面覆寫應用程式要求驗證設定,請將 httpRuntime 組態區段中的 requestV
alidationMode 屬性設定為 requestValidationMode="2.0"。
例如: <httpRun
time requestValidationMode="2.0" />。
設定這項值之後,您就可以停用要求驗
證,方法是在頁面指令或 <pages>
組態區段中設定 validateRequest="false"。但
是我們強烈建議您的應用程式應該明確地檢查所有這類的輸入。
如需詳細資訊,請參閱
http://go.microsoft.com/fwlink/?LinkId=153133。

找了一下子才發現原因出在 FormCollection 這個參數上,在 Post 的時候,除了接收 Model 的部分,還多接收了一個 FormCollection 的參數

[HttpPost]
public ActionResult Edit(ItemModel model, FormCollection collection)

但是 FormCollection 並不會受到 Model 屬性的影響,即使在欄位屬性加上了 [AllowHtml] ,FormCollection 在判斷到有 Html 回傳的時候,還是判斷具有危險。

Reference

http://stackoverflow.com/questions/5022134/mvc-3-rtm-allowhtml-doesnt-work-when-using-formcollection

http://www.dotblogs.com.tw/johnny/archive/2010/03/01/13829.aspx

ASP.NET MVC3 Action Filter 控制輸出的 Layout

在開發 Mvc 的時候可能會用到 RenderAction 的方式去將其他 Action 輸出的結果一次回傳,RenderAction 回傳的結果會是比較簡單的,模組化之後的回傳。但是也有可能原本的 Action 已經是完整的頁面,但是可能為了需要過場特效或是各種原因她必須在某些狀況被包含在 Render 輸出。
原本預設的 Attribute 就有包含 [ChildActionOnly] 可以控制只能被包含在其他的回傳之中,配合 PartialView() 可以很容易做到單純子項目的輸出,但是今天想要的是可以把頁面回傳或是RenderAction 的回傳都一併處理掉。


public class ChildActionWithoutLayoutAttribute
: ActionFilterAttribute, IActionFilter, IResultFilter
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.IsChildAction)
{
try
{
((ViewResult)filterContext.Result).MasterName =
"~/Views/Shared/_Partial.cshtml";
}
catch
{
}
}
base.OnActionExecuted(filterContext);
}
}

利用 IsChildAction 的判斷可以在 OnActionExecuted 的時候去指定不同的 Master Page,要注意的是如果有在各個頁面中去各自指定的情況下,而不是統一由 _ViewStart.cshtml 統一指定的話,這個的 OnActionExecuted 必須改為 OnResultExecuted 才不會被頁面上的設置蓋掉。執行順序是 _ViewStart.cshtml > OnActionExecuted > View > OnResultExecuted

@{
Layout = "~/Views/Shared/_Layout.cshtml";
}

▲如果有在各自的 view 去額外設定的話,則要將 Action Filter 的事件延後
另外一個要注意的是,在這邊的 MasterName 設定為空字串或是 null 是沒有效果的,如果找不到值就會帶入預設的 Layout ,所以為了達到這個效果額外開了一個 _Partial.cshtml 裡面只有 @RenderBody() 一行。

如果要判斷是不是 Ajax 的請求也可以用  filterContext.HttpContext.Request.IsAjaxRequest() 來判斷。

ASP.NET MVC – MaxLength & MinLength Client 驗證

在 Mvc 的 Model 屬性中有 MaxLength 跟 MinLength 的限制屬性,一般情況像是 [Required] 必填還有 Regex 的限制都可以搭配 jquery.validate 做到 Client 的 javascript 驗證效果。我本來以為 MaxLength 跟 MinLength 應該也可以達到同樣的效果在 Javascript 先行驗證。

image

▲在 Model 加上長度限制

image

▲可是在 View 使用 TextboxFor 產出的 Html 並沒有加入 jquery.validate 的屬性

原本想說會不會是內建的方法沒有支援,但是怎麼想都覺得不太可能,這麼常用的方法,不支援也太不合常理了吧。爬了網路上的文章才發現如果要限制字串的長度的話,需要用 [StringLength] Attribute,才會在 TextboxFor 的時候自動加入 jquery.validate 的屬性。

image

▲需要用 [StringLength] Attribute

image

▲加入了 jquery.validate 需要的屬性

image

▲[StringLength] Attribute 也提供了最小長度的具名參數

image

▲Html Helper 產出的 Html

Reference

MaxLength Attribute not generating client-side validation attributes

http://stackoverflow.com/questions/6801656/maxlength-attribute-not-generating-client-side-validation-attributes

ASP.NET C# MVC ViewModel with valueinjecter

Introduction

Model 跟 ViewModel 這塊之前一直沒有發現比較好的方式去處理,Model 跟 ViewModel 在設計的時候通常都是會有一些關聯,但是又希望他們可以獨立運作不要關聯,真的需要用到他們的關係又可以很方便的去使用。在 presentation layer 跟 Domain model 應該是要可以分離的很徹底,又可以準確的被轉換。

image

可以看到在 Service 勢必要將 ViewModel 跟 Model 做一個轉換才能繼續接下來的動作,雖然這是兩個不同的東西,但是大多數情況下,他們之間還是會有斷不掉的羈絆,最近接了同事的案子用到了一個工具Value Injecter

Value Injecter

image

▲直接在 NuGet 上面就可以找到了

這個東西可以幹嘛呢,就是幫你把物件的值注入到另一個物件中,兩個物件可以完全獨立不用任何參考。

Implement

這邊主要的目的只是要建立一個訂單服務,傳入訂單資料,然後到資料庫去建立一份訂單起來。

image

▲Service 的建立訂單,看得出來這邊只接受 Domain.Order ,不接受 ViewModel 的型態,所以這邊需要的就是將 IOrderModel 轉變成可用的 Domain.Order。

image

▲終於輪到 ValueInjecter 出場了,在這邊將 ViewModel 傳入的資料注入我們需要的物件。

image

▲再來我們直接用Cart 這個 ViewModel 來呼叫 Service 的 Submit method

image

▲Injecter 的結果,直接的屬性可以被注入,可是再下層的 OrderDetails 卻沒有帶回應該有的部分。

在來改寫一下 inject 的部分,要完成 Inject 的動作必須要達成兩個條件

1.型態完全相同

2.命名完全一樣(包括大小寫)

image

▲InjectFrom 的時候可以自訂加入要一起作注入的物件,在這個時候先到底下關聯的部分先做轉換,所以這邊OrderDetails 接到的會是完全正確的 ICollection<OrderDetail> OrderDetails ,符合命名跟型態兩個條件。

image

▲將相關聯的部分也一併轉入

Reference

Value Injecter – object(s) to -> object mapper

http://valueinjecter.codeplex.com/releases/view/60311

快速搬移物件內的資料至其他型別 – .NET ValueInjecter

http://kelp.phate.org/2011/08/net-valueinjecter.html

ASP.NET MVC 3 Tools Update – Scaffolding

Introduction

在ASP.NET MVC 3 Tools Update中內建了Scaffolding這個功能,這個功能可以幹嘛呢,基本上它是 T4 Template 的延伸應用。POCO 工具也是 T4 的應用,能讓你根據某種規範去產生出程式碼,xml 或是 class 或是自己定義的規則。Scaffolding這個犯規的地方在於說他根據 Model 的 Class 幫你把整個網站基本功能全部架好了,如果只是後台應用幾乎很大一部份可以直接使用,修改也一定比從頭 Coding 到尾還要簡單多。

Requirement

Visual Studio 2010

Entity Framework 4.1

ASP.NET MVC 3 Tools Update

NuGet(Optional)

MvcScaffolding(Optional)

image

▲利用Scaffolding查詢就有數款工具可以參考

Implement

先準備好 Model 還有 Entity ,不論是 Database First ,Model First,Code First 都可以。

image

▲在MVC專案的Controllers資料夾新增Controller

image

▲如果沒有安裝MvcScaffolding則只會有上面的選項

image

▲把相關資料填完,這邊如果Model 跟 Context 在不同專案當中的話要加入參考,Advanced Options 可以設定 Master Page 的選項,按下OK之後就會開始產生相關的程式碼。

image

▲需要一段處理時間,輸出這邊可以看到產生的部分。

*如果選擇 using repository 的部分的話,需要一併建立相關聯的部分,不然參考的 interface 沒有建立起來會有錯誤。

 

image

▲產生的 List 畫面 ,包括 Edit,Create,Delete 基本功能

image

▲也加入了驗證機制

Summary

實際在執行的時候也並不是每種狀況下都可以100%沒有問題,包括像自訂類別的Complex。不過還是能夠幫助很快的先建立基本的架構,要是覺得預設的工具或是找到的功能都不夠完整的話,T4 Template 也可以不用別人的工具自己實作。之後會不會出現很多公司內部自己研發一整套 T4 ,邊跟客戶開會就邊設計 Model ,等到需求確定網站就完成了,速度快到嚇死人。難怪有人說,重點不是在寫了多少 code ,而是少寫了多少 code

Reference

Steven Sanderson – Scaffold your ASP.NET MVC 3 project with the MvcScaffolding package (NuGet MvcScaffolding package 作者的網誌)

http://blog.stevensanderson.com/2011/01/13/scaffold-your-aspnet-mvc-3-project-with-the-mvcscaffolding-package/

ASP.NET MVC 3 Tools Update

http://www.microsoft.com/download/en/details.aspx?id=1491

黑大的介紹文 ASP.NET MVC 3 Tools Update

http://blog.darkthread.net/post-2011-05-18-mvc-3-tools-update.aspx