1

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

var href = Url.Link("RouteName", new { id = 123 });

的值href是根据路由配置计算的 - 在这种情况下类似于http://example.org/api/products/123. 这很棒,因为我可以调整路线配置,而无需更新所有生成 href 的地方。发出的所有链接都符合新配置。

但是这个,或者任何类似的基于路由的技术,可以用来创建模板化的href吗?例如,要使用 HAL 的templated链接属性创建从某个资源到任意产品的链接:

"_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 吗?如果是这样,怎么做?

4

1 回答 1

2

好吧,我花了一段时间,但我有一些工作。诀窍是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 = this.GetTemplatedHref("DefaultApi", "product");

在实践中,我将再次修改和调整,例如添加绝对/相对选项等。但是使用路由名称获取模板的核心问题现在已经解决。

于 2014-09-24T21:19:56.463 回答