4

WebAPIをサブドメインベースのルートで機能させるのに問題があります。要するに、私は正しいコントローラーとメソッドに到達していますが、サブドメインからのデータトークンがWebAPIによって取得されていません。

私のシナリオにはこれがあります:

contoso.myapp.com
fabrikam.myapp.com
{tenant}.myapp.com

すべて同じApiControllerに解決され、{tenant}トークンを抽出できるようにしたいと思います。

この記事のコードを使用しましたhttp://blog.maartenballiauw.be/post/2012/06/18/Domain-based-routing-with-ASPNET-Web-API.aspx

しかし、記事が書かれてからASP.NET Web Apiがベータ版を終了するまでの間に、何かが変わったようです。RouteTable.Routesこの記事のコードは、 Web APIのルートが構成されているときに依存していますHttpConfiguration.Routesが、これはHttpRouteCollection通常ではなく、実際RouteCollectionに派生しています。RouteCollection

HttpRouteそこで、の代わりに派生するようにコードを変更しましたRoute。コードは次のとおりです。

https://gist.github.com/3766125

このようにルートを設定します

 config.Routes.Add(new HttpDomainRoute(
            name: "test",
            domain: "{tenant}.myapp.com",
            routeTemplate: "test",
            defaults: new { controller = "SomeController", action = "Test" }
        ));

そして、私のリクエストは適切なコントローラーにルーティングされます。ただし、テナントデータトークンが入力されることはありません(this.Request.GetRouteData()表示される場合、コントローラートークンとアクショントークンは表示されますが、テナントは表示されません)。ブレークポイントを設定するGetRouteDataと、呼び出されることはありません。

リフレクターを使用してコードパスをたどり、httpRouteCollectionレベルでGetRouteDataが呼び出されている場所を確認しようとしましたが、コレクションが列挙しているのは空のようです。通常のASP.NETルーティングとWEbAPIルーティングの統合がどのようにブリッジされるかは正確にはわかりませんが、混乱を招きます。

何か案は?

私が今使用している回避策は、明示的にGetRouteDataを呼び出すことです。Route

this.Request.GetRouteData().Route.GetRouteData(this.Request.RequestUri.ToString(), this.Request)

4

2 に答える 2

2

問題を報告していただきありがとうございます。https://github.com/woloski/AspNetWebApiWithSubdomainsであなたの再現を使用し、デバッグを行いました。

これが起こっている理由です。は、Web APIでHttpDomainRoute.GetRouteData呼び出される内部クラスによってラップされているため、呼び出されていません。HttpWebRouteメソッドを使用config.Routes.Addしてカスタムルートを追加すると、を呼び出す代わりに、の実装をHttpDomainRoute.GetRouteData呼び出すだけです。そのため、テナントを除く残りのパラメーターが正しくマップされていることがわかります。System.Web.Routing.Route'sGetRouteData

頭のてっぺんに簡単な回避策は考えられません。この問題を追跡するために、 http: //aspnetwebstack.codeplex.com/のcodeplexサイトで問題を報告できます。

于 2012-09-25T08:20:32.937 に答える
1

これについてもっと考えた後、私はあなたのための回避策を持っています。回避策の基本的な考え方は、Routeから派生したルートを使用し、それをRouteCollectionに直接追加することです。そうすれば、通過するルートがHttpWebRoute内にラップされなくなります。

RouteByPassingハンドラーは、別の既知の問題を回避するためのものです。お役に立てれば。

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        RouteTable.Routes.Add("test", new HttpDomainRoute(
            domain: "{tenant}.auth10.com",
            routeTemplate: "test",
            defaults: new { controller = "Values", action = "GetTenant" }
        ));

        config.MessageHandlers.Add(new RouteByPassingHandler());
    }
}

public class RouteByPassingHandler : DelegatingHandler
{
    protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        HttpMessageInvoker invoker = new HttpMessageInvoker(new HttpControllerDispatcher(request.GetConfiguration()));
        return invoker.SendAsync(request, cancellationToken);
    }
}

public class HttpDomainRoute
    : Route
{
    private Regex domainRegex;
    private Regex pathRegex;

    public HttpDomainRoute(string domain, string routeTemplate, object defaults, object constraints = null)
        : base(routeTemplate, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints), new RouteValueDictionary(), HttpControllerRouteHandler.Instance)
    {
        this.Domain = domain;
    }

    public string Domain { get; set; }

    public override RouteData GetRouteData(HttpContextBase context) 
    {   
        // Build regex
        domainRegex = CreateRegex(this.Domain);
        pathRegex = CreateRegex(this.Url);

        // Request information
        string requestDomain = context.Request.Headers["Host"];
        if (!string.IsNullOrEmpty(requestDomain))
        {
            if (requestDomain.IndexOf(":") > 0)
            {
                requestDomain = requestDomain.Substring(0, requestDomain.IndexOf(":"));
            }
        }
        else
        {
            requestDomain = context.Request.Url.Host;
        }

        string requestPath = context.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + context.Request.PathInfo;

        // Match domain and route
        Match domainMatch = domainRegex.Match(requestDomain);
        Match pathMatch = pathRegex.Match(requestPath);

        // Route data
        RouteData data = null;
        if (domainMatch.Success && pathMatch.Success)
        {
            data = base.GetRouteData(context);

            // Add defaults first
            if (Defaults != null)
            {
                foreach (KeyValuePair<string, object> item in Defaults)
                {
                    data.Values[item.Key] = item.Value;
                }
            }

            // Iterate matching domain groups
            for (int i = 1; i < domainMatch.Groups.Count; i++)
            {
                Group group = domainMatch.Groups[i];
                if (group.Success)
                {
                    string key = domainRegex.GroupNameFromNumber(i);

                    if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0))
                    {
                        if (!string.IsNullOrEmpty(group.Value))
                        {
                            data.Values[key] = group.Value;
                        }
                    }
                }
            }

            // Iterate matching path groups
            for (int i = 1; i < pathMatch.Groups.Count; i++)
            {
                Group group = pathMatch.Groups[i];
                if (group.Success)
                {
                    string key = pathRegex.GroupNameFromNumber(i);

                    if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0))
                    {
                        if (!string.IsNullOrEmpty(group.Value))
                        {
                            data.Values[key] = group.Value;
                        }
                    }
                }
            }
        }

        return data;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) 
    {
        return base.GetVirtualPath(requestContext, RemoveDomainTokens(values));
    }

    private Regex CreateRegex(string source)
    {
        // Perform replacements
        source = source.Replace("/", @"\/?");
        source = source.Replace(".", @"\.?");
        source = source.Replace("-", @"\-?");
        source = source.Replace("{", @"(?<");
        source = source.Replace("}", @">([a-zA-Z0-9_-]*))");

        return new Regex("^" + source + "$");
    }

    private RouteValueDictionary RemoveDomainTokens(RouteValueDictionary values)
    {
        Regex tokenRegex = new Regex(@"({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?");
        Match tokenMatch = tokenRegex.Match(Domain);
        for (int i = 0; i < tokenMatch.Groups.Count; i++)
        {
            Group group = tokenMatch.Groups[i];
            if (group.Success)
            {
                string key = group.Value.Replace("{", "").Replace("}", "");
                if (values.ContainsKey(key))
                    values.Remove(key);
            }
        }

        return values;
    }
}

}

于 2012-09-25T17:47:36.820 に答える