Monday, February 27, 2012

MakeGenericType to the rescue

I have a need to return a HttpResponseMessage<> in an ActionFilterAttribute with ActionDescriptor.ReturnType. All my response object belong to a single base class. Anyway, here is how I did it.

private static void SetInvalidRequestResponse(HttpActionContext context)
{
  var returnType = context.ActionDescriptor.ReturnType;
    
  var response = Activator.CreateInstance(returnType) as BaseResponse;
  var request = context.ActionArguments.First().Value as BaseRequest;

  response.ResponseCode = "Invalid request";

  if (request != null)
  {
    response.SourceId = request.SourceId;
    response.Uuid = request.Uuid;
  }

  var genericType = typeof (HttpResponseMessage<>)
    .MakeGenericType(new[] {returnType});

  context.Response = Activator
    .CreateInstance(genericType, response, HttpStatusCode.Forbidden)
      as HttpResponseMessage;
}

Thursday, February 23, 2012

Use Windsor as Dependency Resolver for Asp.Net Web Api

Here is a little code snippet you can use to integrate Windsor into Web Api. One important note about this class is that we are only resolving Type we care to register to our container and we leave to rest to Web Api internal dependency resolver. We do that by return null in the GetService method and returning an empty collection for GetServices method. You should read Using the Web API Dependency Resolver by Mike Wasson to have a better understanding of how the process work.

public class WindsorDependencyResolver : IDependencyResolver
{
  private readonly IWindsorContainer _container;

  public WindsorDependencyResolver(IWindsorContainer container)
  {
    _container = container;
  }

  public object GetService(Type serviceType)
  {
    return _container.Kernel.HasComponent(serviceType) ?
      _container.Resolve(serviceType) : null;
  }

  public IEnumerable<object> GetServices(Type serviceType)
  {
    return _container.Kernel.HasComponent(serviceType) ?
      _container.ResolveAll(serviceType).Cast<object> () : new List<object>();
  }
}

The next step is to register your Dependency Resolver. I added this line to my Application_Start.

GlobalConfiguration.Configuration.ServiceResolver.SetResolver(new WindsorDependencyResolver(IocContainer));

Wednesday, February 22, 2012

Found the solution

I asked my question on the asp.net forum and I got the answer. Go here to see the solution.

Anyway, I will include the answer here just in case the post disappeared in the future.

public class ReadAsSingleObjectPolicy : IRequestContentReadPolicy
{
  public RequestContentReadKind GetRequestContentReadKind(HttpActionDescriptor actionDescriptor)
  {
    return RequestContentReadKind.AsSingleObject;
  }
}
GlobalConfiguration.Configuration.ServiceResolver
  .SetService(typeof(IRequestContentReadPolicy), new ReadAsSingleObjectPolicy());

Default XmlSerializer for Web Api do not to support complex object?

I am trying to post an xml of a model with nested objects and the XmlSerializer for Asp.Net Web Api only deserialize the first level property.

I found a solution to my problem but I am sure I am not doing it right.

Here is an example of the xml I am trying to post and the C# classes it supposed to deserialize to.
<customer>
  <customerid>100</customerid>
  <orders>
  <order><orderid>1</orderid><amount>1</amount></order>
  <order><orderid>2</orderid><amount>2</amount></order>
  </orders>
</customer>
public class Customer
{
  public Customer()
  {
    Orders = new List();
  }

  public string CustomerId { get; set; }

  public List Orders { get; set; }
}

public class Order
{
  public string OrderId { get; set; }
  public int Amount { get; set; }
}

The only thing I get is CustomerId = 100. Orders count is 0. If I switch the Content-Type to application/json then it deserialized correctly.

After many trials and errors, I noticed that the Type parameter of the OnReadFromStreamAsync in XmlSerializer is IKeyValueModel instead of Customer like I expected.

I ended up writing my own XmlFormatter and replace the default one with mine. I added this two lines to my Application_Start method.

GlobalConfiguration.Configuration.Formatters.Add(new CustomXmlFormatter());
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

Here is mine CustomXmlFormatter class.

  public CustomXmlFormatter()
  {
    SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
    SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml"));
    Encoding = new UTF8Encoding(false, true);
  }

  protected override bool CanReadType(Type type)
  {
    if (type == (Type)null)
      throw new ArgumentNullException("type");

    if (type == typeof(IKeyValueModel))
      return false;

    return true;
  }

  protected override bool CanWriteType(Type type)
  {
    return true;
  }

  protected override Task OnReadFromStreamAsync(Type type, Stream stream, 
    HttpContentHeaders contentHeaders, FormatterContext formatterContext)
  {
    return Task.Factory.StartNew(() =>
    {
      using (var streamReader = new StreamReader(stream, Encoding))
      {
        var serializer = new XmlSerializer(type);
        return serializer.Deserialize(streamReader);
      }
    });
  }

  protected override Task OnWriteToStreamAsync(Type type, object value, Stream stream, 
    HttpContentHeaders contentHeaders, FormatterContext formatterContext, 
    System.Net.TransportContext transportContext)
  {
    var serializer = new XmlSerializer(type);
    return Task.Factory.StartNew(() =>
    {
      using (var streamWriter = new StreamWriter(stream, Encoding))
      {
        serializer.Serialize(streamWriter, value);
      }
    });
  }
}

Tuesday, February 21, 2012

Simplest Web Api Site with Nuget

Here are the steps to create the simplest ASP.Net Web Api with Nuget. If you want to know more about Web Api, go here and read up.

1. Create an Empty Web Application and call it SimpleWebApi.









2. Open your Package Manager Console if it is not already open. Go to View-> Other Windows-> Package Manager Console.

3. Type these two lines
Install-Package AspNetWebApi
Install-Package System.Json

You will see something similar to this:
PM> Install-Package AspNetWebApi
Attempting to resolve dependency 'AspNetWebApi.Core (≥ 4.0.20126.16343)'.
Attempting to resolve dependency 'System.Net.Http.Formatting (≥ 4.0.20126.16343)'.
Attempting to resolve dependency 'System.Net.Http (≥ 2.0.20126.16343)'.
Attempting to resolve dependency 'System.Web.Http.Common (≥ 4.0.20126.16343)'.
'AspNetWebApi 4.0.20126.16343' already installed.
Successfully added 'System.Net.Http 2.0.20126.16343' to SimpleWebApi.
Successfully added 'System.Net.Http.Formatting 4.0.20126.16343' to SimpleWebApi.
Successfully added 'System.Web.Http.Common 4.0.20126.16343' to SimpleWebApi.
Successfully added 'AspNetWebApi.Core 4.0.20126.16343' to SimpleWebApi.
Successfully added 'AspNetWebApi 4.0.20126.16343' to SimpleWebApi.
PM> Install-Package System.Json
'System.Json 4.0.20126.16343' already installed.
Successfully added 'System.Json 4.0.20126.16343' to SimpleWebApi.

4. Right click on the SimpleWebApi project and select Add -> New Item
5. Select Global Application Class and click Add.
6. Open Global.asax and add this line to Application_Start

        protected void Application_Start(object sender, EventArgs e)
        {
            System.Web.Routing.RouteTable.Routes.MapHttpRoute("Default Api", "{Controller}");
        }

7. Add System.Web.Http; to the top of the file.
8. Right click on the SimpleWebApi project and select Add -> New Folder and create a folder name Api. Your project layout should look like this now.








9. Right click on the Api folder and select Add -> Class.
10. Name the class as GreetingController.cs
11. Modify your GreetingController to look like this:

using System;
using System.Web.Http;

namespace SimpleWebApi.Api
{
    public class GreetingController : ApiController
    {
        public string GetGreeting(string name)
        {
            return String.Format("Hello {0}", name);
        }
    }
}
12. Press Ctrl + F5 to run your project. A browser should popup with a Directory listing.
13. Add /Greeting to the end of the url. For example: mine url was http://localhost:4831 so I change it to be http://localhost:4831/Greeting. Your browser should display something similar to this:














14. Now edit your url to be http://localhost:4831/Greeting?World. Remember to make sure your port number is correct. Here is what should be displayed.














You are done. Congratulation!