Moving to ASP.NET MVC Core 1

Introduction

The new version of ASP.NET MVC, Core 1, brought a number of changes. Some were additions, but others were removals.The structure is similar and will be mostly familiar to anyone who has worked with ASP.NET MVC, but a few stuff is very different. Let’s visit all of them, in a lightweight way – more posts coming soon.

Unified API

MVC and Web API have been merged together, which is a good thing. The new API is based on OWIN. There is a single Controller base class and an IActionResult serves as the base class for the responses, if we so want it – we can also return POCO objects.

Application Events

Application_Start and Application_End events are now gone, as is IRegisteredObject interface and HostingEnvironment.RegisterObject. If you want to be notified (and possibly cancel) hosting event, get a handle to the IApplicationLifetime instance using the built-in service provider and hook to the ApplicationStarted, ApplicationStopping or ApplicationStopped properties.

Dependency Injection

The dependency resolution APIs of both MVC and Web API has been dropped in favor of the new .NET Core mechanism, which is usually configured in the Startup class. You can plug your own IoC/DI framework as well. I wrote a post on this, which you can find here. There are now several new services that you can use to query information from the executing context, experienced users will perhaps find it complex. I’d say this is matter for a full post soon.

Routing

Attribute routing comes out of the box, no need to explicitly configure it. There is no global routing table (RouteTable.Routes), so we need to configure it in the Startup class, in the UseMvc method:

app.UseMvc(routes =>

{

    routes.MapRoute(

        name: "default",

        template: "{controller=Home}/{action=Index}/{id?}");

});

Route handlers (IRouteHandler) are not directly usable. Remember, route handlers are called when the route is matched. Instead, we need to use an IRoute:

public class MyRouter: IRouter

{

    private readonly IRouteHandler _handler;

 

    public MyRouter(IRouteHandler handler)

    {

        this._handler = handler;

    }

 

    public VirtualPathData GetVirtualPath(VirtualPathContext context)

    {

        return null;

    }

 

    public Task RouteAsync(RouteContext context)

    {

        context.Handler = this._handler.GetRequestHandler(context.HttpContext, context.RouteData);

 

        return Task.FromResult(0);

    }

}

Routes are added in Startup as well:

app.UseMvc(routes =>

{

    var routeBuilder = routes.MapRoute(

        name: "default",

        defaults: new {},

        template: "{controller=Home}/{action=Index}/{id?}");

 

    routeBuilder.DefaultHandler = new MyRouter();

});

There is already an handler so in this example we are overriding it. You better know what you’re doing if you are going to do something like this!

Route constraints (IRouteConstraint) are still available, just configured in a different way. These allow us to define if a route’s parameter should be matched:

services.Configure<RouteOptions>(options => options.ConstraintMap.Add("my", typeof(MyRouteConstraint)));

And then on the route template attribute we use the constraint:

[Route("home/action/{id:myconstraint}")]

Now there are also conventions (IApplicationModelConvention) that we can use:

public class MyConvention : IApplicationModelConvention

{

    public void Apply(ApplicationModel application)

    {

        //...

    }

}

Registration is done in the AddMvc method too:

services.AddMvc(options => options.Conventions.Insert(0, new MyConvention()));

Configuration

The Web.config file is gone, now configuration is done in a totally different way, preferably through JSON. You can either make the IConfigurationRoot instance available through the services collection:

public IConfigurationRoot Configuration { get; set; }

 

public void ConfigureServices(IServiceCollection services)

{

    //build configuration and store it in Configuration

 

    services.AddSingleton(this.Configuration);

 

    //...

}

or you can build a strongly typed wrapper class for the configuration. For example, for this JSON file:

{

    "ConnectionString": "Data Source=.;Integrated Security=SSPI; Initial Catalog=MyDb",

    "Timeout": 300

}

we could have this code:

class MySettings

{

    public string ConnectionString { get; set; }

    public int Timeout { get; set; }

}

 

public class Startup

{

    public IConfigurationRoot Configuration { get; set; }

    

    public void ConfigureServices(IServiceCollection services)

    {

        services.AddOptions();

        services.Configure<AppSettings>(this.Configuration);

 

        //...

    }

}

 

public MyController : Controller

{

    public MyController(IOptions<AppSettings> appSettings)

    {

        //...

    }

}

Public Folder

Now the public folder is not the same as the project folder: by default, there’s a wwwroot folder below the project folder which is where all the “physical” assets will be copied to at runtime.

Virtual Path Providers

Again, virtual path providers are gone, but there is a similar mechanism. You need to get a hold of the IHostingEnvironment instance and use or change its ContentRootFileProvider or WebRootFileProvider properties. There’s a new file provider interface, IFileProvider, that you can leverage to provide your own behavior:

public class MyFileProvider : IFileProvider

{

    public IDirectoryContents GetDirectoryContents(string subpath)

    {

        //...

    }

 

    public IFileInfo GetFileInfo(string subpath)

    {

        //...

    }

 

    public IChangeToken Watch(string filter)

    {

        //...

    }

}

If you do not wish to or cannot implement one of the IDirectoryContents, IFileInfo or IChangeToken, just return null.

The difference between ContentRootFileProvider and WebRootFileProvider is that the first is used for files inside IHostingEnvironment.ContentRootPath and the latter for those inside IHostingEnvironment.WebRootPath.

OWIN

MVC Core is now based on the OWIN standard, which means you can add your own middleware to the pipeline. This replaces both HTTP Modules and HTTP Handlers. OWIN middleware sits on the pipeline, like this:

public class MyMiddleware

{

    private readonly RequestDelegate _next;

 

    public MyMiddleware(RequestDelegate next)

    {

        this._next = next;

    }

 

    public async Task Invoke(HttpContext context)

    {

        await context.Response.WriteAsync("Hello from a middleware class!");

        await this._next.Invoke(context);

    }

}

We register middleware components in the IApplicationBuilder instance:

app.UseMiddleware<MyMiddleware>();

A middleware class is just a simple POCO class that follows two conventions:

  • The constructor receives a RequestDelegate instance
  • It has an Invoke method taking an HttpContext and returning a Task

The constructor can also take any service that is registered in the service provider. Pay attention, the order by which we add add our middleware has importance. Make sure you add yours soon enough to encapsulate whatever logic you wish to wrap.

Logging and Tracing

Logging and tracing is now only supported as part of the new unified logging framework. You can also write your own middleware that wraps the MVC processing and add your own logging logic. You gain access to the ILoggerFactory or ILogger/ILogger<T> instances through the service provider or using Dependency Injection:

public class MyController : Controller

{

    public MyController(ILogger<MyController> logger)

    {

    }

}

Custom Errors

The custom errors setting is also gone. In order to have similar behavior, enable developer exception page in the Startup class.

Controllers and Views

Controllers stay the same with one addition: we can have POCO controllers, that is, controllers that do not inherit from a base class (other than Object, that is). In order to make proper use of them, for example, if we want to access the context, we need to inject some properties into the controller class – the HttpContext.Current property is no more:

public class HomeController// : Controller

{

    //automatically injected

    [ActionBindingContext]

    public ActionBindingContext BindingContext { get; set; }

 

    //automatically injected

    [ViewDataDictionary]

    public ViewDataDictionary ViewData { get; set; }

 

    //automatically injected

    [ActionContext]

    public ActionContext ActionContext { get; set; }

 

    //constructor-set

    public IUrlHelper Url { get; private set; }

 

    public dynamic ViewBag

    {

        get { return new DynamicViewData(() => this.ViewData); }

    }

 

    public HomeController(IServiceProvider serviceProvider)

    {

        this.Url = serviceProvider.GetService(typeof(IUrlHelper)) as IUrlHelper;

    }

}

The serviceProvider instance will come from the ASP.NET MVC Core dependency injection mechanism.

In views, we now only have the Razor engine. We can now inject components into views:

@inject IMyClass MyClass

 

@MyClass.MyMethod()

and also define functions in the markup:

@functions {

    string MyMethod() {

        return "Something";

    }

}

Another new thing is the _ViewImports.cshtml file. Here we can specify stuff that will apply to all views. The following directives are supported:

  • addTagHelper
  • removeTagHelper
  • tagHelperPrefix
  • using
  • model
  • inherits
  • inject

Layouts files stay the same.

On the other hand, mobile views no longer exist. Of course, you can add logic to find if the current browser is mobile and then serve an appropriate view. Again, the Browser and IsMobileDevice properties are now gone (with the browser capabilities database), so you will have to do your own sniffing.

You can still add view engines (currently, only Razor is supported), you do that when you register MVC services (no more ViewEngines.Engines property):

services

    .AddMvc()

    .AddViewOptions(x =>

        {

            x.ViewEngines.Add(new MyViewEngine());

        });

Model validation providers used to be extensible through the IClientValidatable interface. Now, we have IClientModelValidatorProvider, and we need to add our providers to a collection also when we register MVC services:

services

    .AddMvc()

    .AddViewOptions(x =>

        {

            x.ClientModelValidatorProviders.Add(new MyClientModelValidationProvider());

        });

A client model validation provider needs to implement IClientModelValidatorProvider:

public class MyClientModelValidationProvider : IClientModelValidatorProvider

{

    public void CreateValidators(ClientValidatorProviderContext context)

    {

        //...

    }

}

Finally, views now cannot be precompiled. In the early days of ASP.NET MVC Core, it was possible to precompile them, but not anymore.

Filters

There is no longer a static property for holding the global filers (GlobalFilters.Filters), instead, they can be added to the services collection, normally through the AddMvc overload that takes a lambda:

services.AddMvc(mvc =>

{

    mvc.Filters.Add(typeof(MyActionFilter));

});

Of course, it is still possible to scope filters at the class or method level, using either an attribute inheriting from a *FilterAttribute class (like ActionFilterAttribute) or using TypeFilterAttribute, for dependency injection.

View Components and Tag Helpers

These are new in Core 1. I wrote about View Components here and on Tag Helpers here, so I’m not delving into it again. Two very welcome additions indeed!

Bundling

In the old days, you would normally use the Microsoft ASP.NET Web Optimization package to bundle and minify your JavaScript and CSS files. Now, by default, Gulp is used for that purpose. You can also use BundleMinifier.Core from Mads Kristensen, this needs to be added as a tool and configured through a bundleconfig.json file. BundleMinifier is installed by default starting with Visual Studio 2015 Tooling Preview 1.

Maintaining State

In pre-Core days, one could store state in the Application, which would be available to all requests. Unfortunately, this led to several problems, and the application storage was dropped. Of course, it is still possible to use static members and classes.

Likewise, the Cache storage was also dropped, this time in favor of a more flexible and extensible mechanism. You’ll need the Microsoft.Extensions.Caching.Abstractions Nuget package for the base contracts plus a specific implementation (see Memory or Redis, for example):

public void ConfigureServices(IServiceCollection services)

{

    services.AddDistributedMemoryCache();

 

    //...

}

 

public class MyController : Controller

{

    public MyController(IDistributedCache cache)

    {

        //...

    }

}

Sessions are still around, but they need to be explicitly configured. You need to add a reference to the Microsoft.AspNetCore.Session package, and then register the services and middleware (a distributed memory cache is also required):

public void ConfigureServices(IServiceCollection services)

{

    services.AddDistributedMemoryCache();

    services.AddSession();

}

 

public void Configure(IApplicationBuilder app)

{

    app.UseSession();

    app.UseMvc();

}

 

After this, the ISession instance can be accessed through the HttpContext instance exposed, for example, by the Controller class.

There is no longer support for automatically storing the session in a SQL Server database or the ASP.NET State Service, but it is possible to use Redis, a very popular distributed cache technology.

Publishing

Publish profiles are still around, but now you have other options, such as deploy to Docker. There’s also the dotnet publish command, which places all deployable artifacts in a folder, ready to be copied to the server.

Conclusion

You can see that even though this is still MVC, a lot has changed. In my next post, I will talk a bit about some of the new interfaces that were introduced. In the meantime, hope this gets you up and running! Do let me know if I skipped something or you wish to know more about this. Your feedback is always greatly appreciated!

Payloads as dynamic Objects in ASP.NET MVC

Even though the dynamic type seems to be everywhere these days, ASP.NET MVC still doesn’t support having dynamic parameters in controller action methods out of the box; which is to say, this doesn’t work as expected:

[HttpPost]

public ActionResult Post(dynamic payload)

{

    //payload will be an object without any properties

 

    return this.View();

}

However, because MVC is so extensible, it is very easy to achieve it. For that, we need to build a custom model binder and apply it to our action method parameter. We’ll assume that the content will come as JSON from the HTTP POST payload. Note that this does not happen with Web API, but still happens with MVC Core!

There are a couple of ways by which we can bind a model binder to a parameter:

First, let’s focus on the actual model binder, the core for any of the above solutions; we need to implement the IModelBinder interface, which isn’t really that hard to do:

public sealed class DynamicModelBinder : IModelBinder

{

    private const string ContentType = "application/json";

 

    public DynamicModelBinder(bool useModelName = false)

    {

        this.UseModelName = useModelName;

    }

 

    public bool UseModelName { get; private set; }

 

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)

    {

        dynamic data = null;

 

        if ((controllerContext.HttpContext.Request.AcceptTypes.Any(x => x.StartsWith(ContentType, StringComparison.OrdinalIgnoreCase) == true) &&

            (controllerContext.HttpContext.Request.ContentType.StartsWith(ContentType, StringComparison.OrdinalIgnoreCase) == true)))

        {

            controllerContext.HttpContext.Request.InputStream.Position = 0;

 

            using (var reader = new StreamReader(controllerContext.HttpContext.Request.InputStream))

            {

                var payload = reader.ReadToEnd();

 

                if (string.IsNullOrWhiteSpace(payload) == false)

                {

                    data = JsonConvert.DeserializeObject(payload);

 

                    if (this.UseModelName == true)

                    {

                        data = data[bindingContext.ModelName];

                    }

                }

            }

        }

 

        return data;

    }

}

Nothing fancy here; it will check to see if both the Accept and the Content-Type HTTP headers are present and set to application/json, the official MIME type for JSON, before parsing the posted content. If any content is present, JSON.NET will parse it into it’s own object. The UseModelName property is used to bind to a specific property of the payload, for example, say you are binding to a parameter called firstName, and you want it populated with the contents of the firstName field in the payload. In our case, we don’t need it, we want the whole thing, so it is set to false.

Now, the way I recommend for applying this model binder is through a custom attribute:

[Serializable]

[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]

public sealed class DynamicModelBinderAttribute : CustomModelBinderAttribute

{

    public DynamicModelBinderAttribute(bool useModelName = false)

    {

        this.UseModelName = useModelName;

    }

 

    public bool UseModelName { get; private set; }

 

    public override IModelBinder GetBinder()

    {

        return new DynamicModelBinder(this.UseModelName);

    }

}

It goes like this:

[HttpPost]

public ActionResult Post([DynamicModelBinder] dynamic payload)

{

    //payload will be populated with the contents of the HTTP POST payload

 

    return this.View();

}

Or, if you want to do it for several dynamic parameters, just set a global model binder provider for type Object; this is safe, because we would never have a parameter of type Object:

public sealed class DynamicModelBinderProvider : IModelBinderProvider

{

    public static readonly IModelBinderProvider Instance = new DynamicModelBinderProvider();

 

    public IModelBinder GetBinder(Type modelType)

    {

        if (modelType == typeof (object))

        {

            return new DynamicModelBinder();

        }

 

        return null;

    }

}

And we register is as this:

ModelBinderProviders.BinderProviders.Insert(0, DynamicModelBinderProvider.Instance);

And that’s it. This is one of what I consider to be ASP.NET MVC flaws, and they will deserve another post, soon. Web API already solves this problem, but it is still there in the future version of MVC Core, and can be solved in the same way.

Encrypted JavaScript in ASP.NET MVC Core

To complete the Data URI saga, here is another technique that you may find interesting: this time, it’s about encrypting JavaScript contents, so as to make them more difficult to tamper with.

Data URIs can be used for serving any content that would normally come from an external resource, such as a JavaScript/CSS file or an image. What we will do here is, using ASP.NET MVC Core’s Tag Helpers, we get hold of the JavaScript that is defined inside a SCRIPT tag and we turn into a Data URI. Pretty simple, yet the result is also pretty cool!

Show me the code, I hear you say:

[HtmlTargetElement("script")]

[HtmlTargetElement("script", Attributes = "asp-encrypt")]

[HtmlTargetElement("script", Attributes = "asp-src-include")]

[HtmlTargetElement("script", Attributes = "asp-src-exclude")]

[HtmlTargetElement("script", Attributes = "asp-fallback-src")]

[HtmlTargetElement("script", Attributes = "asp-fallback-src-include")]

[HtmlTargetElement("script", Attributes = "asp-fallback-src-exclude")]

[HtmlTargetElement("script", Attributes = "asp-fallback-test")]

[HtmlTargetElement("script", Attributes = "asp-append-version")]

public class InlineScriptTagHelper : ScriptTagHelper

{

    public InlineScriptTagHelper(ILogger<ScriptTagHelper> logger, IHostingEnvironment hostingEnvironment,

        IMemoryCache cache, IHtmlEncoder htmlEncoder, IJavaScriptStringEncoder javaScriptEncoder,

        IUrlHelper urlHelper) : base(logger, hostingEnvironment, cache, htmlEncoder, javaScriptEncoder, urlHelper)

    {

    }

 

    [HtmlAttributeName("asp-encrypt")]

    public bool Encrypt { get; set; }

 

    public override void Process(TagHelperContext context, TagHelperOutput output)

    {

        if ((this.Encrypt == true) && (string.IsNullOrWhiteSpace(this.Src) == true))

        {

            var content = output.GetChildContentAsync().GetAwaiter().GetResult().GetContent();

            var encryptedContent = Convert.ToBase64String(Encoding.ASCII.GetBytes(content));

            var script = $"data:text/javascript;base64,{encryptedContent}";

 

            output.Attributes.Add("src", script);

            output.Content.Clear();

        }

 

        base.Process(context, output);

    }

}

You can see that this tag helper inherits from ScriptTagHelper, which is the OOTB class that handles SCRIPT tags. Because of this inheritance, we need to add all the HtmlTargetElementAttributes, so that all the rules get applied properly. For this one, we need to add an extra rule, which forces the SCRIPT tag to have an asp-encrypt attribute.

So, say you have this in your Razor view:

<script asp-encrypt="true">
   1:  

   2:     window.alert('hello, world, from an encrypted script!');

   3:  

</script>

What you’ll end up with in the rendered page is this:

<script src="data:text/javascript;base64,DQoNCiAgICB3aW5kb3cuYWxlcnQoJ2hlbGxvLCB3b3JsZCwgZnJvbSBhbiBlbmNyeXB0ZWQgc2NyaXB0IScpOw0KDQo="></script>

Cool, wouldn’t you say? Of course, no JavaScript that runs on the client can ever be 100% secure, and you should always keep that in mind. But for some purposes, it does pretty well! Winking smile

Inline Images in ASP.NET MVC Core

I have blogged extensively about Data URIs in the past. It allows us to render external contents inside of the page’s HTML, avoiding additional HTTP requests, but enlarging the HTML to serve. Sometimes, it does make sense, especially because the whole page can be made cacheable.

MVC does not offer any mechanism for serving images as inline Data URIs, and that is the reason for this post! Winking smile

For this example, I added an extension method to the IHtmlHelper class, InlineImage:

public static class HtmlHelperExtensions

{

    private static string GetFileContentType(string path)

    {

        if (path.EndsWith(".JPG", StringComparison.OrdinalIgnoreCase) == true)

        {

            return "image/jpeg";

        }

        else if (path.EndsWith(".GIF", StringComparison.OrdinalIgnoreCase) == true)

        {

            return "image/gif";

        }

        else if (path.EndsWith(".PNG", StringComparison.OrdinalIgnoreCase) == true)

        {

            return "image/png";

        }

 

        throw new ArgumentException("Unknown file type");

    }

 

    public static HtmlString InlineImage(this IHtmlHelper html, string path, object attributes = null)

    {

        var contentType = GetFileContentType(path);

        var env = html.ViewContext.HttpContext.ApplicationServices.GetService(typeof (IHostingEnvironment)) as IHostingEnvironment;

 

        using (var stream = env.WebRootFileProvider.GetFileInfo(path).CreateReadStream())

        {

            var array = new byte[stream.Length];

            stream.Read(array, 0, array.Length);

 

            var base64 = Convert.ToBase64String(array);

 

            var props = (attributes != null) ? attributes.GetType().GetProperties().ToDictionary(x => x.Name, x => x.GetValue(attributes)) : null;

 

            var attrs = (props == null)

                ? string.Empty

                : string.Join(" ", props.Select(x => string.Format("{0}=\"{1}\"", x.Key, x.Value)));

 

            var img = $"<img src=\"data:{contentType};base64,{base64}\" {attrs}/>";

 

            return new HtmlString(img);

        }

    }

}

You can see that I only support three image formats: JPEG, GIF and PNG. These are the safe formats that can be rendered by all browsers. The format is inferred from the file name’s extension.

The InlineImage method takes a local file name that will be considered from the site’s root, and an optional collection of attributes. If present, these attributes are added to the generated IMG tag as-is.

To use this extension, all we need is to add this code in a Razor view:

@Html.InlineImage("image.jpg", new { width = "200", height = "200" });

And the output should look like this (trimmed):

<img src="data:image/jpeg;base64,SGVsbG8sIFdvcmxkIQ%3D%3D...=" width="200" height="200" />

This code works as is in ASP.NET MVC Core, but can be easily changed to work in MVC 5 or prior: just use Server.MapPath to get the physical address of the file to load.

ASP.NET 5 View Components

One of my favorite features in ASP.NET 5 / MVC 6 is View Components. Like I said of Tag Helpers, I think they are what MVC was missing in terms of reusable components.

View Components are similar to partial views, except that they don’t have a visual design surface; all of its contents must be produced by .NET code. Only “tangible” content can be returned, no redirects, or any other contents that require HTTP status codes.

A View Component needs to either inherit from ViewComponent (no API documentation yet) or be a POCO class decorated with the ViewComponentAttribute. If it inherits from ViewComponent, it gets a lot of useful infrastructure methods, otherwise, it is possible to inject some context, such as a ViewContext reference.

A View Component looks like this:

public class SumViewComponent : ViewComponent

{

    public IViewComponentResult Invoke(int a, int b)

    {

        return this.Content($"<span class=\"result\">{a + b}</span>");

    }

}

As you can see, a View Component can take parameters, any number, for that matter, and, by convention, its entry point must be called Invoke, return an instance of IViewComponentResult, and here is where its parameters are declared.

A View Component is rendered like this:

@Component.Invoke("Sum", 1, 2);    <!-- <span class="result">3</span> –>

As you can see, the argument to Invoke must be the View Component’s class name, without the ViewComponent suffix. We can override this and set our own name, by applying the ViewComponentAttribute:

[ViewComponent(Name = "Sum")]

public class ArithmeticOperation

{

    public IViewComponentResult Invoke(int a, int b)

    {

        return new ContentViewComponentResult($"<span class=\"result\">{a + b}</span>");

    }

}

Notice that this uses a POCO class that does not inherit from ViewComponent. However, the Razor syntax will be exactly the same, because of the Name passed in the ViewComponentAttribute attribute. It basically only needs to be public, concrete and non-generic.

It is also possible to use an asynchronous version:

[ViewComponent(Name = "Sum")]

public class ArithmeticOperation

{

    public async Task<IViewComponentResult> InvokeAsync(int a, int b)

    {

        return new ContentViewComponentResult($"<span class=\"result\">{a + b}</span>");

    }

}

In this case, the Razor syntax must be instead:

@await Component.InvokeAsync("Sum", 1, 2);    <!-- <span class="result">3</span> –>

Because the ViewComponent class exposes some properties that give access to the execution context, namely, ViewContext, it is also possible to have it in the POCO version, by decorating a ViewContext property with ViewContextAttribute:

[ViewContext]

public ViewComponentContext ViewContext { get; set; }

The ViewComponentContext class gives direct access to the arguments, the ViewData and ViewBag collections, the FormContext and a couple of other useful context properties, so it is generally good to have it near by. It is even possible to access the view’s model:

var v = this.ViewContext.View as RazorView;

dynamic page = v.RazorPage;

var model = page.Model;

View Components can benefit of dependency injection. Just add the types that you wish to inject to the constructor, works with both the POCO and the ViewComponent version:

public class SumViewComponent : ViewComponent

{

    public SumViewComponent(ILoggerFactory loggerFactory)

    {

        //do something with loggerFactory

    }

}

One last remark: view components are automatically loaded from the executing assembly, but it is possible to load them from other assemblies, we just need to get an handle to the web application’s IViewComponentDescriptorCollectionProvider. From it, we can get to the ViewComponents collection and add our own:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)

{

    var vcdcp = app.ApplicationServices.GetService<IViewComponentDescriptorCollectionProvider>();

    var list = vcdcp.ViewComponents.Items as IList<ViewComponentDescriptor>;

 

    var viewComponentType = typeof(MyViewComponent);

 

    var attr = viewComponentType.GetTypeInfo().GetCustomAttributes<ViewComponentAttribute>(true).SingleOrDefault();

    var fullName = attr?.Name ?? viewComponentType.DisplayName(true);

 

    list.Add(new ViewComponentDescriptor

    {

        FullName = fullName,

        ShortName = viewComponentType.DisplayName(false),

        Type = viewComponentType

    });

 

    //rest goes here

}

This makes it very easy to register View Components that are declared in different assemblies.

Have fun! Winking smile

Using ASP.NET 5 Tag Helpers and the Bing Translator API

Update: In the latest version of ASP.NET 5, the GetChildContextAsync, that was previously available in the TagHelperContext class, is now in TagHelperOutput.

Introduction

If you’ve been following my posts you probably know that I have been a critic of MVC because of its poor support of reuse. Until now, the two main mechanisms for reuse – partial views and helper extensions – had some problems:

  • Partial views cannot be reused across projects/assemblies;
  • Helper methods cannot be easily extended.

Fortunately, ASP.NET 5 offers (good) solutions for these problems, in the form of Tag Helpers and View Components! This time, I’ll focus on tag helpers.

Tag Helpers

A tag helper behaves somewhat like a server-side control in ASP.NET Web Forms, without the event lifecycle. It sits on a view and can take parameters from it, resulting on the generation of HTML (of course, can have other side effects as well).

Tag helper can either be declared as new tags, ones that do not exist in HTML, such as <animation>, <drop-panel>, etc, or can intercept any tag, matching some conditions:

  • Having some pre-defined tag;
  • Being declared inside a specific tag;
  • Having some attributes defined;
  • Having a well-known structure, like, self-closing or without ending tag.

A number of these conditions can be specified, for example:

  • All IMG tags;
  • All A tags having an attribute of action-name;
  • All SPAN tags having both translate and from-language attributes;
  • All COMPONENT tags declared inside a COMPONENTS tag.

ASP.NET 5 includes a number of them:

  • AnchorTagHelper: generates links to action methods in controllers;
  • CacheTagHelper: caches its content for a number of seconds;
  • EnvironmentTagHelper: renders its content conditionally, depending on the current execution environment;
  • FormTagHelper: posts to an action method of a controller;
  • ImageTagHelper: adds a suffix to an image URL, to control caching of the file;
  • InputTagHelper
  • LabelTagHelper
  • LinkTagHelper
  • OptionTagHelper
  • ScriptTagHelper
  • SelectTagHelper
  • TextAreaTagHelper
  • ValidationMessageTagHelper: outputs model validation messages;
  • ValidationSummaryTagHelper: outputs a validation summary.

In order to use tag helpers, we need to add a reference to the Microsoft.AspNet.Mvc.TagHelpers Nuget package in Project.json (at the time this post was written, the last version was 6.0.0-beta8):

"dependencies": {

    ...

    "Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-beta8"

}

Enough talk, let’s see an example of the TagHelper class with some HtmlTargetElement attributes (no API documentation available yet):

[HtmlTargetElement("p", Attributes = "translate, to-language")]

[HtmlTargetElement("span", Attributes = "translate, to-language")]

public sealed class TranslateTagHelper : TagHelper

{

}

This tag helper will intercept the following tag declarations, inside a view:

<p translate="true" to-language="pt">This is some text meant for translation</p>

<span translate="true" to-language="pt">Same here</span>

Tag helpers need to be declared in a view before they can be used. They can come from any assembly:

@addTagHelper "*, MyNamespace"

Properties declared on the markup will be automatically mapped to properties on the TagHelper class. Special names, such as those having , will require an HtmlAttributeName attribute:

[HtmlAttributeName("to-language")]

public string ToLanguage { get; set; }

If, on the other hand, we do not want such a mapping, all we have to do is apply an HtmlAttributeNotBound attribute:

[HtmlAttributeNotBound]

public string SomeParameter { get; set; }

The TagHelper class defines two overridable methods:

  • Process: where the logic is declared, executes synchronously;
  • ProcessAsync: same as above, but executes asynchronously.

We only need to override one. In it, we have access to the passed attributes, the content declared inside the tag and its execution context:

public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)

{

    var content = await context.GetChildContentAsync();

    var text = content.GetContent();


    //do something with content


    content.TagName = "DIV";

    content = output.Content.Clear();

    content = output.Content.Append("HTML content");

}

You can see that I am redefining the output tag, in this case, to DIV, and, also, I am clearing all content and replacing it with my own.

Now, let’s see a full example!

Content Translation

Microsoft makes available for developers the Bing Translator API. It allows, at no cost (for a reasonable number of requests) to perform translations of text through a REST interface (several API bindings exist that encapsulate it). Let’s implement a tag helper that will automatically translate any text contained inside of it.

In order to use this API, you first need to register at the Microsoft Azure Marketplace. Then, we need to create a Marketplace application. After we do that, we need to generate an authorization key. There are several tools that can do this, in my case, I use Postmap, a Google Chrome extension that allows the crafting and execution of REST requests.

The request for generating an authorization key goes like this:

POST https://datamarket.accesscontrol.windows.net/v2/OAuth2-13

grant_type: client_credentials

client_id: <client id>

client_secret: <client secret>

scope: http://api.microsofttranslator.com

The parameters <client id> and <client secret> are obtained from our registered applications’ page and grant_type, client_id, client_secret and scope are request headers. The response should look like this:

{
“token_type”: “
http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0″,
“access_token”: “<access token>“,
“expires_in”: “600”,
“scope”: “
http://api.microsofttranslator.com”

}

Here, what interests us is the <access token> value. This is what we’ll use to authorize a translation request. The value for expires_in is also important, because it contains the amount of time the access token is valid.

A translation request should look like this:

GET http://api.microsofttranslator.com/V2/Ajax.svc/Translate?to=<target language>&text=<text to translate>&from=<source language>

Authorization: Bearer <access token>

The <target language> and <text> parameters are mandatory, but the <source language> one isn’t; if it isn’t supplied, Bing Translator will do its best to infer the language of the <text>. Authorization should be sent as a request header.

Let’s define an interface for representing translation services:

public interface ITranslationService

{

    Task<string> TranslateAsync(string text, string to, string @from = null);

}

Based on what I said, a Bing Translator implementation could look like this:

public sealed class BingTranslationService : ITranslationService

{

    public const string Url = "http://api.microsofttranslator.com/V2/Ajax.svc/Translate?text={0}&to={1}&from={2}";


    public string AuthKey { get; set; }


    public async Task<string> TranslateAsync(string text, string to, string @from = null)

    {

        using (var client = new HttpClient())

        {

            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", this.AuthKey);


            var result = await client.GetStringAsync(new Uri(string.Format(Url, WebUtility.UrlEncode(text), to, from)));


            return result;

        }

    }

}

Let’s register this service in ConfigureServices:

services.AddSingleton<ITranslationService>((sp) => new BingTranslationService { AuthKey = "<access token>" });

Of course, do replace <access token> by a proper one, otherwise your calls will always fail.

Caching

In order to make our solution more scalable, we will cache the translated results, this way, we avoid unnecessary – and costly – network calls. ASP.NET 5 offers two caching contracts, IMemoryCache and IDistributedCache. For the sake of simplicity, let’s focus on IMemoryCache, On the ConfigureServices method of the Startup class let’s add the caching services:

services.AddCaching();

And now let’s see it all together.

Putting it All Together

The final tag helper looks like this:

[HtmlTargetElement("p", Attributes = "translate, to-language")]

[HtmlTargetElement("span", Attributes = "translate, to-language")]

public sealed class TranslateTagHelper : TagHelper

{

    private readonly ITranslationService svc;

    private readonly IMemoryCache cache;


    public TranslateTagHelper(ITranslationService svc, IMemoryCache cache)

    {

        if (svc == null)

        {

            throw new ArgumentNullException(nameof(svc));

        }


        this.svc = svc;

        this.cache = cache;

    }


    [HtmlAttributeName("from-language")]

    public string FromLanguage { get; set; }


    [HtmlAttributeName("to-language")]

    public string ToLanguage { get; set; }


    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)

    {

        if (string.IsNullOrWhiteSpace(this.ToLanguage) == true)

        {

            throw new InvalidOperationException("Missing target language.");

        }


        var content = await context.GetChildContentAsync();

        var text = content.GetContent();


        if (string.IsNullOrWhiteSpace(text) == true)

        {

            return;

        }


        var cacheKey = $"{this.FromLanguage}:{text.ToLowerInvariant().Trim()}:{this.ToLanguage}";

        var response = string.Empty;


        this.cache?.TryGetValue(cacheKey, out response);


        if (string.IsNullOrWhiteSpace(response) == true)

        {

            response = await svc.TranslateAsync(text, this.ToLanguage, this.FromLanguage);

            response = response.Trim('"');


            cache?.Set(cacheKey, response);

        }


        content = output.Content.Clear();

        content = output.Content.Append(response);

    }

}

It tries to obtain the translated context from the cache – if it is registered – otherwise, it will issue a translation request. If it succeeds, it stores the translation in the cache. The cache key is a combination of the text (in lowercase), the source and destination languages. The only strictly required service is an implementation of our ITranslationService, the cache is optional. In order use the tag in a view, all we need is:

@addTagHelper "*, MyNamespace"


<p translate="yes" to-language="pt">This is some translatable content</p>

Conclusion

I hope I managed to convince you of the power of tag helpers. They are a powerful mechanism and a welcome addition to ASP.NET MVC. Looking forward to hearing what you can do with them!

Case Study: Comparing ASP.NET Web Forms and MVC Implementations

Introduction

Apparently, I am the last ASP.NET Web Forms developer in the whole World, which makes me kind of sad… anyway, after much crying, I decided to try out something: a comparison of Web Forms and MVC to solve a real life problem! Now, I only needed a problem to solve… Then, a colleague of mine came questioning me about captchas, so I thought, why not use it as my example? And here we are!

Everyone is familiar with captchas. They are a way to prevent the automated use of web forms, so as to make sure that it is indeed an human that is providing the information. There are lots of implementations out there, and I don’t claim that mine is better or worse, I just implemented it in a couple of hours as an exercise.

Core Library

I designed a couple of classes, framework-agnostic, to hold my abstractions and sample implementations. These are:

Common

A succinct description is in order.

CaptchaSecretPersister

The contract for storing a secret key. Defines two methods, Store and Retrieve.

SessionCaptchaSecretPersister

An implementation of CaptchaSecretPersister that stores the secret key in the ASP.NET session.

CaptchaImageTransformer

The contract for transforming an image. Only has a single method, Transform.

NullCaptchaImageTransformer

Inherits from CaptchaImageTransformer, but doesn’t do anything.

CaptchaSecretGenerator

The contract for generating a new secret key (GetSecret).

RandomCaptchaSecretGenerator

An implementation of CaptchaSecretGenerator that generates a series of random letters.

CaptchaImageProducer

Base class for producing an image with a given secret key, dimensions, colors and an optional image transformer. Only has a single method, GetImage.

GdiCaptchaImageProducer

An implementation of CaptchaImageProducer that uses GDI+.

CaptchaSecretValidator

Base contract for secret key validation. Only defines a method, Validate.

CaseInsensitiveCaptchaSecretValidator

A case-insensitive string comparison implementation of CaptchaSecretValidator.

And here is their code:

public abstract class CaptchaSecretPersister

{

    public abstract void Store(String secret);

 

    public abstract String Retrieve();

}

 

public sealed class SessionCaptchaSecretPersister : CaptchaSecretPersister

{

    public static readonly CaptchaSecretPersister Instance = new SessionCaptchaSecretPersister();

 

    internal const String Key = "Captcha";

 

    public override void Store(String secret)

    {

        HttpContext.Current.Session[Key] = secret;

    }

 

    public override String Retrieve()

    {

        return HttpContext.Current.Session[Key] as String;

    }

}

 

public abstract class CaptchaImageTransformer

{

    public abstract void Transform(Graphics g);

}

 

public sealed class NullCaptchaImageTransformer : CaptchaImageTransformer

{

    public static readonly CaptchaImageTransformer Instance = new NullCaptchaImageTransformer();

 

    public override void Transform(Graphics g)

    {

        //do nothing

    }

}

 

public abstract class CaptchaSecretGenerator

{

    public abstract String GetSecret(UInt32 length);

}

 

public sealed class RandomCaptchaSecretGenerator : CaptchaSecretGenerator

{

    public static readonly CaptchaSecretGenerator Instance = new RandomCaptchaSecretGenerator();

 

    public override String GetSecret(UInt32 length)

    {

        var builder = new StringBuilder();

        var rand = new Random();

 

        for (var i = 0; i < length; i++)

        {

            var ch = (Char)('A' + rand.Next(26));

            builder.Append(ch);

        }

 

        return builder.ToString();

    }

}

 

public abstract class CaptchaImageProducer

{

    public abstract Image GetImage(Int32 width, Int32 height, String secret, Color foreColor, Color backColor, CaptchaImageTransformer transformer);

}

 

public sealed class GdiCaptchaImageProducer : CaptchaImageProducer

{

    public static readonly CaptchaImageProducer Instance = new GdiCaptchaImageProducer();

 

    public override Image GetImage(Int32 width, Int32 height, String secret, Color foreColor, Color backColor, CaptchaImageTransformer transformer)

    {

        var img = new Bitmap(width, height, PixelFormat.Format32bppArgb);

 

        using (var graphics = Graphics.FromImage(img))

        using (var font = new Font(FontFamily.GenericSansSerif, 10F))

        using (var color = new SolidBrush(foreColor))

        {

            graphics.TextRenderingHint = TextRenderingHint.AntiAlias;

            graphics.Clear(backColor);

            graphics.DrawString(secret, font, color, 0F, 0F);

 

            if (transformer != null)

            {

                transformer.Transform(graphics);

            }

 

            return img;

        }

    }

}

 

public abstract class CaptchaSecretValidator

{

    public abstract Boolean Validate(String storedSecret, String secret);

}

 

public sealed class CaseInsensitiveCaptchaSecretValidator : CaptchaSecretValidator

{

    public static readonly CaptchaSecretValidator Instance = new CaseInsensitiveCaptchaSecretValidator();

 

    public override Boolean Validate(String storedSecret, String secret)

    {

        return String.Equals(storedSecret, secret, StringComparison.OrdinalIgnoreCase);

    }

}

Noteworthy:

  • Base classes are always abstract;
  • Actual implementations are sealed, stateless, and therefore define a static read only field, to avoid multiple instantiations.

Both implementations, Web Forms and MVC, will use these classes.

Web Forms

So let’s start playing. Web Forms has the concept of validators. A validator must implement interface IValidator, and a BaseValidator class exists to make the task easier. When a form is submitted by a control that triggers validation, such as Button, all registered validators (Page.Validators) of the same validation group (ValidationGroup) as the trigger control are fired (Validate is called). The page will be considered valid if all validators have their IsValid property set to true. Knowing this, I created a custom control to display the captcha image and perform its validation:

public sealed class CaptchaImage : WebControl, IValidator

{

    public CaptchaImage() : base(HtmlTextWriterTag.Img)

    {

        this.CaptchaSecretGenerator = RandomCaptchaSecretGenerator.Instance;

        this.CaptchaImageTransformer = NullCaptchaImageTransformer.Instance;

        this.CaptchaImageProducer = GdiCaptchaImageProducer.Instance;

        this.CaptchaSecretPersister = SessionCaptchaSecretPersister.Instance;

        this.CaptchaSecretValidator = CaseInsensitiveCaptchaSecretValidator.Instance;

 

        this.CaptchaLength = 4;

        this.CaptchaFormat = ImageFormat.Png;

 

        this.ForeColor = Color.Black;

        this.BackColor = Color.Transparent;

        this.AlternateText = String.Empty;

        this.ControlToSetError = String.Empty;

        this.ControlToValidate = String.Empty;

 

        (this as IValidator).IsValid = true;

    }

 

    public CaptchaSecretPersister CaptchaSecretPersister { get; set; }

 

    public CaptchaSecretGenerator CaptchaSecretGenerator { get; set; }

 

    public CaptchaImageTransformer CaptchaImageTransformer { get; set; }

 

    public CaptchaImageProducer CaptchaImageProducer { get; set; }

 

    public CaptchaSecretValidator CaptchaSecretValidator { get; set; }

 

    [DefaultValue("")]

    public String AlternateText { get; set; }

 

    [DefaultValue(4)]

    public UInt32 CaptchaLength { get; set; }

 

    [DefaultValue(typeof(ImageFormat), "Png")]

    public ImageFormat CaptchaFormat { get; set; }

 

    [DefaultValue("")]

    [IDReferenceProperty]

    [TypeConverter(typeof(ControlIDConverter))]

    public String ControlToValidate { get; set; }

 

    [DefaultValue("")]

    [IDReferenceProperty]

    [TypeConverter(typeof(ControlIDConverter))]

    public String ControlToSetError { get; set; }

 

    [DefaultValue("")]

    public String ErrorMessage { get; set; }

 

 

    public event EventHandler ValidationSuccess;

    public event EventHandler ValidationFailure;

    public event EventHandler ValidationComplete;

 

    private void OnValidationSuccess()

    {

        var handler = this.ValidationSuccess;

 

        if (handler != null)

        {

            handler(this, EventArgs.Empty);

        }

    }

 

    private void OnValidationComplete()

    {

        var handler = this.ValidationComplete;

 

        if (handler != null)

        {

            handler(this, EventArgs.Empty);

        }

    }

 

    private void OnValidationFailure()

    {

        var handler = this.ValidationFailure;

 

        if (handler != null)

        {

            handler(this, EventArgs.Empty);

        }

    }

    

    private ITextControl FindTextControl(String id)

    {

        return this.NamingContainer.FindControl(id) as ITextControl;

    }

 

    protected override void OnInit(EventArgs e)

    {

        if (this.Enabled == true)

        {

            this.Page.Validators.Add(this);

        }

 

        base.OnInit(e);

    }

 

    protected override void Render(HtmlTextWriter writer)

    {

        var secret = this.CaptchaSecretGenerator.GetSecret(this.CaptchaLength);

 

        this.CaptchaPersister.Store(secret);

 

        using (var img = this.CaptchaImageProducer.GetImage((Int32)this.Width.Value, (Int32)this.Height.Value, secret, this.ForeColor, this.BackColor, this.CaptchaImageTransformer))

        using (var stream = new MemoryStream())

        {

            img.Save(stream, this.CaptchaFormat);

 

            this.Attributes[HtmlTextWriterAttribute.Src.ToString().ToLower()] = String.Format("data:image/{0};base64,{1}", this.CaptchaFormat.ToString().ToLower(), Convert.ToBase64String(stream.ToArray()));

            this.Attributes[HtmlTextWriterAttribute.Alt.ToString().ToLower()] = this.AlternateText;

        }

 

        base.Render(writer);

 

        var val = this as IValidator;

 

        if (val.IsValid == false)

        {

            var errorControl = this.FindTextControl(this.ControlToSetError);

 

            if (errorControl != null)

            {

                errorControl.Text = this.ErrorMessage;

            }

            else

            {

                writer.Write(this.ErrorMessage);

            }

        }

    }

 

    Boolean IValidator.IsValid { get; set; }

 

    void IValidator.Validate()

    {

        var val = this as IValidator;

        val.IsValid = true;

 

        var secretControl = this.FindTextControl(this.ControlToValidate);

        

        if (secretControl != null)

        {

            var storedSecret = this.CaptchaSecretPersister.Retrieve();

 

            val.IsValid = this.CaptchaSecretValidator.Validate(storedSecret, secretControl.Text);

 

            if (val.IsValid == true)

            {

                this.OnValidationSuccess();

            }

            else

            {

                this.OnValidationFailure();

            }

 

            this.OnValidationComplete();

        }

    }

}

The CaptchaImage class inherits from WebControl, so that we can leverage some of its properties (ForeColor, BackColor), and defines a containing tag of IMG.

In its constructor, all relevant properties are instantiated to sensible defaults, these include a number of properties for the core library classes (CaptchaSecretPersister, CaptchaSecretGenerator, CaptchaImageProducer, CaptchaImageTransformer and CaptchaSecretValidator). Other important properties are:

  • ErrorMessage: the message to display in case of a validation error;
  • AlternateText: the image’s ALT text;
  • CaptchaLength: the desired length of the secret key; the default is 4;
  • CaptchaFormat: the image format of choice, where the default is ImageFormat.Png;
  • ControlToValidate: the required ID of a server-side control containing text (must implement ITextControl);
  • ControlToSetError: an optional ID for a control that can take text (must implement ITextControl too), which will be used to set the ErrorMessage value.

The IsValid property, from IValidator, is implemented privately because most of the time we do not want to mess with it. Another inherited property, Enabled, can be used to turn off validation.

Then we have three events:

  • ValidationSuccess: raised when the validation occurs and is successful;
  • ValidationFailure: raised when a failed validation occurs;
  • ValidationComplete: always raised when a validation is performed, regardless of its outcome.

When the control loads, OnInit is called, and it registers itself with the Validators collection, this is a required step. In Render, it generates a secret key, stores it, produces an image from it and renders it as an inline image using the Data URI format. It is the same approach that I talked about a number of times before. When the control receives a validation request, the Validate method is called, and the provided key is validated against the stored one.

A typical usage would be like this:

<web:CaptchaImage runat="server" ID="captcha" Width="50px" Height="25px" BackColor="Blue" AlternateText="Captcha riddle" ControlToValidate="text" ErrorMessage="Invalid captcha" OnValidationSuccess="OnSuccess" OnValidationFailure="OnFailure" OnValidationComplete="OnComplete" />

Whereas the registered event handlers might be:

protected void OnSuccess(Object sender, EventArgs e)

{

    this.captcha.Visible = false;

}

 

protected void OnFailure(Object sender, EventArgs e)

{

}

 

protected void OnComplete(Object sender, EventArgs e)

{

}

Easy, don’t you think? Now, let’s move on to MVC!

MVC

In MVC we don’t really have controls, because of the separation of concerns and all that. The closest we have at the moment are extension methods, let’s have a look at the Captcha method:

public static IHtmlString Captcha(this HtmlHelper html, Int32 width, Int32 height, UInt32 length = 4, String alternateText = "", ImageFormat captchaFormat = null, Color? foreColor = null, Color? backColor = null)

{

    var captchaSecretGenerator = DependencyResolver.Current.GetService<CaptchaSecretGenerator>();

    var captchaSecretPersister = DependencyResolver.Current.GetService<CaptchaSecretPersister>();

    var captchaImageProducer = DependencyResolver.Current.GetService<CaptchaImageProducer>();

    var captchaImageTransformer = DependencyResolver.Current.GetService<CaptchaImageTransformer>();

 

    var secret = captchaSecretGenerator.GetSecret(length);

 

    captchaSecretPersister.Store(secret);

 

    using (var img = captchaImageProducer.GetImage(width, height, secret, foreColor ?? Color.Black, backColor ?? Color.Transparent, captchaImageTransformer))

    using (var stream = new MemoryStream())

    {

        img.Save(stream, captchaFormat ?? ImageFormat.Png);

 

        var tag = String.Format("<img src=\"data:image/{0};base64,{1}\" width=\"{1}\" height=\"{2}\" alt=\"{3}\"/>", (captchaFormat ?? ImageFormat.Png).ToString().ToLower(), Convert.ToBase64String(stream.ToArray()), width, height, alternateText);

 

        return new HtmlString(tag);

    }

}

It is unpractical to pass all components, so we are relying on MVC’s built-in dependency injection framework. I used Microsoft Unity as my Inversion of Control (IoC) container, and to make it integrate with MVC, I installed the Unity.Mvc NuGet package:

image

Then I had to register the actual implementations for my services, this is usually done in the App_Start\UnityConfig.cs file’s RegisterTypes method:

container.RegisterInstance<CaptchaSecretGenerator>(RandomCaptchaSecretGenerator.Instance);

container.RegisterInstance<CaptchaImageProducer>(GdiCaptchaImageProducer.Instance);

container.RegisterInstance<CaptchaSecretPersister>(SessionCaptchaSecretPersister.Instance);

container.RegisterInstance<CaptchaImageTransformer>(NullCaptchaImageTransformer.Instance);

container.RegisterInstance<CaptchaSecretValidator>(CaseInsensitiveCaptchaSecretValidator.Instance);

The code from Unity.Mvc automatically hooks Unity with the DependencyResolver class, so we don’t have to do it ourselves. For an example implementation, I once wrote a post on it that you can check out, if you are curious.

Now, we need two things: a view and a controller. Let’s look at the view first (simplified):

@Html.Captcha(50, 25)

@using (Html.BeginForm("Validate", "Home"))

{

    @Html.TextBox("secret")

    <button>Validate</button>

}

As you can see, the form will post to the Validate method of an HomeController. The output of the Captcha method doesn’t have to be inside the form, because it merely renders an IMG tag.

As for the controller, the Validate method is pretty straightforward:

public ActionResult Validate(String secret)

{

    var captchaPersister = DependencyResolver.Current.GetService<CaptchaSecretPersister>();

    var captchaSecretValidator = DependencyResolver.Current.GetService<CaptchaSecretValidator>();

 

    var storedSecret = captchaPersister.Retrieve();

    var isValid = captchaSecretValidator.Validate(storedSecret, secret);

 

    if (isValid == true)

    {

        return this.View("Success");

    }

    else

    {

        return this.View("Failure");

    }

}

It tries to obtain the services from the DependencyResolver, validates the supplied secret key and then returns the proper view, accordingly.

Conclusion

So, which implementation do you prefer? The Web Forms one, unsurprisingly, only needs a single class, CaptchaImage, and, of course, a host page; the assembly containing it can be referenced by other projects and used very easily. As for the MVC version, we need to have the Captcha extension method, the IoC bootstrapping/registration code, a view and a controller with a Validate method similar to the one presented. The extension method and the controller may come from another referenced assembly, it needs some work, but can definitely be done.

Of course, this is a sample implementation, things can be done in a myriad of alternative ways. I’d like to hear from you about the problems with this particular implementation, and alternative ways to do it. By the way, feel free to use and modify the code anyway you want to, I will soon make it available in my GitHub account.

Generating GDI+ Images for ASP.NET MVC Views

This post is about applying the same technique I presented for ASP.NET Web Forms, but this time for MVC.

We need to have an ActionResult that does the actual work of converting whatever we draw in the Graphics context into an image with an inline Data URI. Here’s a possible solution, where you only specify the dimensions of the image to be generated:

public sealed class InlineImageResult : ActionResult

{

    private readonly Image bmp;

 

    public InlineImageResult(Int32 width, Int32 height)

    {

        //the PixelFormat argument is required if we want to have transparency

        this.bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);

 

        this.Graphics = Graphics.FromImage(this.bmp);

        //for higher quality

        this.Graphics.CompositingQuality = CompositingQuality.HighQuality;

        this.Graphics.SmoothingMode = SmoothingMode.HighQuality;

        this.Graphics.InterpolationMode = InterpolationMode.High;

        //make the background transparent

        this.Graphics.Clear(Color.Transparent);

    }

 

    public Graphics Graphics { get; private set; }

 

    public override void ExecuteResult(ControllerContext context)

    {

        using (this.bmp)

        using (this.Graphics)

        using (var stream = new MemoryStream())

        {

            //PNG because of transparency

            var format = ImageFormat.Png;

 

            this.bmp.Save(stream, format);

 

            var img = String.Format("<img src=\"data:image/{0};base64,{1}\"/>", format.ToString().ToLower(), Convert.ToBase64String(stream.ToArray()));

 

            context.HttpContext.Response.Write(img);

        }

    }

}

Now, we need to create an instance of this result and draw in it, in a controller’s action method:

[ChildActionOnly]

public ActionResult Image()

{

    var result = new InlineImageResult(200, 50);

    result.Graphics.DrawString("Hello, World!", new Font("Verdana", 20, FontStyle.Regular, GraphicsUnit.Pixel), new SolidBrush(Color.Blue), 0, 0);

 

    return result;

}

Notice the ChildActionOnly attribute: it tells MVC that this action method is only meant to be used in child action (RenderAction) call, not as a stand-alone action, like this:

<p>

    @{ Html.RenderAction("Image"); }

</p>

As you can see, this provides a very convenient way to generate images on the fly. You just need to learn about the drawing methods in GDI+.