ASP.NET MVC: Uploading a File

In a project I’m working on, I needed to implement a file upload in ASP.NET MVC. There are basically two ways to implement it:

  1. Handle the upload of the file in the view itself.
  2. Handle it in the controller.

The code for both of them is pretty similar.

First you need to have a file input in your view:

@Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, new { enctype = "multipart/form-data"})
{
	<input type="file" name="uploadedFile" value="Browse">
	<input id="uploadbutton" type="submit" value="Upload">	
}

If you want to handle it all in the view itself, you can also use null as action and controller name and add the following at the beginning of your view:

@{
    if (IsPost)
    {
        HttpPostedFileBase uploadedFile = Request.Files[0];
        if (uploadedFile != null)
        {
            string fileName = Path.GetFileName(uploadedFile.FileName);
            if (!string.IsNullOrWhiteSpace(fileName))
            {
                string directory = AppDomain.CurrentDomain.BaseDirectory + @"App_Data\UploadedFiles";
                string fileSavePath = directory + "\\" + fileName;
                Directory.CreateDirectory(directory);
                uploadedFile.SaveAs(fileSavePath);
            }
        }
    }
}

In case you want to handle it in the controller, you should provide the right controller name and action name e.g. Index:

@Html.BeginForm("Index", "File", FormMethod.Post, new { enctype = "multipart/form-data"})

Then in your controller create an Index method which will handle the file upload:

public class FileController : Controller
{
    // GET
    public ActionResult Index()
    {
        return View();
    }

    // POST
    [HttpPost]
    public ActionResult Index(HttpPostedFileBase uploadedFile)
    {
        if (uploadedFile != null)
        {
            string fileName = Path.GetFileName(uploadedFile.FileName);
            if (!string.IsNullOrWhiteSpace(fileName))
            {
                string directory = AppDomain.CurrentDomain.BaseDirectory + @"App_Data\UploadedFiles";
                string fileSavePath = directory + "\\" + fileName;
                Directory.CreateDirectory(directory);
                uploadedFile.SaveAs(fileSavePath);
            }
        }
        return RedirectToAction("Index");        
    }
}

That’s it ! If you want to handle multiple file uploads e.g. to allow a user to upload a resume, a motivation letter, a degree… it’s damned easy.

In case you went for the solution in the view, you just have to iterate through Request.Files.

If you went for the solution in the controller, just change the signature of your action to take an enumerable instead of a single HttpPostedFileBase:

public ActionResult Index(IEnumerable<HttpPostedFileBase> uploadedFile)

Then iterate through the enumeration and save each file.

Orchard: new installation leads to an HTTP 500 – Internal Server Error

Since I got a new computer (or rather an old one repaired), I first installed IIS switched the DefaultAppPool to .NET Framework Version 4.0 and deployed Orchard from my solution.

When I then open Orchard in my browser, all I got is a white page. Looking at the IIS log files shows that an internal server error:

2014-02-07 10:26:48 127.0.0.1 GET /orchard – 80 – 127.0.0.1 Mozilla/5.0+(Windows+NT+6.1;+WOW64;+rv:27.0)+Gecko/20100101+Firefox/27.0 500 19 33 1

This unfortunately didn’t help much. Then I remember that I already once had a similar and had to use the ASP.NET IIS Registration Tool to install the version of ASP.NET and register ASP.NET in IIS by executing the following in a DOS prompt:

cd C:\Windows\Microsoft.NET\Framework64\v4.0.30319
aspnet_regiis.exe -i

After doing this and setting the rights for the DefaultAppPool user again so that Orchard can write to the App_Data directory, my Orchard Installation was working fine.

It’s just a pity that there is not a clear error message written to the log file in such cases…

ASP.NET MVC: The required anti-forgery form field “__RequestVerificationToken” is not present

When working on an ASP.NET MVC project, you might get the following error message:

The required anti-forgery form field “__RequestVerificationToken” is not present

As the message says, this means that you’re missing the anti-forgery verification token. This token is used to prevent cross-site request forgery (CSRF) attacks. A CSRF attack is similar to a cross-site scripting (XSS) exploit but the other way around. In an XSS exploit, someone is using the fact that a user trusts a site and in a CSRF attack someone is using the fact is trusting a given user e.g. commands are sent to the server pretending to come from the user.

A very simple example of such an attack is to have a trusted user load a web page from a third party server and include a call to the target server e.g. in an image tag e.g.:

<img src="http://targetserver/importantfunction">

If the target server relies only on cookies for security, the user might have a still valid cookie and the server will not notice that it is actually an attack.

Of course this won’t work if you only accept POST requests but you could implement something like this using ajax.

To prevent this, the server has to implement an own protection mechanism instead of relying on the browser. One way to do it is to include in all forms a special random token linked to a cookie or the user session. When a call is received, if the form data do not contain this token (usually some random GUID), the request is rejected.

On the other hand, the token is present on the page so it’s not really secret. But because of the same-origin policy the attacker cannot read the token from the page (Cross-origin writes are allowed but cross-origin reads are typically not allowed).

In ASP.NET MVC you can define a method as requiring such a token by defining the ValidateAntiForgeryToken attribute e.g.:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult MyAction(string id) {
}

Note that when working with Orchard CMS even though your controller doesn’t define this attribute, the anti-forgery validation will happen.

When you use a form, you can handle the anti-forgery token by using the AntiForgeryToken() method of the Html helper e.g.:

@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    <input ...>
}

This will add an invisible field with the name __RequestVerificationToken in the form. This token will then be submitted with the form and checked on the server before processing the form.

Now I use a lot of ajax calls to the server using jquery e.g.:

$.ajax({
	type: "POST",
	url: '@Url.Action("MyMethod", "MyController", new {area = "MyArea"})',
	dataType: "json",
	traditional: true,
	data: { "id": "12345678" };
}).done(function(result) {
	if (result) {
		// Do something
	} else {
		// Log or show an error message
	}
	return false;
});

This call will fail with the error message in the title of this post. What you need to do is get the token from a form on the page and send it along the other call parameters. To fetch the value of the token, you can use jQuery as well:

$('input[name=__RequestVerificationToken]').val()

This will find the input field for the token (in a form on the page) and fetch the value. You then need to append it to the data sent to the server:

$.ajax({
	type: "POST",
	url: '@Url.Action("MyMethod", "MyController", new {area = "MyArea"})',
	dataType: "json",
	traditional: true,
	data: { "id": "12345678", "__RequestVerificationToken": $('input[name=__RequestVerificationToken]').val() };
}).done(function(result) {
	if (result) {
		// Do something
	} else {
		// Log or show an error message
	}
	return false;
});

The error is gone and the call is processed successfully. Now if you have many ajax calls on a page, it’s kind of a pain to add this to all calls. So you should rather create a helper function which will add the token to the data sent to the server:

function addRequestVerificationToken(data) {
	data.__RequestVerificationToken = $('input[name=__RequestVerificationToken]').val();
	return data;
};

You can then use it like this:

function addRequestVerificationToken(data) {
	data.__RequestVerificationToken = $('input[name=__RequestVerificationToken]').val();
	return data;
};
$.ajax({
	type: "POST",
	url: '@Url.Action("MyMethod", "MyController", new {area = "MyArea"})',
	dataType: "json",
	traditional: true,
	data: addRequestVerificationToken( { "id": "12345678" } );
}).done(function(result) {
	if (result) {
		// Do something
	} else {
		// Log or show an error message
	}
	return false;
});

If you use Orchard CMS you will see that the parts in an editor template are displayed within one form so you do not need to add a form. But when viewing content items, you usually do not require a form. So in this case you’ll have to add a form without submit, just to get the token:

@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
}

Also if you use Orchard CMS instead of manually fetching the value of the verification token, you could also use the helper provided by Orchard:

function addAntiForgeryToken(data) {
    data.__RequestVerificationToken = "@Html.AntiForgeryTokenValueOrchard()";
    return data;
};