Eiv*_*eth 2 asp.net odata asp.net-web-api odata-v4
我正在使用WebAPI 2.2和Microsoft.AspNet.OData 5.7.0创建支持分页的OData服务。
当托管在生产环境中时,WebAPI驻留在未对外公开的服务器上,因此OData响应中返回的各种链接(例如@odata.context和@odata.nextLink指向内部IP地址,例如,http://192.168.X.X/<AccountName>/api/...等等)。
Request.ODataProperties().NextLink通过在每个ODataController方法中实现一些逻辑以将内部URL替换为外部URL(例如)https://account-name.domain.com/api/...,我已经能够修改,但这非常不便,并且只能修复NextLinks。
有什么方法可以在OData服务的配置时设置外部主机名?我已经看过一个属性,Request.ODataProperties().Path并且想知道是否可以在config.MapODataServiceRoute("odata", "odata", GetModel());调用时或在GetModel()使用例如ODataConventionModelBuilder?的实现中设置基本路径?
更新:到目前为止,我想出的最好的解决方案是创建一个BaseODataController重写该Initialize方法并检查是否存在Request.RequestUri.Host.StartsWith("beginning-of-known-internal-IP-address"),然后像下面这样重写RequestUri:
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}");
Run Code Online (Sandbox Code Playgroud)
这可以完美地修复@odata.nextLink所有控制器的URL,但是由于某些原因,@odata.contextURL仍然可以发挥AccountName作用(例如 https://account-name.domain.com/AccountName/api/odata/ $ metadata#ControllerName),因此它们仍然没有工作。
lencharest 的回答很有希望,但我发现他的方法有所改进。我没有使用 UrlHelper,而是创建了一个派生自 System.Net.Http.DelegatingHandler 的类。这个类(首先)插入到消息处理管道中,因此在改变传入的 HttpRequestMessage 方面有一个裂缝。这是对上述解决方案的改进,因为除了更改特定于控制器的 URL(如 UrlHelper 所做的,例如https://data.contoso.com/odata/MyController),它还更改显示为的 URL OData 服务文档(例如,https ://data.contoso.com/odata)中的 xml:base 。
我的特定应用程序是在代理服务器后面托管 OData 服务,我希望服务器提供的所有 URL 都是外部可见的 URL,而不是内部可见的 URL。而且,我不想为此依赖注释;我希望它是全自动的。
消息处理程序如下所示:
public class BehindProxyMessageHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var builder = new UriBuilder(request.RequestUri);
var visibleHost = builder.Host;
var visibleScheme = builder.Scheme;
var visiblePort = builder.Port;
if (request.Headers.Contains("X-Forwarded-Host"))
{
string[] forwardedHosts = request.Headers.GetValues("X-Forwarded-Host").First().Split(new char[] { ',' });
visibleHost = forwardedHosts[0].Trim();
}
if (request.Headers.Contains("X-Forwarded-Proto"))
{
visibleScheme = request.Headers.GetValues("X-Forwarded-Proto").First();
}
if (request.Headers.Contains("X-Forwarded-Port"))
{
try
{
visiblePort = int.Parse(request.Headers.GetValues("X-Forwarded-Port").First());
}
catch (Exception)
{ }
}
builder.Host = visibleHost;
builder.Scheme = visibleScheme;
builder.Port = visiblePort;
request.RequestUri = builder.Uri;
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
Run Code Online (Sandbox Code Playgroud)
您在 WebApiConfig.cs 中连接处理程序:
config.Routes.MapODataServiceRoute(
routeName: "odata",
routePrefix: "odata",
model: builder.GetEdmModel(),
pathHandler: new DefaultODataPathHandler(),
routingConventions: ODataRoutingConventions.CreateDefault()
);
config.MessageHandlers.Insert(0, new BehindProxyMessageHandler());
Run Code Online (Sandbox Code Playgroud)
重写RequestUri足以影响@odata.nextLink值,因为计算下一个链接的代码RequestUri直接取决于。其他@odata.xxx链接是通过来计算的UrlHelper,它以某种方式引用了来自原始请求URI的路径。(因此,AccountName您在@odata.context链接中看到的内容。我已经在代码中看到此行为,但是我无法跟踪缓存的URI路径的来源。)
除了重写之外RequestUri,我们还可以通过创建一个CustomUrlHelper类来实时重写OData链接来解决问题。新GetNextPageLink方法将处理@odata.nextLink重写,而Link方法重写将处理所有其他重写。
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()));
}
}
Run Code Online (Sandbox Code Playgroud)
连接基本控制器类CustomUrlHelper的Initialize方法。
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);
}
Run Code Online (Sandbox Code Playgroud)
在上面请注意,页面大小对于给定控制器类中的所有操作都是相同的。您可以通过将对指定ODataProperties().NextLink操作方法的主体的分配移动如下来解决此限制:
var helper = this.RequestContext.Url as CustomUrlHelper;
this.Request.ODataProperties().NextLink = helper.GetNextPageLink(otherPageSize);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2191 次 |
| 最近记录: |