覆盖Webapi OData链接的主机 [英] Override host of webapi odata links
问题描述
我正在使用WebAPI 2.2和Microsoft.AspNet.OData 5.7.0创建支持分页的OData服务.
I'm using WebAPI 2.2 and Microsoft.AspNet.OData 5.7.0 to create an OData service that supports paging.
当托管在生产环境中时,WebAPI驻留在不对外公开的服务器上,因此OData响应中返回的各种链接(例如@odata.context
和@odata.nextLink
)指向内部IP地址,例如. http://192.168.X.X/<AccountName>/api/...
等
When hosted in the production environment, the WebAPI lives on a server that is not exposed externally, hence the various links returned in the OData response such as the @odata.context
and @odata.nextLink
point to the internal IP address e.g. http://192.168.X.X/<AccountName>/api/...
etc.
通过在每个ODataController方法中实现一些逻辑以将内部URL替换为诸如https://account-name.domain.com/api/...
之类的外部URL,我已经能够修改Request.ODataProperties().NextLink
,但这非常不便,并且只能修复NextLinks.
I've been able to modify the Request.ODataProperties().NextLink
by implementing some logic in each and every ODataController method to replace the internal URL with an external URL like https://account-name.domain.com/api/...
, but this is very inconvenient and it only fixes the NextLinks.
在OData服务的配置时是否可以通过某种方式设置外部主机名?我看过一个属性Request.ODataProperties().Path
,想知道是否可以在config.MapODataServiceRoute("odata", "odata", GetModel());
调用中或在GetModel()
实现中使用例如ODataConventionModelBuilder
设置基本路径?
Is there some way to set an external host name at configuration time of the OData service? I've seen a property Request.ODataProperties().Path
and wonder if it's possible to set a base path at the config.MapODataServiceRoute("odata", "odata", GetModel());
call, or in the GetModel()
implementation using for instance the ODataConventionModelBuilder
?
更新:到目前为止,我想出的最好的解决方案是创建一个覆盖Initialize
方法的BaseODataController
并检查Request.RequestUri.Host.StartsWith("beginning-of-known-internal-IP-address")
是否存在,然后执行RequestUri像这样重写:
UPDATE: The best solution I've come up with so far, is to create a BaseODataController
that overrides the Initialize
method and checks whether the Request.RequestUri.Host.StartsWith("beginning-of-known-internal-IP-address")
and then do a RequestUri rewrite like so:
var externalAddress = ConfigClient.Get().ExternalAddress; // e.g. https://account-name.domain.com
var account = ConfigClient.Get().Id; // e.g. AccountName
var uriToReplace = new Uri(new Uri("http://" + Request.RequestUri.Host), account);
string originalUri = Request.RequestUri.AbsoluteUri;
Request.RequestUri = new Uri(Request.RequestUri.AbsoluteUri.Replace(uriToReplace.AbsoluteUri, externalAddress));
string newUri = Request.RequestUri.AbsoluteUri;
this.GetLogger().Info($"Request URI was rewritten from {originalUri} to {newUri}");
这完美地修复了所有控制器的@odata.nextLink
URL,但是由于某些原因,@odata.context
URL仍然具有AccountName
部分(例如,
This perfectly fixes the @odata.nextLink
URLs for all controllers, but for some reason the @odata.context
URLs still get the AccountName
part (e.g. https://account-name.domain.com/AccountName/api/odata/$metadata#ControllerName) so they still don't work.
推荐答案
重写RequestUri
足以影响@odata.nextLink
值,因为 UrlHelper
,这是某种方式引用原始请求URI中的路径. (因此,您在@odata.context
链接中看到了AccountName
.我已经在代码中看到了此行为,但是我无法跟踪缓存的URI路径的来源.)
Rewriting the RequestUri
is sufficient to affect @odata.nextLink
values because the code that computes the next link depends on the RequestUri
directly. The other @odata.xxx
links are computed via a UrlHelper
, which is somehow referencing the path from the original request URI. (Hence the AccountName
you see in your @odata.context
link. I've seen this behavior in my code, but I haven't been able to track down the source of the cached URI path.)
我们可以通过创建一个CustomUrlHelper
类来即时重写OData链接,而不是重写RequestUri
.新的GetNextPageLink
方法将处理@odata.nextLink
重写,而Link
方法重写将处理所有其他重写.
Rather than rewrite the RequestUri
, we can solve the problem by creating a CustomUrlHelper
class to rewrite OData links on the fly. The new GetNextPageLink
method will handle @odata.nextLink
rewrites, and the Link
method override will handle all other rewrites.
public class CustomUrlHelper : System.Web.Http.Routing.UrlHelper
{
public CustomUrlHelper(HttpRequestMessage request) : base(request)
{ }
// Change these strings to suit your specific needs.
private static readonly string ODataRouteName = "ODataRoute"; // Must be the same as used in api config
private static readonly string TargetPrefix = "http://localhost:8080/somePathPrefix";
private static readonly int TargetPrefixLength = TargetPrefix.Length;
private static readonly string ReplacementPrefix = "http://www.contoso.com"; // Do not end with slash
// Helper method.
protected string ReplaceTargetPrefix(string link)
{
if (link.StartsWith(TargetPrefix))
{
if (link.Length == TargetPrefixLength)
{
link = ReplacementPrefix;
}
else if (link[TargetPrefixLength] == '/')
{
link = ReplacementPrefix + link.Substring(TargetPrefixLength);
}
}
return link;
}
public override string Link(string routeName, IDictionary<string, object> routeValues)
{
var link = base.Link(routeName, routeValues);
if (routeName == ODataRouteName)
{
link = this.ReplaceTargetPrefix(link);
}
return link;
}
public Uri GetNextPageLink(int pageSize)
{
return new Uri(this.ReplaceTargetPrefix(this.Request.GetNextPageLink(pageSize).ToString()));
}
}
使用基本控制器类的Initialize
方法连接CustomUrlHelper
.
Wire-up the CustomUrlHelper
in the Initialize
method of a base controller class.
public abstract class BaseODataController : ODataController
{
protected abstract int DefaultPageSize { get; }
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
var helper = new CustomUrlHelper(controllerContext.Request);
controllerContext.RequestContext.Url = helper;
controllerContext.Request.ODataProperties().NextLink = helper.GetNextPageLink(this.DefaultPageSize);
}
在上面请注意,页面大小对于给定控制器类中的所有操作都是相同的.您可以通过将ODataProperties().NextLink
的分配移动到特定操作方法的主体来解决此限制,如下所示:
Note in the above that the page size will be the same for all actions in a given controller class. You can work around this limitation by moving the assignment of ODataProperties().NextLink
to the body of a specific action method as follows:
var helper = this.RequestContext.Url as CustomUrlHelper;
this.Request.ODataProperties().NextLink = helper.GetNextPageLink(otherPageSize);
这篇关于覆盖Webapi OData链接的主机的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!