分支执行后如何return到管道根?

How to return to pipeline root after branch executing?

在 OWIN 管道中,我使用分支来配置自定义身份验证中间件。分支执行后如何return管道根?

app.Use<AuthenticationMiddleware1>();
app.Map("/branch", (application) => {
    application.Use<AuthenticationMiddleware2>();
});
app.UseWebApi(new HttpConfiguration());

当我请求 http://server/branch 时,web api 未配置并且 return 404

我试着写了一个 MapAndContinueMiddleware:

public class MapAndContinueMiddleware:OwinMiddleware
{
    public MapAndContinueMiddleware(OwinMiddleware next, MapOptions options) : base(next)
    {
        this.Options = options;
    }

    public MapOptions Options { get; }


    public async override Task Invoke(IOwinContext context)
    {
        if(context.Request.Path.StartsWithSegments(this.Options.PathMatch))
        {
            await this.Options.Branch(context).ContinueWith((previousTask) =>
            {
                this.Next.Invoke(context);
            });
        }
        else
        {
            await this.Next.Invoke(context);
        }
    }
}

使用此扩展名:

public static IAppBuilder MapAndContinue(this IAppBuilder app, string pathMatch, Action<IAppBuilder> configuration)
{
    // create branch and assign to options
    IAppBuilder branch = app.New();
    configuration(branch);

    MapOptions options = new MapOptions {
        PathMatch = new PathString(pathMatch),
        Branch = (Func<IOwinContext, Task>)branch.Build(typeof(Func<IOwinContext, Task>))
    };
    return MapAndContinue(app, options);
}

public static IAppBuilder MapAndContinue(this IAppBuilder app, MapOptions options)
{
    return app.Use<MapAndContinueMiddleware>(options);
}

但这有一个奇怪的行为:网络 api 两次请求 运行 分支并且不 return 客户端...!?

您是否尝试过在分支后按照配置继续管道

var config = new HttpConfiguration();
app.Use<AuthenticationMiddleware1>();
app.Map("/branch", (application) => {
    application.Use<AuthenticationMiddleware2>();
    application.UseWebApi(config);
});
app.UseWebApi(config);

这样在分支之后它仍然可以使用 Web API

查看 Original MapExtension Source 似乎将中间件添加到管道的顺序很重要

查看以下重构以使用您的自定义地图中间件

using AppFunc = Func<IDictionary<string, object>, Task>;

//...

public static class BranchAndMergeExtensions {

    public static IAppBuilder MapAndContinue(this IAppBuilder app, string pathMatch, Action<IAppBuilder> configuration) {
        return MapAndContinue(app, new PathString(pathMatch), configuration);
    }

    public static IAppBuilder MapAndContinue(this IAppBuilder app, PathString pathMatch, Action<IAppBuilder> configuration) {
        if (app == null) {
            throw new ArgumentNullException("app");
        }
        if (configuration == null) {
            throw new ArgumentNullException("configuration");
        }
        if (pathMatch.HasValue && pathMatch.Value.EndsWith("/", StringComparison.Ordinal)) {
            throw new ArgumentException("Path must not end with slash '/'", "pathMatch");
        }

        // put middleware in pipeline before creating branch
        var options = new MapOptions { PathMatch = pathMatch };
        var result = app.Use<MapAndContinueMiddleware>(options);

        // create branch and assign to options
        IAppBuilder branch = app.New();
        configuration(branch);
        options.Branch = (AppFunc)branch.Build(typeof(AppFunc));

        return result;
    }
}

original MapMiddleware 也需要重构以阻止它通过在分支之后调用根管道来使管道短路。

public class MapAndContinueMiddleware : OwinMiddleware {
    private readonly MapOptions options;

    public MapAndContinueMiddleware(OwinMiddleware next, MapOptions options)
        : base(next) {
        this.options = options;
    }

    public async override Task Invoke(IOwinContext context) {
        PathString path = context.Request.Path;
        PathString remainingPath;
        if (path.StartsWithSegments(options.PathMatch, out remainingPath)) {
            // Update the path
            PathString pathBase = context.Request.PathBase;
            context.Request.PathBase = pathBase + options.PathMatch;
            context.Request.Path = remainingPath;

            //call branch delegate
            await this.options.Branch(context.Environment);

            context.Request.PathBase = pathBase;
            context.Request.Path = path;
        }
        // call next delegate
        await this.Next.Invoke(context);
    }
}

最终导致您的原始设置示例变为

var config = new HttpConfiguration();
app.Use<AuthenticationMiddleware1>();
app.MapAndContinue("/branch", (application) => {
    application.Use<AuthenticationMiddleware2>();
});
app.UseWebApi(config);