免责声明 :首先,我想提一下,我在互联网上寻找答案,阅读了所有文档,阅读了我可能遇到的所有问题在这里找到,但到目前为止还没有运气。
所以,这是我的情况。我正在使用ASP.NET Core 2.2构建API,并且正在使用HATEOAS(HAL规范和Halcyon库)。我应该提供链接以及资源本身。这首先促使我转向HATEOAS。一些链接是模板化的,因为它可能是一种PUT
方法,并且id
将由前端指定。
问题是,我的控制器可以具有非常不同的路由(使用基于属性的路由),并且硬编码链接是一件坏事,因为如果路由发生更改,我需要记住也要更改链接的使用位置。因此,我决定根据Controller类型和Action名称生成链接。我找到了LinkGenerator,但如果我没有为路由指定所有参数,似乎它返回null。这是一个代码示例:
[Route("api/metadata")]
[ApiController]
public class MetadataController : ControllerBase
{
private readonly IMetadataProvider _metadataProvider;
private readonly LinkGenerator _linkGenerator;
public MetadataController(
IMetadataProvider metadataProvider,
LinkGenerator linkProvider)
{
_metadataProvider = metadataProvider;
_linkGenerator = linkProvider;
}
[HttpGet]
public IActionResult GetMetadata()
{
var metadata = _metadataProvider.GetMetadata();
// here url will be 'null', because last parameter is null
// and route requires parameter 'name' to be specified instead of 'null'
// EXPECTED: "api/metadata/{name}"
// ACTUAL: null
string url = _linkGenerator.GetPathByAction(
nameof(MetadataController.GetByName),
nameof(MetadataController).Replace(nameof(Controller), string.Empty),
null);
var response = new HALResponse(metadata)
.AddSelfLink(HttpContext.Request)
.AddLinks(new Link(name, url));
return Ok(response);
}
[HttpGet("{name}")]
public IActionResult GetByName(string name)
{
var metadata = _metadataProvider.GetMetadataForEntity(name);
return Ok(metadata);
}
}
我如何生成链接,以便它不会被硬编码并被模板化?
答案 0 :(得分:1)
经过几个小时的调试ASP.NET源代码,我想我找到了一种方法。
看来LinkGenerator
旨在建立一个完整且有效的url,因此所有参数都是必需的。我要找的实际上是一个路由模式。
在调试时,我发现将IEndpointAddressScheme<RouteValuesAddress>
服务注入到LinkGnerator
中。它实际上用于查找路线样式。之后, LinkGenerator 尝试填充所有参数。
以下代码已修复并可以正常工作:
[ApiController]
public class MetadataController : ControllerBase
{
private readonly IMetadataProvider _metadataProvider;
private readonly IEndpointAddressScheme<RouteValuesAddress> _endpointAddress;
public MetadataController(
IMetadataProvider metadataProvider,
IEndpointAddressScheme<RouteValuesAddress> endpointAddress)
{
_metadataProvider = metadataProvider;
_endpointAddress = endpointAddress;
}
[HttpGet]
public IActionResult GetMetadata()
{
var metadata = _metadataProvider.GetMetadata();
// EXPECTED: "api/metadata/{name}"
// ACTUAL: "api/metadata/{name}"
string actionName = nameof(MetadataController.GetById);
string controllerName = nameof(MetadataController).Replace(nameof(Controller), string.Empty);
var url = _endpointAddress.FindEndpoints(CreateAddress(actionName, controllerName))
.OfType<RouteEndpoint>()
.Select(x => x.RoutePattern)
.FirstOrDefault();;
var response = new HALResponse(metadata)
.AddSelfLink(HttpContext.Request)
.AddLinks(new Link(name, url));
return Ok(response);
}
[HttpGet("{name}")]
public IActionResult GetByName(string name)
{
var metadata = _metadataProvider.GetMetadataForEntity(name);
return Ok(metadata);
}
private static RouteValuesAddress CreateAddress(string action, string controller)
{
var explicitValues = new RouteValueDictionary(null);
var ambientValues = GetAmbientValues(httpContext);
explicitValues ["action"] = action;
explicitValues ["controller"] = controller;
return new RouteValuesAddress()
{
AmbientValues = ambientValues,
ExplicitValues = explicitValues
};
}
}