Tag Archives: MVC

Compiling your MVC views without IIS errors

Visual Studio and ASP.NET by default does not compile your MVC views during build, instead the runtime processes them at run time. The consequence of this is that any error in your views may break your app only at run time.

Fortunately you can force Visual Studio to compile your MVC views as part of the build process. Just open your .csproj file and switch the value of the MvcBuildViews element from false to true:

<MvcBuildViews>true</MvcBuildViews>

This fixes the original problem, however it often introduces another issue. You may receive the following error when you try to build or publish your web application:

It is an error to use a section registered as allowDefinition=’MachineToApplication’ beyond application level.  This error can be caused by a virtual directory not being configured as an application in IIS.

I don’t know who made up this message and what was his intention to communicate. This message does not help neither to find the error, nor to fix it.

The solution – which is definitely not obvious from the previous error – is to delete the obj folder before build.

If you do not want to manually delete that folder every time, you can add that step into your build process. Thankfully MSBuild provides the BaseIntermediateOutputPath variable which points right to the obj folder, and you can combine it with the RemoveDir task to get rid of that folder. Just open the .csproj file again and add a new element to the BeforeBuild target:

<Target Name="BeforeBuild">
    <RemoveDir Directories="$(BaseIntermediateOutputPath)" />
</Target>

If you don’t like the bin folder and want to remove that as well, you can refer to it via the BaseOutputPath variable.

 

Technorati-címkék: ,,
Advertisements

Ajax file upload

Sometimes it would be useful to upload a file to a website while the rest of the page remains unchanged, so with Ajax. I’ve got bad news for you: the Level 1 version of the XMLHttpRequest object doesn’t support that, so if you must support older browsers, you need some tricks.

One of the most widely used tricks is using an iframe, however in that case the results from the server (for example the validation errors) are also delivered to the iframe. So if you work with a hidden iframe and you need the result in client-processable way in JavaScript, you have to dig it from the iframe. Fortunately the jQuery Form Plugin can help you to solve these issues.

First, create a view-model on the server, in which a HttpPostedFileBase property represents the uploaded file. I’ve also added a Name property for demonstration purposes:

public class UploadVM
{
    [Required( ErrorMessage = "Please enter a name!" )]
    public string Name { get; set; }

    [Attachment]
    public HttpPostedFileBase Attachment { get; set; }
}

The [Attachment] is the file validation attributed I presented in an earlier post. It checks whether the file is submitted, and also checks the extension and the size of the file.

For this model you can create a form that submits the file and the provided name:

@using( Html.BeginForm( "Index", "Home", FormMethod.Post, 
        new { id = "myForm", enctype = "multipart/form-data" } ) )
{
    <p>
        <label for="txtName">Name:</label>
        <input type="text" id="txtName" name="Name" />
    </p>

    <p>
        <label for="fupAttachment">File:</label>
        <input type="file" id="fupAttachment" name="Attachment" />
    </p>

    <p>
        <input type="submit" value="Upload" />
    </p>    

    <div id="errors"></div>
}

It’s important that the form must have an enctype attribute with multipart/form-data value, because that supports file upload. I also created a div with errors id, which will host the error messages.

This form posts to the Index action of the HomeController which can be implemented like this:

[HttpPost]
public ActionResult Index( UploadVM model )
{
  if( !this.ModelState.IsValid )
  {
    string firstError = ModelState.First( m => m.Value.Errors.Any() )
.Value.Errors[ 0 ].ErrorMessage;
return this.FileUploadFailure( firstError ); } // Process the file here string message = String.Format( "The file '{0}' is successfully uploaded.",
model.Name );
return this.FileUploadSuccess( message ); }

If there is any validation error in the view-model, the runtime adds them to the ModelState thanks to the attributes. Just as always the ModelState is check in the beginning of this method, and if there is any error an error message is returned, if not, then the file is processed and a success message is returned to the client.

The returned value is a JSON object, because that comes really handy on the client. But don’t forget, that it will land in an iframe, and not every browser tolerates an application/json result in iframes. Fortunately the jQuery Form Plugin supports a simple hack: just wrap the result into a <textarea> element and send it back in a text/html response, the plugin will do the rest.

To simplify (and standardize) this wrapping, you can create a custom result type:

public class FileUploadJsonResult : JsonResult
{
  public override void ExecuteResult( ControllerContext context )
  {
    this.ContentType = "text/html";
    context.HttpContext.Response.Write( "<textarea>" );
    base.ExecuteResult( context );
    context.HttpContext.Response.Write( "</textarea>" );
  }
}

You can also create extension methods for the Controller class to create these upload results just like any other result from the action methods:

public static FileUploadJsonResult FileUploadSuccess( 
this Controller controller, string successMessage = null ) { return new FileUploadJsonResult { Data = new { Success = true, Message = successMessage } }; } public static FileUploadJsonResult FileUploadFailure(
this Controller controller, string errorMessage ) { return new FileUploadJsonResult { Data = new { Success = false, Message = errorMessage } }; }

Here you can create a custom object with any structure in the Data property that will be delivered to the client in JSON. The code above just signals the result of the file upload in the Success property, and returns a detailed status message in the Message property that can be displayed by the browser.

So now you have fully functional server side: a form that POSTs to an action which validates the input, and if every field is valid, processes the values and returns a status in a JSON object.

Let’s implement the client side, of course with the jQuery Form Plugin. As its name implies, this is a jQuery plugin that you can apply to the jQuery object which wraps the form element (the $form in this case):

$form.ajaxForm({
    iframe: true,
    dataType: "json",
    beforeSubmit: function () {
// TODO
}, success: function (result) { // TODO
}, error: function () { // TODO
} });

You usually have to implement the following callback functions:

  • beforeSubmit: this is called before the POST is sent to the server. This is where you can display a “please wait” message, a progress indicator, or simply block the form controls using the jQuery BlockUI plugin.
  • success: this is called when the upload is completed successfully. At least in theory. In my experience this callback is also called when the server returns some low-level error. If there was no error, you receive the response from the server in the function parameter in JSON, but be prepared, that in case of error it can be undefined as well!
  • error: this is called when an error occurred during postback.

Here is an example of a working success callback implementation:

if (!result) {
  $errors.html('<div class="validation-summary-errors"><ul><li>Oooops....
</li></ul></div>'
); } else { $form.resetForm(); if (result.Success === true) { var message = result.Message; if (message && message.length > 0) { $errors.html( message ); } } else { $errors.html('<div class="validation-summary-errors"><ul><li>{0}
</li></ul></div>'
.format(result.Message)); } }

In this code the errors are displayed in a complex HTML fragment. The reason for that is that the same HTML fragment is generated by the standard ASP.NET MVC validation helpers, so with this code our Ajax errors will be rendered with the same style.

The format function acts like the String.Format method on the server and can be implemented like this:

String.prototype.format = function () {
  var args = arguments;
  return this.replace(/\{\{|\}\}|\{(\d+)\}/g, function (m, n) {
    if (m === "{{") { return "{"; }
    if (m === "}}") { return "}"; }
    return args[n];
  });
};

If you return all ModelState errors from the server, you can render them in a loop.

You can download the full source code with tons of comment from the following link: http://sdrv.ms/10QQLSp

 

Technorati-címkék: ,

File upload validation in ASP.NET MVC

One of the beauties of ASP.NET MVC is model validation. Just attach the attributes from the System.ComponentModel.DataAnnotations namespace to the properties of your model or view-model, and you magically have a working validation without messing up your code.

This works perfectly if you have a simple property, like a string or a number, but you won’t find too much help for validating uploaded files in this namespace. From .NET 4.5 there is a FileExtensionAttribute, but usually a more thorough check is requested.

What you always have to check:

  • Is any file uploaded?
  • The extension of the uploaded file is valid?
  • The size of the uploaded file falls within the accepted range?

Sometimes you also have to check:

Let’s create your own validation attribute! Just make sure you derive your custom attribute class from the ValidationAttribute base class and apply it to HttpPostedFileBase properties in the model.

This is a working solution:

[AttributeUsage( AttributeTargets.Property )]
public sealed class AttachmentAttribute : ValidationAttribute
{
  protected override ValidationResult IsValid( object value, 
ValidationContext validationContext ) { HttpPostedFileBase file = value as HttpPostedFileBase; // The file is required. if( file == null ) { return new ValidationResult( "Please upload a file!" ); } // The meximum allowed file size is 10MB. if( file.ContentLength > 10 * 1024 * 1024 ) { return new ValidationResult( "This file is too big!" ); } // Only PDF can be uploaded. string ext = Path.GetExtension( file.FileName ); if( String.IsNullOrEmpty( ext ) || !ext.Equals( ".pdf", StringComparison.OrdinalIgnoreCase ) ) { return new ValidationResult( "This file is not a PDF!" ); } // Everything OK. return ValidationResult.Success; } }

Just add the [Attachment] attribute to the appropriate properties of the model, and your validation errors will land in the ModelState.

If your app allows uploading different files on different pages, you can create an enum for the different file types and pass that enum to the constructor of the AttachmentAttribute. This way you can centralize the file validation logic in your website.

What else do you usually check when you validate the uploaded files?

 

Technorati-címkék: ,,

Content Injector for ASP.NET MVC

If you ever created reusable components for ASP.NET MVC, you’ve probably faced with the issue, that a partial view or a HTML helper requires one or more external CSS stylesheets or JavaScript files. If you consider code performance and quality, you always insert your CSS files at the top, and your JS files at the bottom of the page, paying attention to load them exactly once, even if multiple parts of your page require them. However, that’s not easy, because partial views and HTML helpers are rendered autonomously. Although the problem is not new at all, it’s not easy to solve in WebForms, and definitely difficult in MVC.

You may be not surprised that the freshly baked solution is delivered by Peter Blum in the form of the Content Injector for ASP.NET MVC toolkit. Experienced ASP.NET developers probably saw this name several times, because Peter creates reusable ASP.NET WebControls in his one-person company. Among them probably the most well-known is the Peter’s Data Entry Suite (DES), which contains more than 100 webcontrol that simplify input handling and validation. The DES is a very useful set of components, so it’s not a coincidence that it received many positive feedback, excellent rating and professional awards.

So it was not surprising, that a few weeks ago it was Peter, who came up with an idea in the ASPInsiders list, that solves the problem above. Many of us tried and tested the tool, and provided feedback that he quickly applied to the source code, so I can now full heart recommend the Content Injector for ASP.NET MVC toolkit, especially in the NuGet package form.

Using the package is very straightforward. First, you have to mark the places on your Layout.cshtml page, where you want to insert custom content:

@Injector.InjectionPoint("ScriptFiles")

Next, if for example a view requires the jQuery Validate library, then you can insert the corresponding script files directly from the view:

@Injector.ScriptFile("~/Scripts/jquery.validate.min.js"); 
@Injector.ScriptFile("~/Scripts/jquery.validate.unobtrusive.min.js");

Of course this is only the simplest use case, you can specify additional parameters (eg. order), or you can insert stylesheets, scripts, meta tags, hidden fields, script blocks or anything else, thanks to the extensible and configurable (eg. tracing) nature of the product. You can read about all those options in details in the 23-page User’s Guide.

Another interesting thing is that reacting to the first feedback, Peter connected the Content Injector with the Microsoft Web Optimization Framework, so after installing the ContentInjector.WOF NuGet package, you can insert StyleBundle and ScriptBundle as well.

Downloads:

Grab it, try it, and don’t forget to send him feedback or just a quick thank you!

 

Technorati-címkék: ,,

T4MVC: String-free ASP.NET MVC

One of the few not-so-beautiful parts of ASP.NET MVC is that you have to use strings to refer to your controllers, actions and views. If you ever created an MVC project you probably write something like this into an action:

  return View( "Index" );

or this into a view:

  @Html.ActionLink( "Click here to sign in", "Login" )

The pain points are the strings, because the smallest mistake results a runtime error.

The T4MVC project can help you to solve this problem with strongly typed helpers. The best way to include it into your project is to add it via the NuGet Package Manager:

t4mvc-nuget

If you already have controllers in your project, don’t be surprised that the installer will slightly modify them:

t4mvc-warnings

And you will get some extra files as well:

t4mvc-files

But thanks to all that, you can use strongly typed values to refer to views:

  return View( Views.Index );

actions:

  @Html.ActionLink( "Bejelentkezés", MVC.Home.Login() )

any many other components – that you can learn more from the documentation.

Because all magic is done via code generation based on T4 templates in the background, you may wonder what happens when you add a new action. You will see that you can immediately use it in your view, it will appear immediately in IntelliSense, but unfortunately it will fail in runtime with the following error (this is the moment when you can think about whether it is not exactly what you wanted to avoid):

T4MVC was called incorrectly. You may need to force it to regenerate by right clicking on T4MVC.tt and choosing Run Custom Tool

Thanks to the perfect error message, if you follow it, your issue will be solved. However, you may find the AutoT4MVC extension more convenient, which monitors your files and folders and automatically regenerates the files when required.

 

Technorati-címkék: ,

Phil Haack in IT Business

Having a break during the final exams I got my hands on the July 2012 issue of the Hungarian IT Business journal:

it-business-1

I’ve started reading the articles till on page 15, in an article about how to learn driving online, I’ve seen a familiar face (the article is available online too):

it-business-2

ASP.NET old-timers will probably recognize these guys: he is Phil Haack the creator of ASP.NET MVC and NuGet with his brother. You can find the original image here in a very good blog post: http://haacked.com/archive/2008/11/06/usability-vs-discoverability.aspx

IIRC, Phil had a Twitter post about this picture, copyright and websites about learning to drive.

 

Technorati-címkék:

Deleting a result file in ASP.NET MVC

It happens sometimes, that you send back a file to the browser from ASP.NET MVC using FilePathResult with some code like this:

public FileResult GetFile()
{
    // TODO: something important is done here...
    return this.File( filePath, type );
}

If the file is actually a temporary file, how can you delete the unnecessary file from the server only after the file is fully sent to the browser?

You can create a result filter:

public class DeleteFileAttribute : ActionFilterAttribute
{
  public override void OnResultExecuted( ResultExecutedContext filterContext )
  {
    filterContext.HttpContext.Response.Flush();
    string filePath = ( filterContext.Result as FilePathResult ).FileName;
    File.Delete( filePath );
  }
}

Of course this code is simplified, you should add additional checks to validate that the action result is really a FilePathResult, check that the file exists, and finally make sure that your code doesn’t blow up if the file deletion fails.

After you completed your filter, just add the [DeleteFile] attribute to your action and you’re done!

 

Technorati-címkék: ,