Including External JavaScript and Stylesheets in a .NET Page


I’ve seen several ASP.NET developers architect their web apps so that the only way they can include page-specific CSS or JavaScript is via the code-behind file. That is a bad method because you generally want to keep as much display logic in the aspx as possible. It is actually very easy to structure your master page to allow page-specific CSS/JavaScript. You’ll kick yourself when you see this:

The Master Page

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title><asp:ContentPlaceHolder ID="Title" runat="server" /></title>
        <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
        <link rel="Stylesheet" type="text/css" href="/layout.css" />
        <asp:ContentPlaceHolder ID="Head" runat="server" />
    </head>
    <body>
      ...
    </body>
</html>

The Page

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" MasterPageFile="~/Views/Shared/Site.Master" %>

<asp:Content ContentPlaceHolderID="Title" runat="server">
    This is my page!
</asp:Content>

<asp:Content ContentPlaceHolderID="Head" runat="server">
    <link rel="Stylesheet" type="text/css" href="/css/home.css" />
    <script type="text/javascript" src="/scripts/home.js"></script>
</asp:Content>

Yep…it’s that easy!

ASP.NET MVC Ajax Redirect


I’ve had several occasions where I have needed to make an Ajax request to a secure action on the server (i.e. an action that requires the user to be logged in). The problem is…sometimes the user’s session will timeout between the time they access the secure page and the time they make the secure Ajax request. My action notices this and redirects the user to the login page…but if its an Ajax request then a simple 302 redirect just won’t work. Here’s a method that will.

public class MyBaseController : System.Web.Mvc.Controller
{
    protected override RedirectResult Redirect(string url)
    {
        return new AjaxAwareRedirectResult(url);
    }
}

public class AjaxAwareRedirectResult : RedirectResult
{
    public AjaxAwareRedirectResult(string url)
        : base(url)
    {
    }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context.RequestContext.HttpContext.Request.IsAjaxRequest())
        {
            string destinationUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext);

            JavaScriptResult result = new JavaScriptResult()
            {
                Script = "window.location='" + destinationUrl + "';"
            };
            result.ExecuteResult(context);
        }
        else
            base.ExecuteResult(context);
    }
}

Now you can use the controller’s Redirect() function as usual…and it will automatically detect if it needs to perform a 302 redirect or an Ajax redirect.

Cheers!

ASP.NET MVC Render Partial View to String


I have run into a situation where I would like to render a partial view to a string and then return it as part of a JSON response like so:

return Json(new {
    statusCode = 1,
    statusMessage = "The person has been added!",
    personHtml = PartialView("Person", person)
});

The ability to do something like this would open up a ton of amazing possibilities, so I really scoured the internet looking for a solution. Unfortunately, no one seems to have come up with a clean solution for it, so I dug into the MVC code and came up one…and because I’m such a nice guy, you get to copy it for free. 😉

public abstract class MyBaseController : Controller {

    protected string RenderPartialViewToString()
    {
        return RenderPartialViewToString(null, null);
    }

    protected string RenderPartialViewToString(string viewName)
    {
        return RenderPartialViewToString(viewName, null);
    }

    protected string RenderPartialViewToString(object model)
    {
        return RenderPartialViewToString(null, model);
    }

    protected string RenderPartialViewToString(string viewName, object model)
    {
        if (string.IsNullOrEmpty(viewName))
            viewName = ControllerContext.RouteData.GetRequiredString("action");

        ViewData.Model = model;

        using (StringWriter sw = new StringWriter()) {
            ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
            ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
            viewResult.View.Render(viewContext, sw);

            return sw.GetStringBuilder().ToString();
        }
    }
}

Now you can simply do this:

public class MyController : MyBaseController {

    public ActionResult CreatePerson(Person p) {
        if (ModelState.IsValid) {
            try {
                PersonRepository.Create(p);
                return Json(new {
                    statusCode = 1,
                    statusMessage = "The person has been added!",
                    personHtml = RenderPartialViewToString("Person", p)
                });
            }
            catch (Exception ex) {
                return Json(new {
                    statusCode = 0,
                    statusMessage = "Error: " + ex.Message
                });
            }
        }
        else
            return Json(new {
                statusCode = 0,
                statusMessage = "Invalid data!"
            });
    }
}

Also note that you can modify these functions to render a View (rather than a PartialView) with this small change:

ViewEngineResult viewResult = ViewEngines.Engines.FindView(ControllerContext, viewName);

Enjoy!