0

Edit: I need to re-open my question because it is not a RegEx Problem! The problem resides in the forward slash. Even if the RegEx matches I won't get the desired result in my controller action. ASP.NET Core does not map path segments if they contain a forward slash. And I'm looking for a workaround in terms of a middleware, routing options, whatever exists.

I'm looking for a solution to get several path segments from the calling url within my controller action including the forward slash via RegEx constraint.

Example:

The user calls: PUT api/v1/model/elements/elementCollection1/subElement1/subSubElement2/value

Now everything between elements/ and /value shall be passed to an action parameter called elementPath in a function like...

[HttpPut("api/v1/model/elements/{elementPath}/value")]
public IActionResult PutElementValue(string elementPath, [FromBody] Value value)
{
    //Here is code
}

Do you have an idea how to RegEx and map tge match to the elementPath parameter including forward slash? Something like:

[HttpPut("api/v1/model/elements/{elementPath:regex(<Here shall be the right RegEx>)}/value")]

It's worth notice that I tried also with * catch-all parameters and it was kind of a hack but it worked for a moment. The problem is that I have another Put-Operation on api/v1/model/elements/{elementPath} without the "/value" appendix. Hence I'm forced to get the elementPath the right way. Plus I want to auto-generate a Swagger Doc from the Controller and everything inside and hacks are not parsed.

Please help and excuse me - this is my first question on StackOverflow.

considere
  • 11
  • 1
  • 1
    Apparently you don't know where to start with your regex. Please check out [Reference - What does this regex mean resource](https://stackoverflow.com/questions/22937618), and [Learning Regular Expressions](https://stackoverflow.com/questions/4736) for more info on regex. – Christian Baumann Oct 01 '20 at 07:53
  • Let's say I'm not a RegEx expert but I already got this far: `elementPath:regex((?<=elements/)(.*)(?=/value))` But not luck with ASP.NET giving me what I want – considere Oct 01 '20 at 07:55
  • 2
    The regex you used and that fails should be put into the question itself, not a comment. You should explain what you get with it, or what happens, errors, etc., and then add what you tried to fix it. Then, it will be a good question, please fix. – Wiktor Stribiżew Oct 01 '20 at 08:35
  • @WiktorStribiżew Please re-open my question because it is not a RegEx Problem! The problem resides in the forward slash. Even if the RegEx matches I won't get the desired result in my controller action. ASP.NET Core does not map path segments if they contain a forward slash. And I'm looking for a workaround in terms of a middleware, routing options, whatever exists. – considere Oct 01 '20 at 08:53
  • You removed the regex tag, so I cannot reopen with a single click. Once three users check your question and agree it is a unique question, the question will get reopened. – Wiktor Stribiżew Oct 01 '20 at 09:21

2 Answers2

0

From the docs:

You can use an asterisk (*) or double asterisk (**) as a prefix to a route parameter to bind to the rest of the URI. These are called a catch-all parameters. For example, blog/{**slug} matches any URI that starts with /blog and has any value following it, which is assigned to the slug route value. Catch-all parameters can also match the empty string.

The catch-all parameter escapes the appropriate characters when the route is used to generate a URL, including path separator (/) characters. For example, the route foo/{*path} with route values { path = "my/path" } generates foo/my%2Fpath. Note the escaped forward slash. To round-trip path separator characters, use the ** route parameter prefix. The route foo/{**path} with { path = "my/path" } generates foo/my/path.

Change your code like below:

[HttpPut("api/v1/model/elements/{**value}")]
public void PutElementValue(string value, [FromBody] string data)
{
    value = value.Replace("/value", "");
}

Result: enter image description here

Rena
  • 14,556
  • 3
  • 15
  • 44
  • Thank you for your reply. I've been there but it doesn't meet my requirements entirely. I need to be able to have other postfixes instead of /value, e.g. /invoke. Additionally, I need a PUT (without /value) request to create the element but independent of its value. – considere Oct 02 '20 at 12:49
0

I found another solution, i.e. create and register my own middleware with an URL-encoded element path. That overcomes the issue not mapping the forward slash to any route template.

Solution:

app.Use((context, next) =>
            {
                string requestPath = context.Request.Path.ToUriComponent();
                
                if (requestPath.Contains("elements/"))
                {
                    Match valueMatch = Regex.Match(requestPath, "(?<=elements/)(.*)(?=/value|/invoke)");
                    if(valueMatch.Success)
                    {
                        string elementPath = HttpUtility.UrlEncode(valueMatch.Value);
                        requestPath = requestPath.Replace(valueMatch.Value, elementPath);
                        context.Request.Path = new PathString(requestPath);
                    }
                    else
                    {
                        Match baseMatch = Regex.Match(requestPath, "(?<=elements/)(.*)");
                        if(baseMatch.Success)
                        {
                            string elementPath = HttpUtility.UrlEncode(baseMatch.Value);
                            requestPath = requestPath.Replace(baseMatch.Value, elementPath);
                            context.Request.Path = new PathString(requestPath);
                        }
                    }
                }
                return next();
            });

It works but still it feels like a mediocre hack. If you have any idea how to do it in another wa - RegEx in the Action-Route-Template is still favored - please let me know ;-)

considere
  • 11
  • 1