.NET Core,如何处理带有额外前导斜杠的路由

5

我需要处理一个形如以下请求:

//ohif/study/1.1/series

注意前面的额外斜杠

我的控制器签名是:

[Route("ohif/study/{studyUid}/series")]
[HttpGet]
public IActionResult GetStudy(string studyUid)

如果我修改传入的请求为/ohif/study/1.1/series,它就可以正常工作。但是当我使用//ohif/study/1.1/series时,路由就无法匹配了。另外,我也尝试过[Route("/ohif/study/{studyUid}/series")]和[Route("//ohif/study/{studyUid}/series")],但两者都失败了。不幸的是,我不能更改传入的请求,因为它来自外部应用程序。有没有什么技巧可以处理这个路由?我正在使用.NET Core 3.0。更新:我已经激活了日志记录,并且看到asp.net core正在分析路由,我收到了以下消息:“Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware”的请求路径'//ohif/study/1.1/series'没有找到候选项。

也许你可以尝试重写控制器的OnActionExecuting方法,并在那里应用一些逻辑。 - Muhammad Kamran Aslam
你觉得能否用正则表达式来提取它? - Felix Castor
Route("ohif/study/{studyUid:regex(^(\\d*\\.)?\\d+(?!.\\\\series))}") 可能是这样的。我无法测试它。 - Felix Castor
@FelixCastor 那个正则表达式处理了末尾的斜杠,问题实际上是在URL开头有一个多余的斜杠"/",我会尝试一些正则表达式并提供反馈。 - shelbypereira
噢,是的,我以为末尾的斜杠是问题所在... - Felix Castor
4个回答

9

中间件如何处理双斜杠?

app.Use((context, next) =>
            {
                if (context.Request.Path.Value.StartsWith("//"))
                {
                    context.Request.Path = new PathString(context.Request.Path.Value.Replace("//", "/"));
                }
                return next();
            });

当我在微软文档中查找这个中间件方法时,他们使用了异步方式。app.Use(async (context, next) => { var path = context.Request.Path.Value; /* do something */ await next.Invoke(); }); - StackOverflowUser
在 .Net 6 中,app.UseRouting() 可以是隐式的,如果是这样的话,它将位于管道的开头,因此似乎不起作用。请在上述代码后添加显式的 UseRouting() 调用。更多详细信息请参见 https://dev59.com/l1MH5IYBdhLWcg3w3Efb#59966192。 - LMK

4

在Web服务器级别重写URL,例如对于IIS,您可以使用URL Rewrite模块将//ohif/study/1.1/series自动重定向到/ohif/study/1.1/series。这不是应用程序的工作。


我认为这是正确的方法,我还发现了一个快速解决方法,使用路由约束和一个 catchall 路由,如此处所述 https://dev59.com/5n7aa4cB1Zd3GeqPlAqS - shelbypereira
2
当你迁移应用程序到其他地方并且忘记时,这将会变得有趣。 - LMK

2

我采用了Ravi的答案并完善了一个中间件。中间件非常好,因为它是封装的,易于测试,可以注入日志记录器,更易读等。

app.UseDoubleSlashHandler();

代码和测试:
public class DoubleSlashMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<DoubleSlashMiddleware> _logger;

    public DoubleSlashMiddleware(RequestDelegate next, ILogger<DoubleSlashMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        _logger.LogInformation($"Invoking {nameof(DoubleSlashMiddleware)} on {context.Request.Path}");

        context.Request.Path = context.Request.Path.FixDoubleSlashes();

        // Call the next delegate/middleware in the pipeline.
        await _next(context);
    }
}

public static class DoubleSlashMiddlewareExtensions
{
    public static IApplicationBuilder UseDoubleSlashHandler(
        this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<DoubleSlashMiddleware>();
    }
}

[TestClass()]
public class DoubleSlashMiddlewareTests
{
    private DoubleSlashMiddleware _sut;
    private ILogger<DoubleSlashMiddleware> _logger;
    private bool _calledNextMiddlewareInPipeline;

    [TestInitialize()]
    public void TestInitialize()
    {
        _logger = Substitute.For<ILogger<DoubleSlashMiddleware>>();
        Task Next(HttpContext _)
        {
            _calledNextMiddlewareInPipeline = true;
            return Task.CompletedTask;
        }
        _sut = new DoubleSlashMiddleware(Next, _logger);
    }

    [TestMethod()]
    public async Task InvokeAsync()
    {
        // Arrange
        _calledNextMiddlewareInPipeline = false;

        // Act
        await _sut.InvokeAsync(new DefaultHttpContext());

        // Assert
        _logger.ReceivedWithAnyArgs(1).LogInformation(null);
        Assert.IsTrue(_calledNextMiddlewareInPipeline);
    }
}

用于替换的字符串方法:

public static class RoutingHelper
{
    public static PathString FixDoubleSlashes(this PathString path)
    {
        if (string.IsNullOrWhiteSpace(path.Value))
        {
            return path;
        }
        if (path.Value.Contains("//"))
        {
            return new PathString(path.Value.Replace("//", "/"));
        }
        return path;
    }
}

[TestClass()]
public class RoutingHelperTests
{
    [TestMethod()]
    [DataRow(null, null)]
    [DataRow("", "")]
    [DataRow("/connect/token", "/connect/token")]
    [DataRow("//connect/token", "/connect/token")]
    [DataRow("/connect//token", "/connect/token")]
    [DataRow("//connect//token", "/connect/token")]
    [DataRow("/connect///token", "/connect/token")]
    public void FixDoubleSlashes(string input, string expected)
    {
        // Arrange
        var path = new PathString(input);

        // Act
        var actual = path.FixDoubleSlashes();

        // Assert
        Assert.AreEqual(expected, actual.Value);
    }
}

这确实是更多的代码,但你真的想要在每个请求执行的未经测试的代码吗?(没有日志记录);) - Jess
1
这里是另一个测试用例:[DataRow(“/connect///token”,“/connect/token”)] - Christof Wollenhaupt

0

这个SO答案对于一个类似的问题描述了另一个更简单的解决方案:使用内置的URL重写中间件。例如,可以通过以下方式将双斜杠替换为单斜杠:

var options = new RewriteOptions().AddRewrite(@"(.*)\/\/(.*)", "$1/$2", false);
app.UseRewriter(options);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接