Twitter style paging with ASP.NET MVC and jQuery


 

I really like the simplicity of the AJAX paging at Twitter so I decided to use the same type of paging, including a similar more button, on the start page on this site. It actually surprised me how simple it was to build it, including fallback for visitors that doesn’t have javascript enabled (such as our dear friend Google), with ASP.NET MVC and a few lines of javascript with jQuery.

Basic controller logic

Being an advocate of progressive enhancement I started out by building a non-AJAX version of the feature. In the controller for the start page I let the default (Index) method have a nullable int parameter named entryCount which tells the method how many of the latest blog entries it should return for the view to display.

public class HomeController : Controller

{

private const int defaultEntryCount = 10;


public ActionResult Index(int? entryCount)

{

if (!entryCount.HasValue)
entryCount = defaultEntryCount;
//Retrieve the first page with a page size of entryCount
int totalItems;
IEnumerable<Entry> entries = GetLatestEntries(1, 
entryCount.Value,out totalItems);
if (entryCount < totalItems)
AddMoreUrlToViewData(entryCount.Value);
return View(entries);
}
private void AddMoreUrlToViewData(intentryCount)
{
ViewData["moreUrl"] = Url.Action("Index", "Home",
 new { entryCount = entryCount + defaultEntryCount });
}
}

The method begins by making sure that the entryCount variable has a value, setting it to a default value if the parameter is null. It then retrieves as many of the latest blog entries as entryCount specifies by calling the GetLatestEntries method. I’ve omitted the GetLatestEntries method as it’s implementation will vary depending on blogging platform. The GetLatestEntries method also has an out parameter, totalItems, which tells us the total number of blog entries. I’m not a big fan of using out parameters but that’s the way the framework that I used for my blog (EPiServer Community) works so I decided to follow that pattern for consistency. If you use some other type of blogging platform I would recommend making a field of the totalItems.

The method moves on to check if there are more blog entries than the  number that will be displayed, in other words if a link for showing more entries should be displayed. If so, it calls the AddMoreUrlToViewData which, you guessed it, adds a route URL for displaying more entries to the ViewData dictionary.

Finally the method returns a ViewResult with the list of blog entries as the model.

Creating the views

The Index view for the Home controller is very simple. It simply renders a partial view named EntryTeaserList, passing along the list of blog entries (Model) and the it’s ViewData dictionary.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage<IEnumerable<Entry>>" %>
<asp:ContentContentPlaceHolderID="PrimaryMainContent"runat="server">
<% Html.RenderPartial("EntryTeaserList", Model, ViewData); %>
</asp:Content>

The partial view EntryTeaserList offers a bit more excitement. It renders an ordered list and displays a teaser for each blog entry by rendering another partial view, EntryTeaser, inside a list item, passing in each individual blog entry as model to it. It also checks if the ViewData dictionary contains a URL for a more link. If it does it renders a link with that URL in the href attribute.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Entry>>" %>
<div id="entryTeaserList">
<ol>
<% foreach (Entry item in Model) { %>
<li class="entryTeaser">
<% Html.RenderPartial("EntryTeaser", item); %>
</li>
<% } %>
</ol>
<% if(ViewData["moreUrl"] != null) { %>
<a href='<%= ViewData["moreUrl"] %>' id="moreLink">More</a>
<% } %>
</div>

Finally I made the link look like a button with some CSS.

#moreLink {
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
border: 1px solid #666666;
background:url('/styles/gfx/more-bg.gif')repeat-x;
width: 100%;
display: block;
text-align: center;
padding: 0.4em 0 0.4em 0;
font-weight: bold;
color:#9aa57c; 
}
#moreLink:hover {
background:url('/styles/gfx/more-bg.gif') 0 -64px; repeat-x;
border: 1px solid #888888;
text-decoration: none;
}

As you might have noticed I used CSS to round the buttons corners. This will only work for some browsers. In this particular case I though that that was OK, but in many other situations I would instead have used images or javascript. You might also have noticed that the button has the same background image when it’s hovered over as when it isn’t. The background image is however offset vertically so it appears that it’s actually another image. I did this to keep the number of HTTP requests required to load the page to a minimum.

Spicing things up with AJAX

With the controller and views set up as described above I was done with the non-AJAX functionality. This will work fine for visitors that doesn’t have javascript enabled or debugging purposes, but this kind of paging is pretty pointless if the page has to reload. After all the point is that when someone clicks the more button the experience shouldn’t be that another page is displayed but that the list, more or less instantly, just grows a bit.

To add the AJAX functionality I begun by modifying the controllers Index method.

public ActionResult Index(int? entryCount)
{
if (!entryCount.HasValue)
entryCount = defaultEntryCount;
int totalItems;
if(Request.IsAjaxRequest())
{
int page = entryCount.Value / defaultEntryCount;
//Retrieve the page specified by the page variable with a page size o defaultEntryCount
IEnumerable<Entry> pagedEntries = GetLatestEntries(page,
 defaultEntryCount, out totalItems);
if(entryCount < totalItems)
AddMoreUrlToViewData(entryCount.Value);
return View("EntryTeaserList", pagedEntries);
}
//Retrieve the first page with a page size of entryCount
IEnumerable<Entry> entries = GetLatestEntries(1, 
entryCount.Value, outtotalItems);
if (entryCount < totalItems)
AddMoreUrlToViewData(entryCount.Value);
return View(entries);
}

The added code checks if the current request is an AJAX request, with the IsAjaxRequest extension method that ships with MVC. IsAjaxRequest determines if the current request is an AJAX request by looking for and at the X-Requested-With request header. If such an header, or actually any request parameter with that name, is set to “XMLHttpRequest” the method will return true. As jQuery’s AJAX methods sets that header this method works great in this example.

Anyway, if the current request is an AJAX request we know that the visitors browser already displays a number of blog entry teasers and instead of returning the full number of entries specified by the entryCount parameter we should only return those that haven’t yet been sent to the visitors browser. So, we calculate what page (as if we where using traditional paging) is requested by dividing entryCount with the defaultEntryCount constant. Then we retrieve a list of the entries on that page with a page size of defaultEntryCount. That is we retrieve the defaultEntryCount number of entries with an offset of page*defaultEntryCount.

Finally, if there are more entries we set the moreUrl in the ViewData dictionary by calling the AddMoreUrlToViewData method and return a ViewResult. This time around however we don’t return the default view for the method. Instead we return the EntryTeaserList partial view. This way we don’t return more HTML than necessary but we are able to reuse an already existing view. We could of course have returned the result as JSON or XML instead but that would have forced us to write javascript for rendering the markup to display the result and thereby duplicating the same markup in two places.

The last thing I did was to add a few lines of javascript to intercept clicks on the more link.

$(function() {
addMoreLinkBehaviour();
});
function addMoreLinkBehaviour() {
$('#entryTeaserList #moreLink').live("click", function() {
$(this).html("<img src='/images/ajax-loader.gif' />");
$.get($(this).attr("href"),function(response) {
$('#entryTeaserList ol').append($("ol", response).html());
$('#entryTeaserList #moreLink').replaceWith($("#moreLink", response));
});
return false;
});
}

When the DOM is ready we add a function to the click event of the more link, and, since I’m using the live function, to any future objects matching that selector. When the link is clicked two things initially happen. First the link’s text is replaced with an image to give the visitor some visual feedback if the response of the AJAX request isn’t instantly returned. Then an AJAX request is made to the same URL as the link had in it’s href attribute. That is, there’s no special URL for the AJAX request. This works as the controller takes care of determining what type of request it is.

When the server has responded with the partial view, that is an ordered list and possibly a new more link the list items are appended to the existing ordered list and the more link is replaced with the new more link if it exists. This way the more link is automatically updated with a new URL in it’s href attribute and the loading image is replaced with the original text.

Conclusion

I personally find this solution pretty elegant. It requires quite few lines of code and almost no duplicate logic or markup at all. It also offers full fallback functionality for visitors without javascript. However, if I was really interested in offering the best possible experience to human visitors with javascript disabled I could also give each blog entry teaser an id with it’s number in the list and include a hash tag with entryCount + 1 – defaultEntryCount in the more link’s target URL so that they would automatically be scrolled to the first entry that was added to the list. In my case I deemed that to be overkill though.

PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.

Advertisements

S3Captcha For ASP.net MVC


Small tip with dynamic type view in ASP.MVC 2


ASP.NET MVC 2 allows you use dynamic type view by using System.Web.Mvc.ViewPage<dynamic> (or someone call this case is anonymous type view). When you use dynamic type view, the Model property is a dynamic-object which is resolved at the runtime.

But, you will meet something wrong when you use dynamic type view. Example, Display is a dynamic type view in my ASP.NET MVC 2 Web Application. And in my action, I do something like this

Code Snippet
  1. return View("Display",
  2.     new
  3.     {
  4.         Mode = "Error",
  5.         Message = "This Student ID has already registered by someone."
  6.     });

I return Display view and pass an anonymous object as a model to Display view. And this is my Display view

Code Snippet
  1. <div>
  2.     <%: Model.Mode %>
  3.     <%: Model.Message %>
  4. </div>

Everything looks good. But, when you run this application and navigate to Display view, you will get RuntimeBinderException exception with message {‘object’ does not contain definition for ‘Mode’}. And it’s more crazy when you switch to debug mode and watch Model property, there are existing Mode and Message properties! So, what’s wrong here?

The answer is Anonymous type is internal, it means its properties, methods can’t be access from outside its assembly. (You can read more explaination at http://www.heartysoft.com/post/2010/05/26/anonymous-types-c-sharp-4-dynamic.aspx)

The solution for my case is very simple like this

Code Snippet
  1. <div>
  2.     <%: Model.GetType().GetProperty("Mode").GetValue(Model, null) %>
  3.     <%: Model.GetType().GetProperty("Message").GetValue(Model, null)%>
  4. </div>

And everything working well.

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.

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.

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.