When base address contains a space requests return 404 not found

Jun 8, 2014 at 2:47 PM
Hi

I need to self host a WebApi service on a base address that includes a space character in the path. When I do this, requests return 404 not found.

I tried encoding the space as %20 but that throws an exception on start up. (The doco for the WebApp.Start method doesn't specify how the 'url' parameter should be encoded).

The following tests demonstrate the issue:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Owin.Hosting;
using System.Net.Http;
using Owin;
using System.Web.Http;
using System.Net;
using System.Collections.Generic;

namespace OwinTests
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void BaseAddressPathWithEmbeddedReturns404()
        {
            string baseAddress = "http://localhost:9000/embedded space/";

            using (WebApp.Start<Startup>(url: baseAddress))
            {
                var uri = new Uri(baseAddress + "api/values");
                var response = new HttpClient().GetAsync(uri).Result;
                // returns 404 not found
                Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
            }
        }

        [TestMethod]
        public void BaseAddressPathWithEncodedSpaceThrowsException()
        {
            string baseAddress = "http://localhost:9001/embedded%20space/";

            // throws HttpListenerException
            using (WebApp.Start<Startup>(url: baseAddress))
            {
                var uri = new Uri(baseAddress + "api/values");
                var response = new HttpClient().GetAsync(uri).Result;
                Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
            }
        }
    }
    
    public class Startup
    {
        public void Configuration(IAppBuilder appBuilder)
        {
            var config = new HttpConfiguration();
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            appBuilder.UseWebApi(config);
        }
    }

    public class ValuesController : ApiController
    {
        // GET api/values 
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }
    }
}
I have tried with v2.1, 2.2 and 3.0 beta 2 - all exhibit the same behaviour.

Thanks
Coordinator
Jun 8, 2014 at 3:07 PM
It works fine if I leave WebApi out of the picture:
    class Program
    {
        static void Main(string[] args)
        {
            string baseAddress = "http://localhost:9000/embedded space/";

            using (WebApp.Start<Startup>(url: baseAddress))
            {
                var uri = new Uri(baseAddress + "/Foo bar");
                var response = new HttpClient().GetAsync(uri).Result;
                Console.WriteLine(response);
                Console.WriteLine(response.Content.ReadAsStringAsync().Result);
            }
        }
    }

    public class Startup
    {
        public void Configuration(IAppBuilder appBuilder)
        {
            appBuilder.Run(async context =>
                { await context.Response.WriteAsync(context.Request.PathBase.Value + "; " + context.Request.Path.Value); });
        }
    }
Returns:
StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
  Transfer-Encoding: chunked
  Date: Sun, 08 Jun 2014 15:02:34 GMT
  Server: Microsoft-HTTPAPI/2.0
}
/embedded space; /Foo bar
I wonder if WebApi's treatment of spaces conflicts with that of Katana.
Aug 1, 2014 at 12:14 PM
Got the exact same problem on a self hosted Owin project with ASP NET MVC Web API. Seems to me there is a bug between OWIN and MVC Routing when matching the base address.

I got a very ugly fix, but it works as far as I know. Implement this custom MessageHandler and add it to your HttpConfiguration :
internal class FixSpaceInBaseAddressMessageHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (request.Properties.ContainsKey("MS_RequestContext"))
            {
                object owinRequestContext = request.Properties["MS_RequestContext"];
                if (owinRequestContext != null)
                { 
                    Type owinRequestContextType = owinRequestContext.GetType();
                    if (owinRequestContextType.FullName.Contains("System.Web.Http.Owin.OwinHttpRequestContext"))
                    {
                        PropertyInfo virtualPathRootProperty = owinRequestContextType.GetProperty("VirtualPathRoot");

                        string virtualPathRoot = (string)virtualPathRootProperty.GetMethod.Invoke(owinRequestContext, null);
                        if (virtualPathRoot.Contains("%20"))
                        {
                            virtualPathRootProperty.SetMethod.Invoke(owinRequestContext, new object[] { virtualPathRoot.Replace("%20", " ") });
                        }
                    }
                }
            }

            return base.SendAsync(request, cancellationToken);
        }
    }
This replaces the %20 character in an OWIN Context, and suddenly MVC gets happy with it :).
Dec 17, 2014 at 7:15 AM
That does the job, thanks!