覆盖 webapi odata 链接的主机 [英] Override host of webapi odata links

查看:16
本文介绍了覆盖 webapi odata 链接的主机的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 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 地址 eghttp://192.168.X.X//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 方法中实现一些逻辑来修改 Request.ODataProperties().NextLink 以将内部 URL 替换为像 https 这样的外部 URL://account-name.domain.com/api/...,但是这样很不方便,而且只能修复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?

更新:到目前为止我想出的最好的解决方案是创建一个 BaseODataController 来覆盖 Initialize 方法并检查是否 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 部分(例如 https://account-name.domain.com/AccountName/api/odata/$metadata#ControllerName) 所以它们仍然不起作用.

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 值,因为 计算下一个链接的代码直接依赖于 RequestUri.其他 @odata.xxx 链接是通过 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆