Search code examples
c#asp.net-mvc-4asp.net-web-apiurl-helper

Url.Link not working in WebAPI


Using the unit test below .. Iam trying to test my webapi.

[Test]
public void CheckControllerForCreate()
{
    var config = new HttpConfiguration();
    config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
    var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/product");
    var route = config.Routes.MapHttpRoute("Foo", "api/{controller}/{id}");
    var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "products" } });
    var controller = new ProductController
    {
        ControllerContext = new HttpControllerContext(config, routeData, request),
        Request = request,
        Url = new UrlHelper(request)
    };

    controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;

    var result = controller.Create(new Product {Id = 4, Name = "Tomato Soup", Category = "Groceries", Price = 1});
}

[HttpPost]
public HttpResponseMessage Create(Product product)
{
    var url = Url;
    if (product == null)
        throw new HttpResponseException(new HttpResponseMessage{StatusCode = HttpStatusCode.BadRequest,ReasonPhrase = "Product is not specified"});
    products.Add(product);
    var response = Request.CreateResponse(HttpStatusCode.Created, product);
    string uri = Url.Link("Foo", product.Id);
    response.Headers.Location = new Uri(uri);
    return response;
}

The Create Action throws an exception because uri is null. Now, the Url helper is correctly picking up the RouteName , otherwise there would be a RouteName not found exception. I am assuming that somethign is wrong with my configuration.

I referred to http://www.peterprovost.org/blog/2012/06/16/unit-testing-asp-dot-net-web-api and several other posts for unit testing the controllers.

The WebAPI method is here on codeplex

http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/1acb241299a8#src/System.Web.Http/Routing/UrlHelper.cs

Edit

I have narrowed it down to vpd being null in ( UrlHelper)

IHttpVirtualPathData vpd = configuration.Routes.GetVirtualPath(
                request: request,
                name: routeName,
                values: routeValues);

Can't seem to figure out why ?


Solution

  • You need to set the routeData into the request, the same way you did with the configuration:

    controller.Request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
    

    Also you are incorrectly using the Url.Link helper. You haven't specified a controller nor you have indicated the id.

    The code in your controller should look like this:

    string uri = Url.Link("Foo", new { id = product.Id, controller = "product" });
    

    UPDATE:

    Here's a full example.

    Controller:

    public class ProductController : ApiController
    {
        public HttpResponseMessage Create(int id)
        {
            var uri = Url.Link("Foo", new { id = id, controller = "product" });
            return Request.CreateResponse(HttpStatusCode.OK, uri);
        }
    }
    

    Test:

    // arrange
    var config = new HttpConfiguration();
    config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
    var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/product");
    var route = config.Routes.MapHttpRoute("Foo", "api/{controller}/{id}");
    var routeData = new HttpRouteData(route, new HttpRouteValueDictionary(new { controller = "product" }));
    var controller = new ProductController
    {
        ControllerContext = new HttpControllerContext(config, routeData, request),
        Request = request,
        Url = new UrlHelper(request)
    };
    controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
    controller.Request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
    
    // act
    var result = controller.Create(4);
    
    // assert
    ...