Search code examples
asp.netmono

URL path with Unicode characters rendered with ?? in Mono ASP.NET


I'm porting an old ASP.NET web application from Azure to Mono running on Docker on a Raspberry Pi 4 with Nginx as the web server. Part of this application creates a downloadable file in .PS1 format. The user selects the appropriate settings in the web GUI, which includes city names from around the world. The city name forms part of the URL path to the downloadable file, which also includes the city name. Sometimes, these city names have Unicode/non-ASCII characters in them. The application has no problem displaying these Unicode/non-ASCII characters in the GUI, and the resulting folder/file combination in the filesystem renders the city name with all non-ASCII characters intact. The full URL is rendered properly for the user to download.

The problem occurs when the user actually clicks the download link. Instead of downloading the file, I get a 404 error from Mono indicating the file does not exist. The "Requested URL" shows ?? in place of any Unicode/non-ASCII characters, which is why it thinks the file does not exist. If the file does not include Unicode/non-ASCII characters, everything works fine.

Example:
Posted URL: https://mywebsite.com/files/V%C3%A4ster%C3%A5s/V%C3%A4ster%C3%A5s.ps1
URL in error message: /files/V??ster??s/V??ster??s.ps1

The name of the sample city doesn't seem to render properly once posted to StackOverflow, but its "Västerås".

Everything I've found indicates that any changes are to be done to the web.config file. I've tried everything I can think of inside of the web.config, but nothing seems to be working. I've worked so hard porting this app over, and this is one of the final bits that I can't seem to figure out. Hopefully, someone can help a flailing dude out. This is all working fine with the version of the webapp that's running in Azure as an Azure Webapp.

Mono Docker Image: mono:slim (pulled Aug 23, 2022)
Version Information: 6.12.0.182 (tarball Tue Jun 14 22:39:59 UTC 2022)
ASP.NET Version: 4.0.30319.42000

start_server script that I use on container startup

service nginx start
fastcgi-mono-server4 /applications=/:/var/www/,/api/:/var/www
 /socket=tcp:9000

/etc/nginx/sites-enabled/default

server {
    location / {
            fastcgi_index default.cshtml;
            fastcgi_pass 127.0.0.1:9000;
            include /etc/nginx/fastcgi_params;
    }

    location /api {
            fastcgi_index process.aspx;
            fastcgi_pass 127.0.0.1:9000;
            include /etc/nginx/fastcgi_params;
    }
}

web.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <globalization fileEncoding="utf-8" requestEncoding="utf-8" responseEncoding="utf-8" />
    <httpRuntime targetFramework="4.8" requestValidationMode="2.0" />
    <pages>
      <namespaces>
        <add namespace="System" />
        <add namespace="System.Xml" />
        <add namespace="System.Xml.XPath" />
        <add namespace="System.IO" />
        <add namespace="System.Text" />
        <add namespace="System.Text.RegularExpressions" />
        <add namespace="System.Collections.ObjectModel" />
        <add namespace="System.Collections.Generic" />
        <add namespace="System.Collections.Specialized" />
        <add namespace="System.Configuration" />
        <add namespace="MySql.Data.MySqlClient" />
        <add namespace="MySql.Data" />
      </namespaces>
    </pages>
    <sessionState timeout="60" />
    <customErrors mode="Off" />
  </system.web>
  <system.webServer>
    <staticContent>
      <mimeMap fileExtension=".ps1" mimeType="file/download" />
      <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="01:00:00" />
    </staticContent>
    <urlCompression doDynamicCompression="true" />
  </system.webServer>
  <system.data>
    <DbProviderFactories>
        <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory,MySql.Data" />
    </DbProviderFactories>
  </system.data>
  <connectionStrings>
    <add connectionString="server=mydbserver.com;database=mydatabase;uid=dbreaderguy;pwd=SuperSecurePassword; Convert Zero Datetime=True" name="mydatabase" providerName="MySql.Data.MySqlClient" />
  </connectionStrings>
  <appSettings>
    <add key="webPages:Version" value="3.0" />
  </appSettings>
   <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cshtml" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=3.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701" />
      <compiler extension=".cs" language="c#;cs;csharp" warningLevel="4" compilerOptions="/langversion:7.3 /nowarn:1659;1699;1701;612;618" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=3.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      <compiler extension=".vb" language="vb;vbs;visualbasic;vbscript" warningLevel="4" compilerOptions="/langversion:default /nowarn:41008,40000,40008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=3.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </compilers>
  </system.codedom>
</configuration>


Solution

  • Of course, not long after I posted this question, I managed to figure out my answer on my own. Rather than defaulting to having Mono/ASP.NET handle requests to the files in the files folder, I just modified my /etc/nginx/sites-enabled/default by adding a root /var/www; line at the top and an empty location pointing to the files subfolder. This appears to make Nginx handle the files without the Mono layer:

    server {
        root /var/www;
    
        location / {
                fastcgi_index default.cshtml;
                fastcgi_pass 127.0.0.1:9000;
                include /etc/nginx/fastcgi_params;
        }
    
        location /files {
        }
    
        location /api {
                fastcgi_index process.aspx;
                fastcgi_pass 127.0.0.1:9000;
                include /etc/nginx/fastcgi_params;
        }
    }