Showing posts with label ASP.Net MVC. Show all posts
Showing posts with label ASP.Net MVC. Show all posts

Tuesday, April 28, 2009

Rendering to Xml with ASP.Net MVC

 

Warning: This is my first post using Windows Live Writer so if things don’t come out right I apologize.

I have been working lately on using the ASP.Net MVC platform as a rest based endpoint. My current consumer is looking for xml documents of our model; however, in the future I’d love to move our web site over as well. I have been playing around with a couple of different ways to render xml content from a view.

I have seen some approaches on the web which use an automatic serialization mechanism to convert your model into xml or json. I feel that this should not be done automatically but you should be able to customize the rendering however way you need. This has led me to try out three different options to date.

Option 1 Serializing a DTO

The first approach I tried was to take my DTO and get into Xml the quickest easiest way possible. The XmlSerializer. This broke down a little bit when I discovered I actually needed two DTO’s. This method I used a helper method in a code behind file. So my view looks like:

<%@ Page Language="C#" Inherits="MyViewToXml" CodeBehind="MyViewToXml.aspx.cs"  %> 
<%this.RenderToXml(); %>




Now if we look at RenderToXml we can see the magic here:




Response.ContentType = "text/xml";
Response.Write("<MyRoot>");

var dcs = new DataContractSerializer(typeof(List<DTO1>));
dcs.WriteObject(Response.OutputStream, this.Model);

var dcs2 = new DataContractSerializer(typeof(IList<DTO2>));
IList<DTO2> bfs = (IList<DTO2r>)this.ViewData["DTO2"];

dcs2.WriteObject(Response.OutputStream, bfs);

Response.Write("</MyRoot>");



I like this approach in that it was quick easy, and gave me full control, but it doesn’t sit well with me. It feels like I am grinding against the purpose of having a view. So let’s look at option 2.



Option 2 Embracing MVC View



My next shot was to serialize a much more complex object. In my actual case I don’t have a DTO defined, and I need to have even more control over the Xml being generated. Serialization by the framework won’t cut it, and I wasn’t go back 10 years to the days of having build xml via strings or even via a DOM. So what does that leave me with?




<Order Id="<%=this.Model.Id%>"  Number="<%=this.Model.Number%>">

<Customer Company="<%=this.Model.CompanyName %>">
<Address Line1="<%=this.Model.Address.Line%>"
City="<%=this.Model.Address.City %>"
State="<%=this.Model.Address.State %>"/>
</Customer>

<ShipTo Name="<%=this.Model.Location.Name %>">
<%if (!this.Model.ShipTo.Equals(this.Model.Contact.Address))
{ %>
<Address City="<%=this.Model.ShipTo.City %>"
State="<%=this.Model.ShipTo.State %>" />
<%} %>
</ShipTo>

<Items GrandTotal="<%=this.Model.CalculateGrandTotal()%>"
IsOverride
="<%=this.Model.IsGrandTotalOverridden %>">
<%foreach(var item in this.Model.Items) {%>
<Item Name="<%=item.Name%>" Total="<%=item.Total%>" />
<%} %>
</Items>
<Order>



Here we are embracing the view and generating our xml just as if we were rendering an Xhtml view of the domain. This is neat; however, it is a little verbose so I started to question what else is out there.



Option 3 Finding Alternate View Engines



Asp.Net MVC is very extensible. If you don’t like the view engine go find a new one or write your own. I ran across a port of HAML called NHAML. This can simplify the syntax to look at; however, NHAML’s tooling is not there. If you want intellisense or color highlighting, its just not there.



Further more it treats white space as significant characters. For example to nest elements you would need to do:




%Order
  %Customer
    %Address


Child elements are indented by two spaces. While this frees you from worrying about closing all your tags, its a little counterintuitive. There are other view engines out there, which I am looking forward to running through their paces.

Sunday, March 1, 2009

ASP.net MVC Model Binder

Model Binders is a powerful extension point to the ASP.NET MVC. It allows you to define your own class which is responsible for creating parameters for your controller actions. Scott Hansleman had a good example showing how to decouple a controller from HttpContext's user property.

Recently I've started a new Proof of Concept site which is based upon REST. My goal was to build a single web site which would expose its resources as addressable items. That is the URL defines the resource. This site should also be able to serve multiple clients which want different representations of the same resource.

For example assume we have an Automobile Parts catalog. We might have the browser point to http://myPartsCatalog.com/Ford/OilFilters/ which would render an HTML page of all the ford oil filters. Now let us say we want to implement an AJAX callback on the page so as a user mouse overs a specific part, the site might send a request to http://myPartsCatalog.com/Ford/OilFilters/fl1a, with the intention of gathering additional data about the part to display to the user. For this request we don't want to get an HTML representation of the part, we want the part rendered as JSON or XML, which will let us programatically access the part information.

I have grown tired of having to support multiple sites / pages one that renders the HTML and another that provides an API and exposes the data. The natural seperation of concerns that the MVC model gives us makes this an ideal platform to build a single web site which can serve HTML, XML, JSON or any other content we could imagine (for example what about a PDF version of the HTML page for offline usages).

To do this imagine we have the following controller defined:


public class PartController : Controller

{

public ActionResult List(string oem)

{

}

public ActionResult Detail(string oem, string partnumber)

{

}

}



We need a way to determine how the client wants the data to be returned. We could put that in the URL; however, that doesn't feel very restful. A key idea of a REST implementation is leveraging the capabilities in the HTTP protocol to define the resource your requesting. One of the Http Header's is Accept.

The Accept header allows us to define the content type that the client is requesting. So we could indicate in our request that we want text/json or text/xml. We could then put the following code in our controller:



        public ActionResult List(string oem)

        {

            //Get the Model for OEM.

            switch (HttpContext.Request.AcceptTypes[0])

            {

                case "*/*":

                case "text/html":

                    return View("PartList.aspx");

                case "text/json":

                    return View("PartList.json.aspx");

            }

        }



Warning: I am not finished investgating how ASP.Net handles the Accept header and my switch statement might not work; however, this is an example to highlight the flexibility of ASP.Net MVC.

So this works great; except that we want to unit test our code to ensure we are returning the right view for each request; however our controller is bound HttpContext which can be difficult to unit test. So the question is how do we decouple our controller from HttpContext? IModelBinder's are the answer.

We can define a ModelBinder by implementing the IModelBinder interface. The interface requires a single method that takes a couple parameters which provide context about the request, and returns an object.



    public class ContentTypeModelBinder : IModelBinder

    {

 

        #region IModelBinder Members

 

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)

        {

            if (controllerContext == null)

            {

                throw new ArgumentNullException("controllerContext");

            }

            if (bindingContext == null)

            {

                throw new ArgumentNullException("bindingContext");

            }

 

            return controllerContext.HttpContext.Request.AcceptTypes[0];

        }

 

        #endregion

    }




The return value from the function will be used to pass in to a parameter in a controller. This allows us to change our controller definition to:

        public ActionResult List(string oem, [ModelBinder(typeof(ContentTypeModelBinder))]string contentType)

        {

            //Get the Model for OEM.

            switch (contentType)

            {

                case "*/*":

                case "text/html":

                    return View("PartList.aspx");

                case "text/json":

                    return View("PartList.json.aspx");

            }

        }



The magic here is the ModelBinder attribute which is applied to the contentType parameter. This tells the ASP.Net MVC runtime to use our own custom IModelBinder to provide the value for the contentType attribute.



The thing I don't like about this is that we have to use an attribtue in our controller. It is possible to indicate that all instances of a specific type use a specific binder. But I will leave that for a different article.

Enjoy

Thursday, November 20, 2008

ASP.Net MVC Hyperlinks and Suboptimal Design

I never thought that after 8 years of professsionally programing I would be stumped at how to create a hyperlink. Well with ASP.Net's MVC framework there are a lot of ways to create links, and I want to share an experience I am having.

I want to create a link to my Product controller, calling a ListProductsByCategory action passing in a single parameter which is the id of the category. This should be a very simple pattern, and while yes I could hardcode the link such as:

<a href="Product/ListProductsByCategory/1">Widgets</a>


However, there should be a better way, and in fact there is. The ViewPage has a property called Html which has helper methods to help rendering a view. One of the methods is ActionLink, which sounds like what we need. So if we look at all the overloads we see two in particular:

  1. string linkText, string actionName, string controllerName, object values, object htmlAttribute
  2. string linkText, string actionName, string controllerName, RouteValueDictionary values, RouteValueDictionary htmlAttributes


Option 2 is fairly self explanatory based on the signature of the API. It looks like it takes a collection of parameters that is probally name value (I've not had the time to dig into what a RouteValueDictionary actually is). The problem I have with this is I really don't want to clutter up my markup with the syntax of building a dictionary. So seeing option 1 I figued I could do:

 <%=Html.ActionLink("Accessories",
"ListProductsByCategory", "Product", 3, null)%>


This resulted in the following html being generated:


<a href="Product/ListProductsByCategory">Accessories</a>


The problem is we have lost the value portion of the url. After reading Oren's post on some of his issues with the API Design I figured this was something more then a simple object that they were expecting but what? The API provides no hint as to what it might be (and the intellisense doesn't have the documentation yet so there were no tooltips).

Firing up google I came accross this post which showed me what I was missing. The object is actually an anonymous type, which surprises me since I had thought you couldn't pass anonymous types around. Well so anyways our code now looks like:


<%=Html.ActionLink("Accessories", "ListProductsByCategory", "Product", new { id = 3 }, null)%>



The only reason I feel this is suboptimal is that the API signature doesn't provide any hint as to what is expected. I prefer an API where I don't have to google to figure out the parameters, and with in most modern development environments this can be accomplished using good names mixed with intellisense. In this case, I wish the intellisense for the parameters would have been available and it could have explained what I needed. But I understand this is a beta version. I wonder why anonymous types don't have a base type other then object, that would have also made this more discoverable.

This works like a charm!

-Josh

S#arp Arch & MSTest

I have just started a new project, and as such I took this chance to further my exploration into alternative models then standard ASP.Net Webforms. I have been curious to learn more about ASP.Net MVC, and I wanted to continue exploring nHibernate.

When I first implemented nHibermate I used a model which was basically based upon Billy McCafferty's work; however, during this I found severe limitations one of which I blogged about here. Billy has been hard at work at developing a new set of patterns which are encapsulated in an architecure called S#arp.

One small glitch I ran into related to my use of MSTest. Billy has created a base class that all Respository test classes can inherit from. This class will setup and tear down nHibernate session and roll back any transactions which should help keep your database at a known state.

However, this class assumes the use of nUnit. Which when you try and use this from within MSTest it causes a null reference exception when you try and access the repository. If your like me and want to ue MSTest a real simple solution is this:

  1. Inherit from the RepositoryUnitTestsBase class.
  2. Explitly call the Setup and Tear down methods

So your class should basically look like:


    [TestClass]
    public class ProductRepositoryTest : RepositoryUnitTestsBase
    {
        [TestInitialize()]
        public void MyTestInitialize()
        {
            this.SetUp();
        }
 
        [TestCleanup()]
        public void MyTestCleanup()
        {
            this.TearDown();
        }
    }


I'd love to see these methods moved out into a seperate utility type class.

Enjoy
-Josh