Supporting multiple submit buttons on an ASP.NET MVC view


A while ago, I was asked for advice on how to support multiple submit buttons in an ASP.NET MVC application, preferably without using any JavaScript. The idea was that a form could contain more than one submit button issuing a form post to a different controller action.

The above situation can be solved in many ways, one a bit cleaner than the other. For example, one could post the form back to one action method and determine which method should be called from that action method. Good solution, however: not standardized within a project and just not that maintainable… A better solution in this case was to create an ActionNameSelectorAttribute.

Whenever you decorate an action method in a controller with the ActionNameSelectorAttribute (or a subclass), ASP.NET MVC will use this attribute to determine which action method to call. For example, one of the ASP.NET MVC ActionNameSelectorAttribute subclasses is the ActionNameAttribute. Guess what the action name for the following code snippet will be for ASP.NET MVC:

public class HomeController : Controller
{
    [ActionName("Index")]
    public ActionResult Abcdefghij()
    {
        return View();
    }
}

That’s correct: this action method will be called Index instead of Abcdefghij. What happens at runtime is that ASP.NET MVC checks the ActionNameAttribute and asks if it applies for a specific request. Now let’s see if we can use this behavior for our multiple submit button scenario.

The view

Since our view should not be aware of the server-side plumbing, we can simply create a view that looks like this.

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<MvcMultiButton.Models.Person>" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "//www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"">http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Create person</title>
    <script src="<%=Url.Content("~/Scripts/MicrosoftAjax.js")%>" type="text/javascript"></script>
    <script src="<%=Url.Content("~/Scripts/MicrosoftMvcAjax.js")%>" type="text/javascript"></script>
</head>
<body>

    <% Html.EnableClientValidation(); %>
    <% using (Html.BeginForm()) {%>

        <fieldset>
            <legend>Create person</legend>
            <p>
                <%= Html.LabelFor(model => model.Name) %>
                <%= Html.TextBoxFor(model => model.Name) %>
                <%= Html.ValidationMessageFor(model => model.Name) %>
            </p>
            <p>
                <%= Html.LabelFor(model => model.Email) %>
                <%= Html.TextBoxFor(model => model.Email) %>
                <%= Html.ValidationMessageFor(model => model.Email) %>
            </p>
            <p>
                <input type="submit" value="Cancel" name="action" />
                <input type="submit" value="Create" name="action" />
            </p>
        </fieldset>

    <% } %>

    <div>
        <%=Html.ActionLink("Back to List", "Index") %>
    </div>

</body>
</html>

Note the two submit buttons (namely “Cancel” and “Create”), both named “action” but with a different value attribute.

The controller

Our controller should also not contain too much logic for determining the correct action method to be called. Here’s what I propose:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new Person());
    }

    [HttpPost]
    [MultiButton(MatchFormKey="action", MatchFormValue="Cancel")]
    public ActionResult Cancel()
    {
        return Content("Cancel clicked");
    }

    [HttpPost]
    [MultiButton(MatchFormKey = "action", MatchFormValue = "Create")]
    public ActionResult Create(Person person)
    {
        return Content("Create clicked");
    }
}

Some things to note:

  • There’s the Index action method which just renders the view described previously.
  • There’s a Cancel action method which will trigger when clicking the Cancel button.
  • There’s a Create action method which will trigger when clicking the Create button.

Now how do these last two work… You may also have noticed the MultiButtonAttribute being applied. We’ll see the implementation in a minute. In short, this is a subclass for the ActionNameSelectorAttribute, triggering on the parameters MatchFormKey and MatchFormValues. Now let’s see how the MultiButtonAttribute class is built…

The MultiButtonAttribute class

Now do be surprised of the amount of code that is coming…

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MultiButtonAttribute : ActionNameSelectorAttribute
{
    public string MatchFormKey { get; set; }
    public string MatchFormValue { get; set; }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {
        return controllerContext.HttpContext.Request[MatchFormKey] != null &&
            controllerContext.HttpContext.Request[MatchFormKey] == MatchFormValue;
    }
}

When applying the MultiButtonAttribute to an action method, ASP.NET MVC will come and call the IsValidName method. Next, we just check if the MatchFormKey value is one of the request keys, and the MatchFormValue matches the value in the request. Simple, straightforward and re-usable.

Advertisements

Đặng Thế Phong: Một tài năng đoản mệnh


Văn đàn Việt Nam những năm 1930-1945 đã phải chứng kiến nhà văn Vũ Trọng Phụng ra đi ở tuổi 27 (1939) vì bệnh lao để lại sự tiếc thương vô hạn cho những người yêu thích văn học nước nhà.

Năm 1940, chúng ta mất nhà thơ nổi tiếng Hàn Mặc Tử khi ông mới 28 tuổi vì bệnh phong.

Trong lĩnh vực âm nhạc, một tài năng khác cũng ra đi ở độ tuổi rất trẻ (24) cũng bởi căn bệnh thuộc hàng tứ chứng nan y như Vũ Trọng Phụng. Đó là Đặng Thế Phong.

Cũng như Vũ Trọng Phụng, phải mãi sau này khi làn gió đổi mới đến với đất nước ta thì các nhạc phẩm nổi đình đám một thời của Đặng Thế Phong và các nhạc sĩ tiền chiến khác mới được đánh giá một cách đúng mức trong kho tàng lịch sử âm nhạc Việt Nam mặc dầu từ rất lâu rồi nó đã có chỗ đứng vững chắc trong lòng người hâm mộ.

Cũng giống như Nguyễn Đình Thi, chỉ với hai bài hát cách mạng "Diệt phát xít" và "Người Hà Nội", ông đã là một nhạc sĩ nổi tiếng bậc cao thủ trong giới nhạc sĩ Việt Nam đương đại.

Đặng Thế Phong cũng vậy. Chỉ với ba bài hát "Đêm thu", "Con thuyền không bến" và "Giọt mưa thu", ông đã là nhạc sĩ có một không hai gắn liền tên tuổi bất hủ của mình với mùa thu. Xem thế đủ biết không phải cứ đẻ sòn sòn đã là hay. Vấn đề là ở chỗ ít mà tinh, mà để đời.

Đặng Thế Phong là vậy đó.

Sinh ra trong một gia đình công chức ở thành Nam nhưng không may mắn. Cha mất sớm buộc Đặng Thế Phong phải sớm tìm con đường mưu sinh: vừa đi học, vừa vẽ thuê kiếm sống.

Mới hơn hai mươi tuổi đầu đã lang thang nơi đất khách quê người tận xứ Nam Vang làm đủ thứ nghề vẽ thuê, dạy nhạc để sống, để học và để sáng tác. Năng khiếu bẩm sinh về âm nhạc cho phép Đặng Thế Phong có thể vừa ôm đàn, vừa sáng tác, vừa biểu diễn tác phẩm của mình với chất giọng tenor rất đặc biệt.

Theo các bạn bè và bà Đặng Thanh Kim, em út của Đặng Thế Phong thì ông là một thanh niên rất điển trai, hoạt bát, thích ăn diện, ăn nói rất có duyên lại giỏi đàn hát nên được rất nhiều cô gái thành Nam yêu mến. Đặc biệt Đặng Thế Phong có biệt tài sắm các vai nữ, y như thật. Nhiều cô gái mê ông vì thế.

Trong số đó có ba cô lọt vào mắt xanh chàng trai trẻ đa tình. Đó là cô Hà Tiên, học sinh Trường Sarcree coeur Nam Định; cô Nguyễn Thị Na, tức Lê ở khu Ga Hải Phòng; và cô Bạch Yến ở Hàng Bông, Hà Nội.

Trong ba cô thì Bạch Yến xem ra nặng tình hơn cả bởi cô là người chăm sóc và tiễn đưa Đặng Thế Phong đến nơi an nghỉ cuối cùng và buồn thay cuộc đời của cả ba người đều lỡ dở.

Đặng Thế Phong viết "Đêm thu" năm 1940. Ca khúc này ông viết và biểu diễn ở những đêm lửa trại, lời ca được nhiều bạn bè đóng góp chỉnh sửa nên rất trong trẻo hồn nhiên, thể hiện nỗi đam mê của con người trước thiên nhiên và cuộc sống. Ngay lập tức bài hát được giới trẻ hưởng ứng nhiệt liệt. Song phải đến "Con thuyền không bến" viết vào tháng 9/1941 sau khi nhạc sĩ đi Phnom Penh trở về thì tên tuổi Đặng Thế Phong mới nổi như cồn.

Hôm đó tại Nhà hát Lớn Hà Nội, ca sĩ Vũ Thị Hiển lần đầu tiên hát ca khúc này đã làm xôn xao dư luận. Sau đó chính tác giả đã trình bày bài hát này tại rạp Olimpia ở phố Hàng Da thì công chúng Hà Nội càng thêm mến mộ một tài năng. Không chỉ bởi chất giọng của người nhạc sĩ kiêm ca sĩ mà bởi chất thơ nhuần nhuyễn trong từng giai điệu và lời ca thẫm đẫm chất thu:

Đêm nay thu sang cùng heo may
Đêm nay sương lam mờ chân mây
Thuyền ai lờ lững trôi xuôi dòng
Như nhớ thương ai chùng tơ lòng.

Có một điều rất khó lý giải là tại sao ở cái tuổi mới hơn hai mươi mà Đặng Thế Phong lại nhuần nhuyễn từ ca từ đến làn điệu thấm đẫm chất dân ca đồng bằng Bắc Bộ như vậy.

Lướt theo chiều gió. Một con thuyền theo trăng trong.

Giai điệu này lặp đi lặp lại nhiều lần như một điểm nhấn, nó nghe như một làn điệu dân ca mơ hồ, chầu văn hay chèo nào đó.

Cũng lạ, trong lúc âm nhạc Pháp đang tràn ngập Việt Nam, tâm lý hướng ngoại đang là "mốt" trong giới trí thức, những đĩa hát tango chinoise 78 vòng/phút đầy chất lính kèn lê dương, phong trào tân nhạc chủ yếu là đặt lời cho các bài hát tây thì nhạc phẩm của Đặng Thế Phong lại thấm đẫm tâm hồn Việt…

Năm 1942, Đặng Thế Phong viết "Vạn cổ sầu", sau Bùi Công Kỳ sửa lời và đặt tên mới là "Giọt mưa thu". Đây là giai phẩm thứ ba của Đặng Thế Phong và lại có chủ đề là mùa thu:

 http://www.nhaccuatui.com/m/AqNq6Rpttr

Ngoài hiên giọt mưa thu thánh thót rơi
Trời lắng u buồn mây hắt hiu ngừng trôi.

Giọt mưa thu rơi thánh thót. Thật không có hình ảnh nào sống động chính xác hơn. Hơn nửa thế kỷ đã trôi qua, người ta vẫn ví những khúc ca lộng lẫy đó như là "hoa hậu" của ca từ âm nhạc hiện đại Việt Nam.

"Giọt mưa thu" là một nhạc phẩm song cũng là một mảng tâm hồn của con người. Nó như một lời ru kỳ diệu đưa hồn người hòa đồng vào các cung bậc cảm xúc đầy sắc màu của thiên nhiên, nó như giãi bày hộ nỗi buồn nhân thế đang mong được giải tỏa.

 

Một tiếng tơ lòng cất lên mỏng manh như tâm hồn người nghệ sĩ.

Đã bao năm rồi, đã có biết bao thế hệ người Việt Nam cất tiếng hát:

Ai nức nở thương đời
Châu buông mau, dương thế bao la sầu.

Sự tích Ngưu Lang – Chức Nữ chia xa, mỗi năm được gặp nhau một lần ngắn ngủi, nước mắt của tình yêu và nỗi nhớ giội từ trên trời cao xuống hiu hắt lắng đọng từ cõi vô biên, phải chăng đó là chất xúc tác để chàng nghệ sĩ họ Đặng cảm xúc cất lên những âm thanh siêu ảo vượt lên nỗi buồn thế tục, phải chăng giọt mưa thu chính là thiên sứ của tình yêu đã được nhân cách hóa một cách tài tình.

Hãy thử tưởng tượng trong một ngày mưa thu rơi thánh thót ngoài hiên, tiếng vĩ cầm cất lên réo rắt những giai điệu như nức nở của giọt mưa thu của Đặng Thế Phong, người ta có cảm nhận như đâu đây tiếng lòng thổn thúc của thơ Verlain, hay tiếng nhạc buồn của Chopin, hay những cánh lá vàng trong bức tranh "Mùa thu vàng" của Levitan. Thiên nhiên và lòng người hòa quyện một cách tài tình trong bàn tay sáng tạo của người nghệ sĩ.

Chỉ với ba nhạc phẩm thôi, Đặng Thế Phong đã là một hiện tượng trong lịch sử nền âm nhạc ViệtNam. Từ "Đêm thu" với những kỷ niệm tươi trẻ hồi thơ bé cùng Tết Trung thu:

Qua lá cành ánh trăng lan dịu dàng
Ru hồn bao nhớ nhung.

đến "Con thuyền không bến" mộng mơ:

Ánh trăng mờ chiếu.
Một con thuyền trong đêm thâu
Trên sông bao la thuyền mơ bến nơi đâu?

đã là một bước chuyển rất lớn trong tâm hồn người nghệ sĩ.

Đến "Giọt mưa thu": Nghe gió thoảng mơ hồ trong mưa thu ai khóc ai than hờ.

Có thể thấy Đặng Thế Phong đã hóa thân vào tiếng thu lòng thổn thức một cách tài tình như thế nào.

Trong số những người bạn học thân của Đặng Thế Phong hồi thiếu thời ở Nam Định có Vũ Đức Oong. Ông này sau tham gia cách mạng bị Pháp bắt giam ở nhà ngục Sơn La. Từ trong tù, nghe tin bạn bị lao mà chết, năm 1943, ông cảm thán viết bài thơ "Nhớ Thế Phong", trong đó có đoạn:

Đã mấy thu rồi xa cách lắm
Mấy lần thu tới mấy thu đi
Bạn ơi! Có biết thu này khác
Trăng úa bên rừng khóc biệt ly!

Cũng lại ý tứ mùa thu. Phải chăng hai người bạn nối khố này rất đồng cảm với nhau. Năm 1945 ra tù tham gia cướp chính quyền ở Nam Định, ông Oong đã đến ngay gia đình Đặng Thế Phong và còn giữ nhiều tấm ảnh quý về gia đình họ Đặng này. Ông nói: "Nếu tôi không bị đi tù thì tôi không để cho Phong đi Campuchia và ốm đau bệnh tật như thế!".

Thoắt đấy mà đã chín chục năm ngày sinh của một nhạc sĩ tài hoa nhưng đoản mệnh. Hôm qua chúng ta đã hát những ca khúc buồn của ông, hôm nay chúng ta tiếp tục hát những ca khúc của ông nhưng với một tâm thế khác. Ngày mai cũng vậy. Bởi lẽ đó là những ca khúc bất hủ của một tài năng đích thực

My bonsai


Password Recovery in an ASP.NET MVC Project


Password Recovery in an ASP.NET MVC Project

While rewriting my personal web site with ASP.NET I noticed that although support for the ASP.NET Membership Provider comes included out of the box in a ASP.NET MVC project not all the options are fully implemented to the same extend that they are in a brand new ASP.NET WebForms project. For example, the option to reset your own password if you forgot your old one is not available out of the box in an ASP.NET MVC project.

Out of the box the following options are fully implemented in a ASP.NET MVC project:

  • Login in
  • Login out
  • Change your password
  • Create new user

Adding support for Password Recovery to an ASP.NET MVC project turned out to be pretty easy as the core functionality already exists in the Membership Provider and it’s just a matter of calling it from your application.

The process that I implemented goes like this:

  • From the LogOn view users can go to the PasswordReset view
  • In the PasswordReset the user indicates his/her username and then they are sent to the QuestionAndAnswer view
  • In the QuestionAndAnswer view the user enters the answer to their own security question
  • Finally, the user is sent to the PasswordResetFinal view with a message indicating that their password has been reset and e-mailed to them.

New Views and Controllers

LogOn

The first thing that I did was update the LogOn view that comes with ASP.NET and added a link to start the Password Reset process. I wired this link to the a new method called PasswordReset in the AccountController.

password_logon

Password Reset

Secondly I created the HTTP-GET PasswordReset method in the AccountController and a very simple view (PasswordReset.aspx) to allow the user to enter his/her username so that we can reset their password. The PasswordReset.aspx view is extremely simple as it only has a textbox where the user enter their user name.

password_reset

   1: public ActionResult PasswordReset()
   2: {
   3:     if (!MembershipService.PasswordResetEnabled) throw new Exception("Password reset is not allowed");
   4:     return View();
   5: } 

I also implemented an HTTP-POST PasswordReset method in the AccountController to pick up the data and continue the process. This controller method decides whether the next step is to reset the password for this user or if we need to ask him/her a Password Recovery question before we reset their password. This step is required to honor the requiresQuestionAndAnswer configuration setting in the ASP.NET Membership Provider.

   1: [HttpPost]
   2: public ActionResult PasswordReset(string userName)
   3: {
   4:     if (!MembershipService.PasswordResetEnabled) throw new Exception("Password reset is not allowed");
   5:  
   6:     if (MembershipService.RequiresQuestionAndAnswer)
   7:     {
   8:         return RedirectToAction("QuestionAndAnswer", new { userName = userName } );
   9:     }
  10:     else
  11:     {
  12:         MembershipService.ResetPassword(userName, GetLoginUrl());
  13:         return RedirectToAction("PasswordResetFinal", new { userName = userName });
  14:     }
  15: } 
Password Question and Answer

If the Membership Provider is configured to require a question and answer before resetting a user’s password then we route users to the QuestionAndAnswer view. This view is also very simple as it merely has two labels (one with the username and another with password question for the user) and a textbox where the user will enter the answer to their password question.

Security Question View

To support this QuestionAndAnswerView I implemented an HTTP-GET controller method that fetches the security question for the username entered in the PasswordReset view.

public ActionResult QuestionAndAnswer(string userName)
{
    if (!MembershipService.PasswordResetEnabled) throw new Exception("Password reset is not allowed");
    ViewData["UserName"] = userName;
    ViewData["Question"] = MembershipService.GetUserQuestion(userName);
    return View();
}

Finally I added an HTTP-POST method to support the QuestionAndAnswer. By the time we get to this HTTP-POST method we have all the information that we need to reset a user’s password (namely the user name and the answer to the security question.) Hence this method is where the call to actually reset the user’s password actually happens.

[HttpPost]
public ActionResult QuestionAndAnswer(string userName, string answer)
{
    if (!MembershipService.PasswordResetEnabled) throw new Exception("Password reset is not allowed");
MembershipService.ResetPassword(userName, answer, GetLoginUrl());
    return RedirectToAction("PasswordResetFinal", new { userName = userName });
}

Password Reset Final

The last step in the process if a new view called PasswordResetFinal that just displays a message to the user telling him/her that a new password has been generated and e-mailed to them.

Password Reset Final View

Changes to the Model

Every ASP.NET MVC project comes with a default model called AccountMembershipService to support the Views and Controllers that handle membership information. This AccountMembershipService is not much more than a wrapper for the ASP.NET MembershipProvider. Adding functionality to this model to support the password reset operation was very simple as the MembershipProvider already provides the core functions. The MembershipService referenced in the controller actions in the code actually point to an instance of this AccountMembershipService.

For example, the QuestionAndAnswer view calls the following method to to retrieve the security question for a user. Notice how this method’s only job is to forward the calls to the Membership provider.

public string GetUserQuestion(string userName)
{
MembershipUser user = _provider.GetUser(userName, false);
    if (user == null)
    {
        throw new Exception("User name not found");
    }
    else
    {
    return user.PasswordQuestion;
    }
}

All in all I added two methods to the AccountMembershipService (one to retrieve a user’s security question and one to actually do the password reset) plus a few properties to expose a couple of features of the Membership provider (like the need for a security question) that were not exposed on the default implementation.

In Summary…

As I indicated at the beginning of this blog post, adding support for Password Recovery to an ASP.NET MVC project turned out to be pretty easy as the core of the functionality already exists in the Membership Provider.

JetBrains Intellij Idea 8.1.0


Phần mềm soạn thảo JAVASCRIPT tuyệt vời cho dân lập trình!
IntelliJ IDEA features excellent JavaScript editor for productive JavaScript programming. All of its features including code completion, error highlighting and refactoring, quick fixes and intention actions are JavaScript aware and let you productively create efficient JavaScript code.
IntelliJ IDEA is an intelligent Java IDE intensely focused on developer productivity. It provides a robust  combination of enhanced development tools, including refactoring, J2EE support, Ant, JUnit, and version controls integration. Packaged with an intelligent Java editor, coding assistance and advanced code automation tools, IntelliJ IDEA enables Java programmers to boost their productivity while reducing routine time consuming tasks.

JS_DOM_Completion

Key Benefits
— Increase developer’s capabilities
— Full-spectrum web-development support (HTML, CSS, JavaScript)
— Full JSP 2.0 specification support
— J2ME support
Prevent and fix bugs
— New Code Inspections
— New Intention Actions and Quick Fixes
— New Debugger Features
Work productively with code
— Quick Definition Lookup
— Code Inspection Profiles
— Enhanced code assistance and formatting
— Improved editor splitting and tab grouping
Understand and improve code design
— Module dependency viewer and backward/cyclic dependencies analysis
— New and enhanced refactorings
Save developers time navigating projects
— Favorites view
— External stacktrace-to-code navigation
— Welcome screen
Make it easy to work with teams
— New VCS Integrations (Subversion, Perforce)
— JBuilder/Eclipse project import
Enable developers to write plug-ins
— Expanded, improved API architecture
— New Language API
— New module type for plug-ins
File Size: 117 MB
http://rapidshare.com/files/197739437/inteledg.rar

[Trạng Quỳnh] Lệnh Vua ban


Một đêm kia, quan coi thiên văn trong triều tâu với nhà vua :
– Thưa Hoàng Thượng, thần quan sát đã bảy ngày nay, hễ vào giờ này là góc trời phương Nam loé lên một ánh sao thật kỳ lạ. Theo thần biết thì đó là vùng đất xứ Thanh. Ðiều đó chứng tỏ có nhân tài chưa xuất đầu lộ diện, cúi xin Hoàng Thượng cho xuống chiếu chiêu hiền, may ra có người có người tài ra giúp nước nhà.

Vua chấp thuận và ban lệnh cho dân chúng vùng Thanh Hóa – mỗi làng phải nộp cho nhà vua một con trâu đực có chửa, hẹn trong vòng một tháng thì phải nộp đủ, nếu không bị tội.
Nghe tin ấy, dân chúng già trẻ cùng các hương dịch, chức sắc các làng lo mất ăn mất ngủ. Phen này ai cũng chắc mẩm là chết, vì có bao giờ trâu đực lại có chửa! Họa chăng là xuống gặp Diêm Vương mà mượn! Chiều ấy, cha Quỳnh là hương xã trong làng, đi họp về nét mặt nặng trĩu buồn rầu. Quỳnh thấy thế bèn hỏi thì được ông kể lại cho nghe đầu đuôi sự việc oái oăm kia. Nghe xong, Quỳnh cười, thưa với cha:
– Xin cha cho con một ít tiền làm lộ phí đi đường. Con lên kinh lo việc này thì nhất định vua sẽ không làm tội làng ta nữa đâu.
Cha Quỳnh miễn cưỡng bằng lòng. Sáng hôm sau, Quỳnh khăn gói lên đường. Ðến kinh thành, cậu bé nghỉ ngơi ở một quán trọ để đợi cơ hội…
Ngay khi nghe biết tin nhà vua cùng các quan hầu đi dạo cảnh phố phường, Quỳnh nấp dưới một bụi trúc bên đường, ra sức la khóc, gào thét cốt cho nhà vua nghe được. Nghe tiếng con nít khóc vang rân, vua sai lính dẫn đến cho ngài hỏi:
– Bé con kia có việc gì buồn bực sợ hãi mà la om sòm thế?
– Thưa Ðức Vua, con khổ và buồn quá nên bỏ nhà đi, lại định tự vẫn chết cho xong…
– Hãy nói cho ta nghe xem là việc gì nào? Quỳnh giả vờ khóc tức tưởi rồi thưa:
– Con vốn mất mẹ từ lâu, sống thui thủi với bố, muốn có em bé để bồng ẵm cho vui nhưng bảo thế nào bố con vẫn không chịu đẻ cho con một đứa.
Vua nghe xong không chịu nổi cười, vuốt râu bảo:
– Cái thằng ôn con này hay thật! Bố mày là đàn ông thì đẻ cái nỗi gì chứ! Ðúng là dở hơi!
Bấy giờ, Quỳnh tiến sát lại bên kiệu vua mà nói :
– Con không dở hơi đâu ạ, vì chính con nghe bố nói triều đình cũng có chiếu chỉ xuống bắt dân phải nộp mỗi làng một con trâu đực có chửa cơ mà!
Vua nghe xong té ngửa, trong bụng biết ngay đây là người tài, rất ứng với lời tiên đoán của quan thiên văn dạo nào. Lập tức, vua ra lệnh bãi bỏ chiếu chỉ "dở hơi" kia ngay.
Mọi người biết tiếng Quỳnh từ đó.

ASP.NET MVC and File Uploads


Introduction

Not any Web applications need to upload a file, but any serious Web framework must provide some tooling for developers to write file upload functionalities quickly and comfortably. ASP.NET solved the issue by offering the FileUpload server control. Frankly, there’s not much that developers can invent when it comes to file uploads. Browsers offer some core functionality through their implementation of the <input type=file> element. Anything beyond this, though, would require some rich client side capabilities such as an ActiveX control, Silverlight or perhaps Flash. As long as you remain in the Web programming realm you are limited to using the basic browser provided file uploader which is in turn limited to uploading one file at a time.

ASP.NET MVC is only a new framework for ASP.NET programming; so it can’t really offer anything different from what you did with Web Forms. The model binding infrastructure, however, can help a bit to make the whole process of uploading and storing file content seamless.

Multi-part Input Forms

Let’s go through a typical example of file upload. Suppose you are writing a Web interface to let users register to a given community. You expect users to provide their account information and want them to choose a picture. So you have an action method like below that takes users to a registration page:

Code– Sokhanh03 Blog
  1. [HttpGet]
  2. public ActionResult Register()
  3. {
  4. return View();
  5. }

 

The registration page shows a classic input form where a new user can specify its own name, email and upload a picture.

view sourceprint?

Code– Sokhanh03 Blog
  1. <% Html.BeginForm("Register", "User", FormMethod.Post,
  2. new {enctype = "multipart/form-data"}); %>
  3. <%= Html.TextBox("Name") %>
  4. <%= Html.TextBox("Email") %>
  5. <input type="file" id="Picture" name="Picture" />
  6. <input type="submit" name="btnAdd" value="Add" />
  7. <% Html.EndForm(); %>

 

HTTP dictates that any form expected to upload the content of a file must have the enctype attribute set to the multipart/form-data value. The enctype attribute specifies the content type used to submit (via post) the form to the server. The enctype attribute defaults to a value of application/x-www-form-urlencoded. The value must be changed to multipart/form-data when the form contains an <input> of type file. The reason is that a multipart form is allowed to contain any data type in its multiple parts – whether text, binary, or whatever else. The actual content and type are determined when the data is parsed out. You could even make multipart/form-data the default value for enctype in your forms but this would generate a bit more traffic (especially for headers) and is therefore preferable that you bring it in only when it’s strictly needed.

The user interface of the input file field depends on the browser and there’s not much you can do other than making it a bit more stylish via CSS. (Not all browsers let you style input file fields, however.) You are not allowed to write to the field via script; and when you attempt to read its content depending on the browser’s implementation either you get the sole file name (Firefox) or a fully qualified name with fake path (Internet Explorer).

The canonical behavior of the input file field element is showing the user an open-file dialog box, letting the user pick up a file from the local machine, and displaying the full path in the (read-only) text box. Next, when the multi-part form is submitted, the browser will pick up the file name and prepare HTTP packets that upload both plain text and file content – binary or text.

That’s all for the client side. Let’s turn our attention to the server.

Model Binding with Uploaded Files

The preceding form will post its content to the Register action of the User controller. Here’s a possible signature for the action method:

Code– Sokhanh03 Blog
  1. [HttpPost, ActionName("Register")]
  2. public ActionResult Add(UserInputModel dto)
  3. {
  4. :
  5. }

 

The UserInputModel class indicates a data transfer object that will collect the data being posted to the server. The following template will work well with native ASP.NET MVC model binding.

Code– Sokhanh03 Blog
  1. public class UserInputModel
  2. {
  3. // User name
  4. public String Name { get; set; }
  5. // User email address
  6. public String Email { get; set; }
  7. // Name of the JPG file with the user’s picture
  8. public String Picture { get; set; }
  9. }

 

As expected, the Name and Email members will receive the value of matching input fields. What about the Picture member? The default model binder will try to bind it to the output posted by the Picture field. The Picture uploads the entire content of the selected file. Here’s an excerpt from the request packet:

view sourceprint?

Code– Sokhanh03 Blog
  1. —————————–7da2381760049e
  2. Content-Disposition: form-data; name="Name"
  3. Dino
  4. —————————–7da2381760049e
  5. Content-Disposition: form-data; name="Email"
  6. Esposito
  7. —————————–7da2381760049e
  8. Content-Disposition: form-data; name="Picture"; filename="dinoe.jpg"
  9. Content-Type: image/jpeg

 

The content for the Picture field is not a scalar value. ASP.NET MVC will render it through an HttpPostedFileBase object. This means that if you define Picture as a String member (meaning you want it to contain the file name) it will receive instead the output of the ToString method as defined by the HttpPostedFileBase class. This default behavior can be modified by creating a custom value provider for posted files that resolves the match using a different logic and storing the file name to the matching property. The key point, however, seems to be quite another.

When you upload a form like the one discussed here you want to achieve two main goals. First, you want to know the name of the image and store it in the database. Second, you want to save the actual file content somewhere on the server – either as a server file or into some database table. Modifying the value provider to bind the file name directly to the property is not enough. In addition, you still need to access the ASP.NET representation of the posted file to save it in some way. You still need code like the one shown below:

Code– Sokhanh03 Blog
  1. —————————–7da2381760049e
  2. Content-Disposition: form-data; name="Name"
  3. Dino
  4. —————————–7da2381760049e
  5. Content-Disposition: form-data; name="Email"
  6. Esposito
  7. —————————–7da2381760049e
  8. Content-Disposition: form-data; name="Picture"; filename="dinoe.jpg"
  9. Content-Type: image/jpeg

 

The code loops through any posted files and for each occurrence builds a server path and saves the uploaded content. The list of uploaded files is reached through the Files collection of the Request object. This code is exactly the same you would write for a classic ASP.NET application.

What ASP.NET MVC allows you to do with a different style is the retrieval of the files. You can do that through the members of the method parameter. You change the definition of the UserInputModel class as follows:

 

Code– Sokhanh03 Blog
  1. public class UserInputModel
  2. {
  3. // User name
  4. public String Name { get; set; }
  5. // User email address
  6. public String Email { get; set; }
  7. // Reference to the uploaded file
  8. public HttpPostedFileBase Picture { get; set; }
  9. }

 

Now that the Picture member has the matching type the file value provider can easily assign it an instance of the HttpPostedFileBase object created by the ASP.NET runtime. The result is that now you can write the following code to process the action:

Code– Sokhanh03 Blog
  1. public ActionResult Add(UserInputModel dto)
  2. {
  3. var destinationFolder = Server.MapPath("/Users");
  4. var postedFile = dto.Picture;
  5. if (postedFile.ContentLength > 0)
  6. {
  7. var fileName = Path.GetFileName(postedFile.FileName);
  8. var path = Path.Combine(destinationFolder, fileName);
  9. postedFile.SaveAs(path);
  10. }
  11. return View();
  12. }

 

The net effect doesn’t really change, but the code is a bit more abstract and in line with the model binding approach.

A Few Things to Keep in Mind

Any uploaded files will be likely saved somewhere on the server. It should be noted that creating files on the Web server is not usually an operation that can be accomplished standing the default permission set. Any ASP.NET application runs under the account of the worker process serving the application pool the application belongs to. Under normal circumstances, this account is NETWORK SERVICE and it isn’t granted the permission to create new files. This means that the previous won’t work unless you either change the account behind the ASP.NET application or elevate the privileges of the default account.

For years, the identity of the application pool has been a fixed identity–the aforementioned NETWORKSERVICE account, a relatively low-privileged built-in identity in Windows. Originally welcomed as an excellent security measure, the use of a single account for a potentially high number of concurrently running services in the end created more troubles than it helped to solve. In a nutshell, services running under the same account could tamper each other. For this reason, in IIS 7.5, worker processes by default run under unique identities automatically and transparently created for each newly created application pool. The underlying technology is known as Virtual Accounts and is currently supported by Windows Server 2008 R2 and Windows 7. For more information, have a look at http://technet.microsoft.com/en-us/library/dd548356(WS.10).aspx.

Another point that may be source of headaches is the maximum size of the request and subsequently the maximum size allowed for your uploads. By default, any ASP.NET request can’t be longer than 4 MB. This amount should include any uploads, headers, body and whatever is being transmitted. The value is configurable at various levels. You do that through the maxRequestLength entry in the httpRuntime section of the web.config file:

Code– Sokhanh03 Blog
  1. <system.web>
  2. <httpRuntime maxRequestLength="6000" />
  3. </system.web>

 

It goes without saying that the larger a request can be, the more you potentially leave room the hackers to prepare attacks to your site. Finally, note that in a hosting scenario your application level settings may be ignored if the hoster has set a different limit at the domain level and locked down the maxRequestLength property at lower levels.

Multiple File Uploads

What about multiple file uploads? As long as the overall size of all uploads is compatible with the current maximum length of a request, you are allowed to upload multiple files within a single request. However, consider that Web browsers just don’t know how to upload multiple files. All a Web browser can do is uploading a single file and only if you reference it through an input element of type file. To upload multiple files, you can resort to some client side ad hoc component or place multiple INPUT elements in the form. If multiple INPUT elements are placed, and properly named, the following class will bind them all:

Code– Sokhanh03 Blog
  1. public class UserInputModel
  2. {
  3. public String Name { get; set; }
  4. public String Email { get; set; }
  5. public HttpPostedFileBase Picture { get; set; }
  6. public IList<HttpPostedFileBase> AlternatePictures { get; set; }
  7. }

 

The class represents the data posted for a new user with a default picture and a list of alternate pictures. The markup for alternate pictures is below:

Code– Sokhanh03 Blog
  1. <input type="file" id="AlternatePictures[0]" name="AlternatePictures[0]" />
  2. <input type="file" id="AlternatePictures[1]" name="AlternatePictures[1]" />

 

In ASP.NET MVC, model binding does the trick!

Summary

ASP.NET MVC runs on the same runtime environment of ASP.NET Web Forms. So the way in which some basic tasks are accomplished can’t be significantly different. And if it is different it’s because of a different level of abstraction. File upload is the perfect example to explain this concept. It works like in Web Forms, but model binding can simplify the way in which you write your server code and add a bit of (very welcome) abstraction.