Automatic UrlDecode GET request paremeters

So I’m running into an issue where I find myself needing to UrlDecode() parameters on my ServiceModel that come in from a GET request (i.e. over the URL).

My partner thought this should be handled for us by Service Stack automatically. Is there a config switch or something to toggle to enable Automatic UrlDecoding of GET parameters?

I.E. I have a model with a string parameter that in tests works no problem. When hitting the endpoint with a JsonServiceClient, the parameter gets Url-encoded as the request is made. This causes the back-end service model to show up as UrlEncoded text, which returns zero results because it can’t find items based on a URL-encoded parameter. Once I manually UrlDecode the property, all is fine again. I’m just not sure if I should be handling this manually, or does Service Stack have an option to do this for me?

QueryString parameters are automatically be decoded, please provide the code example + raw HTTP Request Headers showing the issue.

This is from Fiddler:

GET https://dev.io/reminder-api/reminders/byprofile/ern%3A%3ACloser2U%3A%3AUserprofile%3A%3A1c7e9ead-c7d9-46f8-a0cc-2777c4373ac4/2018-06-01 HTTP/1.1
Connection: Keep-Alive
Accept: application/json
Accept-Encoding: gzip, deflate
User-Agent: ServiceStack .NET Client 5.11
Host: dev.io

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 16
Connection: keep-alive
Date: Wed, 09 May 2018 17:25:19 GMT
x-amzn-RequestId: f21092d3-53ad-11e8-b82c-5d96452a2f24
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
set-cookie: ss-id=ZLJonZirJVm1QomA6uzF; path=/; samesite=lax; httponly
set-cookie: ss-pid=16HmoXJvxJP18yQN7dsd; expires=Sun, 09 May 2038 17:25:19 GMT; path=/; samesite=lax; 
httponly
x-amz-apigw-id: GoRH2FKWCYcF3hg=
Vary: Accept
X-Powered-By: ServiceStack/5.11 NETStandard/.NET
Access-Control-Allow-Methods: *
X-Amzn-Trace-Id: Root=1-5af32eff-4748d747fb8c690594512cf7
Access-Control-Allow-Credentials: true
X-Cache: Miss from cloudfront
Via: 1.1 abfc920f60e32b50b36ecc54c5a19cf4.cloudfront.net (CloudFront)
X-Amz-Cf-Id: 7F_g6F5zSvMA4CShBqYfZQynU5kx1D1rC761PsvcrRain1CfJgP-TA==

My ServiceModel’s parameter has: “ern%3A%3ACloser2U%3A%3AUserprofile%3A%3A1c7e9ead-c7d9-46f8-a0cc-2777c4373ac4”

yet I am expecting : “ern::Closer2U::Userprofile::1c7e9ead-c7d9-46f8-a0cc-2777c4373ac4”

This request fails in ASP.NET by default because : is a potentially malicious character for a HTTP Request so it’s rejected by ASP.NET:

http://test.servicestack.net/custom/ern%3A%3ACloser2U%3A%3AUserprofile%3A%3A1c7e9ead-c7d9-46f8-a0cc-2777c4373ac4

But it will pass ASP.NET’s request validation if it’s on the ?QueryString instead:

http://test.servicestack.net/custom?data=ern%3A%3ACloser2U%3A%3AUserprofile%3A%3A1c7e9ead-c7d9-46f8-a0cc-2777c4373ac4

But this test works in both HttpListener and .NET Core Hosts:

[Test]
public void Does_URL_Decode_PathInfo()
{
    var client = new JsonServiceClient(Config.HostNameBaseUrl);

    var pathInfo = "ern::Closer2U::Userprofile::1c7e9ead-c7d9-46f8-a0cc-2777c4373ac4";
    var response = client.Get(new CustomRoute {
        Data = pathInfo 
    });
    
    Assert.That(response.Data, Is.EqualTo(pathInfo));
}

Where CustomRoute is:

[Route("/custom")]
[Route("/custom/{Data}")]
public class CustomRoute : IReturn<CustomRoute>
{
    public string Data { get; set; }
}

public class MyServices : Service
{
    public object Any(CustomRoute request) => request;
}

The expected raw HTTP Request and Response Headers were sent:

GET http://desktop-bcs76j0:20000/custom/ern%3A%3ACloser2U%3A%3AUserprofile%3A%3A1c7e9ead-c7d9-46f8-a0cc-2777c4373ac4 HTTP/1.1
Accept: application/json
User-Agent: ServiceStack .NET Client 5.00
Accept-Encoding: gzip,deflate
Host: desktop-bcs76j0:20000
Connection: Keep-Alive

HTTP Response:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Vary: Accept
Server: Microsoft-HTTPAPI/2.0
X-Powered-By: ServiceStack/5.00 NET45 Win32NT/.NET
Date: Wed, 09 May 2018 18:27:22 GMT
Content-Length: 75

{"Data":"ern::Closer2U::Userprofile::1c7e9ead-c7d9-46f8-a0cc-2777c4373ac4"}

I’m not sure what’s causing your issue, but I’d first try calling the Service locally without going through an AWS proxy in-case that’s causing double-encoding.

Note: the /path/info segment of the URL is part of the URL identifier which is normally a human readable string. It’s not normal to require URL encoding on the path info. instead you can use the ?queryString for any data params. I’d especially avoid using : on the path info which can be flagged as a potentially malicious request.