WebAPI Url.Link() returning NULL

2019-09-10 09:44发布

问题:

I am having some trouble in generating WebAPI hypermedia links in a project I am working on.

In the code snippet below the extension methods Order, OrderUpdate and OrderDelete for individual orders are all returning Null links. I suspect this is because WebAPI is unable to resolve these inner routes from the parent Orders route.

I am not certain why Null links are being returned as I am supplying valid route names to the Link method.

You can see from the resulting JSON output that the Uri elements are all Null for the Links array element within the Order object.

// WebApiConfig.cs

config.Routes.MapHttpRoute(
    name: "Orders",
    routeTemplate: "api/orders",
    defaults: new { controller = "order", action = "get" },
    constraints: new { httpMethod = new HttpMethodConstraint( HttpMethod.Get ) }
);


config.Routes.MapHttpRoute(
    name: "Order",
    routeTemplate: "api/orders/{orderid}",
    defaults: new { controller = "order" },
    constraints: new { orderid = "^[0-9]+$", httpMethod = new HttpMethodConstraint( HttpMethod.Get ) }
);

config.Routes.MapHttpRoute(
    name: "OrderUpdate",
    routeTemplate: "api/orders/{orderid}",
    defaults: new { controller = "order", action = "put" },
    constraints: new { orderid = "^[0-9]+$", httpMethod = new HttpMethodConstraint( HttpMethod.Put ) }
);

// OrderController.cs

public async Task<Orders> Get()
{
    var orders = new Orders();

    orders.Links.OrderList( Url, "self" ); // returns a link OK
    orders.Links.OrderAdd( Url, "order-create" ); // returns a link OK
    foreach ( var order in await _repository.GetOrders() )
    {
        order.Links.Order( Url, "self", order.Id ); // returns NULL
        if ( !User.IsInRole( "Readonly" ) )
        {
            order.Links.OrderUpdate( Url, "order-update", order.Id ); // returns NULL
            order.Links.OrderDelete( Url, "order-delete", order.Id ); // returns NULL
        }
        orders.Order.Add( order );
    }
    return orders;
}

// LinkGenerationExtensions.cs

public static void Order( this List<Link> @this, UrlHelper url, string rel, int orderId )
{
    @this.Add( new Link { Rel = rel, Uri = url.OrderUri( orderId ) } );
}

public static string OrderUri( this UrlHelper url, int orderId )
{
    return url.Link( "Order", new { id = orderId } );
}

public static void OrderList( this List<Link> @this, UrlHelper url, string rel )
{
    @this.Add( new Link { Rel = rel, Uri = url.OrderListUri() } );
}

public static string OrderListUri( this UrlHelper url )
{
    return url.Link( "Orders", new { } );
}

public static void OrderUpdate( this List<Link> @this, UrlHelper url, string rel, int orderId )
{
    @this.Add( new Link { Rel = rel, Uri = url.OrderUpdateUri( orderId ) } );
}

public static string OrderUpdateUri( this UrlHelper url, int orderId )
{
    return url.Link( "OrderUpdate", new { id = orderId } );
}

The above code generates the following JSON response:

{
    "Order": [
        {
            "Id": 4,
            "OrderRef": 123456,
            "OrderDate": "2015-02-04T10:28:00",
            "CustomerName": "ACME Construction Ltd",
            "OrderedBy": "PA",
            "InstallationDate": "2015-06-15T00:00:00",
            "Address": "900 ACME Street",
            "Postcode": "SW2 7AX",
            "Town": "London",
            "OrderNumber": "6508756191",
            "Value": 525,
            "Invoice": 0,
            "Links": [
                {
                    "Rel": "self",
                    "Uri": null
                },
                {
                    "Rel": "order-update",
                    "Uri": null
                },
                {
                    "Rel": "order-delete",
                    "Uri": null
                }
            ]
        }
    ],
    "Links": [
        {
            "Rel": "self",
            "Uri": "http://localhost/Site/api/orders"
        },
        {
            "Rel": "order-create",
            "Uri": "http://localhost/Site/api/orders/add"
        }
    ]
}

回答1:

I've answered my own question after discovering that the parameter names in the route values must match those of their defined routes.

Replacing id with orderid did the job.

Here is the updated code:

// LinkGenerationExtensions.cs

public static void Order( this List<Link> @this, UrlHelper url, string rel, int orderId )
{
    @this.Add( new Link { Rel = rel, Uri = url.OrderUri( orderId ) } );
}

public static string OrderUri( this UrlHelper url, int orderId )
{
    return url.Link( "Order", new { orderid = orderId } );
}

public static void OrderList( this List<Link> @this, UrlHelper url, string rel )
{
    @this.Add( new Link { Rel = rel, Uri = url.OrderListUri() } );
}

public static string OrderListUri( this UrlHelper url )
{
    return url.Link( "Orders", new { } );
}

public static void OrderUpdate( this List<Link> @this, UrlHelper url, string rel, int orderId )
{
    @this.Add( new Link { Rel = rel, Uri = url.OrderUpdateUri( orderId ) } );
}

public static string OrderUpdateUri( this UrlHelper url, int orderId )
{
    return url.Link( "OrderUpdate", new { orderid = orderId } );
}

public static void OrderDelete( this List<Link> @this, UrlHelper url, string rel, int orderId )
{
    @this.Add( new Link { Rel = rel, Uri = url.OrderDeleteUri( orderId ) } );
}

public static string OrderDeleteUri( this UrlHelper url, int orderId )
{
    return url.Link( "OrderDelete", new { orderid = orderId } );
}