Removing errors from ModelState in MVC

I wouldn’t normally try to override MVC’s own validation on textboxes etc but occasionally it can be useful. All the things I have seen so far on the Web are pretty messy – turning the ModelState into a dictionary and then iterating through it – and then just removing all the errors associated with that screen control regardless of what the error is. Not very subtle and a bit hacky.

The code below shows a way of doing this in the (slightly contrived but simple) situation where you have a view model with a strongly typed non-nullable DateTime tied to a textbox. This will of course throw an error automatically when trying to submit a blank in the textbox – which is great most of the time. However in a particular context you might want to allow it. Instead of making the DateTime nullable in the view model, and then manually testing for null you could also check for the error in the ModelState and then remove it. Here is how:

public ActionResult Save(MyViewModel model, string btnSave, string btnCancel)
{
       ModelState val;
       if (ModelState.TryGetValue("MyDate", out val) == true)
       {
              if (val.Value.AttemptedValue == "") // Test for the string you are going to allow here              
              {
                        ModelState.Remove(new KeyValuePair<string, ModelState>("MyDate", val));
                        model.MyDate = null; // Set your property to whatever your override is here
              }
       }

      .....
}
 

Obviously the example is a little bit contrived, but the point is that you can use the AttemptedValue property to check for specific entered values – which is useful if you only want to check for a particular value. Also – the TryGetValue method is very useful too, no converting to dictionaries and iterating needed!

Generating PDFs and returning them from a controller method in MVC

The PDFSharp library for .Net is an excellent and relatively easy way of generating PDFs, but how to return them from a controller method in MVC?

The answer is pretty straightforward – use the File() helper, code example returning after a post action shown below.

Generate the PDF in a service class that implements the below interface, using PDFSharp, and return the PDF as a MemoryStream (it’s easy to create the stream using PDFSharp). Note that the stream has to be closed before returning.


public interface IMyPDFService
{
        MemoryStream GeneratePDF();
}

Within controller:

private IMyPDFService _pdfService;

[HttpPost]
public ActionResult ReturnPDF(MyViewModel thisView)
{
       MemoryStream reportStream = _pdfService.GeneratePDF();
       return File( reportStream, "application/pdf") ;
            
}

This was just a brief post, as I didn’t have much time. Instructions on how to create PDFs using PDFSharp are available at their website, but I will add in a brief example function if anyone is interested.

CAS with MVC and Sharp Architecture

To integrate the central authentication system into an MVC/Sharp Architecture website do the following:

Download the dotnet-client-1.0-Bin.zip file from the following link, unzip and put the dll and related files in your bin directory. Follow the instructions on how to configure your web.config:

https://wiki.jasig.org/display/CASC/.Net+Cas+Client

3 points to note here:
(a) the instructions say that the position of the casClientConfig section in the web.config is unimportant. It isn’t, it needs to go after the configSections section.
(b) I found that I needed to set the ticketValidatorName attribute in the casClientConfig to Cas10, as we are still using CAS 1.0.
(c) The path attribute in the authentication section should not have a trailing slash, or CAS seems to get discombobulated if you try to go to your home page. This might be more to do with the configuration of my particular site though, as I am using a virtual directory.

If you are using a Sharp Architecture project then you can now just decorate the controller methods you want to protect with the Authorize attribute, i.e.:

[HttpGet]
[Authorize]
public ActionResult MyMethod()
{
      ...
}

You can also decorate the class with Authorize to protect all the methods in that controller.

You will probably need a log out controller method if you want to provide the user with a link to log out. This is probably best in a separate controller but it’s up to you! This is the method:

public class SecurityController: Controller
{
       public ActionResult LogOff()
       {
            CasAuthentication.SingleSignOut();
            return RedirectToAction("Index", "Home");
        }
}

For those who aren’t using Sharp Architecture projects you can follow this link for how to set up a standard MVC project for CAS: https://wiki.jasig.org/pages/viewpage.action?pageId=32210981

Handling a browser back-button press with MVC

If you need MVC to reload a view via a controller method, even when the browser’s back button has been pressed, then you need to tell the browser to disable caching of that page. This can be done using the OutputCache attribute on the controller method that displays the view.

[HttpGet]
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")] 
public ActionResult MyView()
{
  ...
}

This method will then be called every time the browser displays that view. If you want to pass information to this page (that wasn’t in the original URL), then the ‘next’ view needs to put that data in a Session variable so that it is available when the user presses the back button.

Note that there are also jQuery plugins that can read your browser history but they all seem to have Internet Explorer compatibility issues.

Unit testing MVC Controllers with Model Validation

If you use ModelState.isValid in your controller classes then you will run into problems during unit testing as it will always return true as Model Validation occurs during the Request pipeline, which isn’t the route followed during testing

To overcome I’ve implemented a static controller extension “CallWithModelValidation” as seen below:

public static class ControllerExtensions
    {
        //Enforeces model validation during testing. This is required because model binding doesn't occur in 
        //during testing which means that ModelState.IsValid is always true
        //Taken from
        //http://blog.overridethis.com/blog/post/2010/07/08/MVC2-Validation-and-Testing-e28093-Refactored.aspx
        public static ActionResult CallWithModelValidation<C, R, T>(this C controller
            , Func<C, R> action
            , T model)
            where C : Controller
            where R : ActionResult
            where T : class{
            DataAnnotationsModelValidatorProvider provider = new DataAnnotationsModelValidatorProvider();

            IEnumerable<ModelMetadata> metadata = ModelMetadataProviders
                .Current
                .GetMetadataForProperties(model, typeof(T));
            foreach (ModelMetadata modelMetadata in metadata) 
            {
                IEnumerable<ModelValidator> validators = provider
                    .GetValidators(modelMetadata, new ControllerContext());
                
                foreach (ModelValidator validator in validators) 
                {
                    IEnumerable<ModelValidationResult> results = validator.Validate(model);
                    foreach (ModelValidationResult result in results)
                        controller.ModelState.AddModelError(modelMetadata.PropertyName, result.Message);
                }
            }
            return action(controller);
        }
    }

This can then be called in your controller test by doing the following (where in the case below “AddSupplier” is the controller method under test):

_testController.CallWithModelValidation(m => m.AddSupplier(viewModel), viewModel).AssertViewRendered().ForView("CreateSupplier");