有没有办法在 ASP Net/ASP Net Core 中处理 URL 路径段中的参数

问题描述

最近我阅读了关于 URL 及其部分以及标准中定义的内容article。 关于参数有一个有趣的部分。通常,您通过以下方式之一传递参数:

  • 在 URL 路径中作为一个段;
  • 在 URL 查询字符串中;
  • 在请求正文中;
  • 在请求头中;

但是,根据文章还有另一种方法 - 在 URL 路径段中传递参数,用分号将它们与段分开:

parameters - 谈到参数,这些也可以出现在路径之后但在查询字符串之前,也可以与 URL 的其余部分分开,并且彼此之间用 ;字符,例如:

http://www.blah.com/some/crazy/path.html;param1=foo;param2=bar

我以前从未遇到过这种方法文章中提到它很少使用。 .NET 是否支持,尤其是 ASP.NET 或 ASP.NET Core?

解决方法

我真的不在乎查询参数的那种奇怪的格式是否是标准的。如果您自己尝试,您会发现 ASP.NET Core 不支持该格式。它仍然被认为是一个段,一旦被解析,如果它没有被任何路由模式匹配,那么响应就是 404

为了支持那种奇怪的格式(我在你的问题中谈论的是原始格式,而不是你评论中的另一种更奇怪的格式),理论上你可以使用自定义 QueryStringValueProviderFactory。默认的从 QueryStringValueProvider 创建 Request.Query。在您的自定义参数中,您可以从您自己的一组参数创建一个 QueryStringValueProvider,这些参数可以从原始请求 URL 中解析出来。然而,这种方式并不那么容易,因为您的请求在模型绑定阶段(值提供者用于构建请求模型以及操作参数)为时已晚。因为为时已晚,您的请求路径甚至与任何路由模式都不匹配,因此管道将被短路,响应为 404

从技术上讲,要遵循这种方法,您需要先以某种方式使其进入模型绑定阶段(意味着使请求工作,就好像分号分隔的查询参数不存在一样)。我认为可以删除路由过程中的最后一段(如果它包含任何分号)。然而这当然并不容易。那种方式简直太复杂了。

在这里,我想介绍另一种更简单(且有效)的方法。我们可以使用中间件来解析 URL 并在请求进入 MVC 管道之前自己构建查询字符串(如果有的话,可以附加到现有的标准查询字符串)。

就这么简单:

//an extension method for conveniently registering your middleware later 
public static class ComplexQueryStringMiddlewareExtensions
{
    public static IApplicationBuilder UseComplexQueryStringMiddleware(this IApplicationBuilder appBuilder)
    {
        return appBuilder.Use((context,next) => {
            var path = context.Request.Path;
            var semicolonSepParameters = path.Value.Split(';');
            //the first part is always the correct path
            context.Request.Path = semicolonSepParameters[0];
            semicolonSepParameters = semicolonSepParameters.Skip(1).Where(e => !string.IsNullOrWhiteSpace(e)).ToArray();
            if (semicolonSepParameters.Length > 0)
            {
                var appendedQueryString = string.Join("&",semicolonSepParameters);
                //in case there is some standard query string as well
                if (context.Request.Query != null && context.Request.Query.Count > 0)
                {
                    appendedQueryString = context.Request.QueryString + "&" + appendedQueryString;
                } else
                {
                    appendedQueryString = "?" + appendedQueryString;
                }
                context.Request.QueryString = new Microsoft.AspNetCore.Http.QueryString(appendedQueryString);
            }
            return next();
        });
    }
}

现在在 Startup.Configure 方法中,确保您的中间件注册放在 UseRouting(如果有)之前:

app.UseComplexQueryStringMiddleware();
//if this exists (which is usually by the default generated code)
//this must be after the above
app.UseRouting();
//of course the UseEndpoints is always at the end

现在您的 http://www.blah.com/some/crazy/path.html;param1=foo;param2=bar 应该像 http://www.blah.com/some/crazy/path.html?param1=foo&param2=bar 一样工作。您甚至可以将这 2 种格式混合在一起,例如 http://www.blah.com/some/crazy/path.html;param1=foo;param2=bar?param3=ok&param4=yes