webdis: Inconsistent behavior between GET / POST
Hi @nicolasff,
Hope all is well. I’ve been making progress on my project over here and I’ve started to run into some issues.
Sepcifically with inconsistencies with GET / POST and some of the args I’m trying to pass.
Issue 1
This GET works
The same POST does not.
I assume both of these requests should produce the same results?
Issue 2
While attempting to use this redis command with a GET: http://127.0.0.1:7379/JSON.SET/My.DataModel.Redis.TmDb.TmResult%3A01H0QTRN3VY3PBN5C1FJG4H75H/./{"Id"%3A"01H0QTRN3VY3PBN5C1FJG4H75H"%2C"RiskScore"%3A0%2C"RiskScoreMin"%3A0%2C"TestScoreMin"%3A0%2C"TestScoreMax"%3A0}
Postman has no issue, but inside my dotNet environment, something is interperting the ‘./’ as a root or something and removing it before the requests goes out so I get {"JSON.SET":[false,"ERR wrong number of arguments for 'JSON.SET' command"]}
However I can POST it no problem and get back "{\"JSON.SET\":[true,\"OK\"]}"
So here’s my issue. Some commands only GET works, and others only POST works.
I don’t really want to use different verbs based on the command, as the commands could change going forward and I really want this to be pass-through as much as possible, so I would prefer to use a single verb POST to handle all requests.
Hopefully this is an easy fix?
Thoughts?
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 17 (8 by maintainers)
Great! I’m glad it worked for you too, I was worried the internal field names would be different on .NET for macOS for whatever reason – as long as they’re not public we can’t really assume much.
It was interesting to have a quick look at modern .NET and see how much it has changed since I last used it seriously in 2006-2007. I had actually tried to do this around the time of my earlier message identifying the issue as coming from the HTTP client, and went down the wrong path flipping the bit for a different flag which made no difference for the
/./use case. Good thing I gave it a second try 😃By the way, I learned that even my tool of choice for crafting HTTP requests – the ubiquitous
curl– simplifies request URIs by default unless it’s run with--path-as-is.I think we can close this issue now, the subject has drifted quite a bit but as far as I can tell all the weird behaviors with Postman and the .NET HTTP client have been explained and workarounds were found for both.
Above and beyond bro! I was hitting dead ends because it was so embedded into the .NET, and as you pointed out .NET 7 is a different beast. I have tested this and it works well. Thank you!
Okay, I think I have a solution for you. Please read carefully.
There are many threads online about how to prevent the .NET URI parser from simplifying URIs in this manner. This is sometimes referred to as “normalizing”, sometimes “canonicalizing”. It is done by the
UriParserclass in .NET, based on behavior flags provided to it in its constructor.If you go to
System.UriSyntaxFlags, you’ll find a list of these flags. They are “OR’d” together as binary flags, and separate instances ofUriParserare available by default for various protocols. If you go to the code forSystem.UriParser, you’ll find them:There are many more. Note how
HttpSyntaxFlagsis passed to the"http"parser. It is defined as:These flags – and especially
CompressPath– control the behavior of theUriParserinstance for HTTP URIs. They are themselves part of an enum, once again we can look it up and find the values (truncated here for brevity):All of this is private and not externally configurable, except… if we use reflection. I wrote that there are lots of threads about it online, and they all bring up some version of this technique. For example:
The problem with all of these is that they are only valid for old versions of .NET, and they no longer work. None of them do. Look at the dates: 2010, 2011, 2012… this is code that modifies the internal fields of
UriParserby name, so if they were renamed at some point or if the flags were changed, it’s not going to work. And that’s exactly what happened over time.What we need to do is this:
"http"and"https", get the dedicatedUriParserobject_flagsfield of theUriParserclass (it’s not longerm_Flagslike in 2010)UriParserobject we got aboveintNOT(the value)(see here for details)_flagsfield of our currentUriParserobjectI tried doing all this and it seems to work well. I do see requests being sent to Webdis with all their
/./././still present when I first make these changes.Here’s a screenshot of my Rider debugger showing the structure or
uriParser(theUriParserinstance for `“http” in this case):uriParserobjectFlagsgetter_flagsfield that’s returned by theFlagsgetterThere are more values than fit on the screen, this is the full list:
Here is the method to make these changes, call it at the start of your program:
I’ve also written a test program that does this:
DoNotSimplifyUris()/SET/foo/bar/GET/foo/bar/GET/././././foo– I see it unchanged in the Webdis logs/GET/././././foo, but this time using aUriobject instead of astring– again, I see it unchanged in the Webdis logsHere is the full program: https://gist.github.com/nicolasff/5de66eda4264a27637aebe2e767e4abf
This is its output:
and if I comment the initial call to
DoNotSimplifyUris():^ clearly the
/GET/././././fooget simplified to/GET/foo.It would be nice if Microsoft made this configurable, but as you can see it’s been at least 12-13 years and they themselves have suggested this reflection hack on .NET support forums so it doesn’t look like a cleaner solution is coming any time soon. Note that this is a .NET 7 version, I don’t know if it would work with other versions and there’s no guarantee it’ll continue to work. I thought it was worth posting here since I couldn’t find a version of it for recent releases of .NET.
Let me know what you think!