C# 使用ASP.NET Web API创建基于路由的模板化HREF

C# 使用ASP.NET Web API创建基于路由的模板化HREF,c#,asp.net-web-api2,url-template,C#,Asp.net Web Api2,Url Template,在Web API控制器中向资源表示添加链接非常容易。使用控制器的Url属性(属于UrlHelper类),您可以使用路由名称构建链接的href,如下所示: var href = Url.Link("RouteName", new { id = 123 }); var href = this.GetTemplatedHref("DefaultApi", "product"); href的值是根据路线配置计算的-在本例中类似于http://example.org/api/products/123。

在Web API控制器中向资源表示添加链接非常容易。使用控制器的
Url
属性(属于
UrlHelper
类),您可以使用路由名称构建链接的href,如下所示:

var href = Url.Link("RouteName", new { id = 123 });
var href = this.GetTemplatedHref("DefaultApi", "product");
href
的值是根据路线配置计算的-在本例中类似于
http://example.org/api/products/123
。这很好,因为我可以调整路由配置,而无需更新生成HREF的所有位置。发出的所有链接都符合新配置

但是,这项技术或任何类似的基于路由的技术能否用于创建模板化的HREF?例如,要使用HAL的
templated
link属性创建从某个资源到任意产品的链接:

"_links": {
  "self": {
    "href": "http://example.org/api/productfinder"
  },
  "product": {
    "href": "http://example.org/api/products/{productId}",
    "templated": true
  }  
}
(我没有一个叫做productfinder的资源——只是这个问题的一个人为的例子)

我尝试将
routeValues
参数设置为模板的字符串,例如

var href = Url.Link("RouteName", new { id = "{productId}" });
这将生成一个URL编码字符串:
http://example.org/api/products/%7BproductId%7D
。我可以使用
.Replace
,但这种方法由于另一个原因而失败。如果路由配置具有类型约束,例如
{id:int}
,则href变为null

我尝试将null传递给
routeValues
,希望随后可以添加模板部分
Link()
返回null,因此
href
最终只是模板部分。另外,对于像
api/{this}/something/{that}{?q}


所以我被卡住了。我可以使用路由生成模板化的HREF吗?如果是这样,怎么做?

好吧,我花了一段时间,但我有一些有效的方法。诀窍是从配置的路由表中查找
RouteTemplate
。每个控制器都有一个对配置的引用。从那以后,一切都很容易

我为
ApiController
创建了一个扩展类。实际上,我可能会将ApiController子类化,但想法是一样的。另外,我可以返回
Uri
对象,而不是使用
.ToString()

public static class ApiControllerExtensions
{
    public static string GetTemplatedHref(this ApiController controller, string routeName, string controllerName = "")
    {
        var routeTemplate = controller.Configuration.Routes[routeName].RouteTemplate;

        var baseUri = new Uri(controller.Request.RequestUri.GetLeftPart(UriPartial.Authority));
        var templatedHref = new Uri(baseUri, routeTemplate).ToString();

        return string.IsNullOrEmpty(controllerName) ? 
            templatedHref : 
            templatedHref.Replace("{controller}", controllerName.ToLower());
    }
}
现在,当我在控制器的方法中需要一个模板href时,我可以这样做:

var href = this.GetTemplatedHref("RouteName");
这适用于管线属性。它也适用于预先配置的路由,例如:

config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
因此,我可以提供控制器名称并获得模板href,如下所示:

var href = Url.Link("RouteName", new { id = 123 });
var href = this.GetTemplatedHref("DefaultApi", "product");
在实践中,我将再次进行修改和调整,例如添加绝对/相对等选项。但使用管线名称获取模板的核心问题现在已经解决