HowTo: Linux, Nginx, Mono, Asp.Net Mvc

These are notes I made for getting an application written for Asp.Net Mvc (It was probably Mvc 3-ish) to run on a Centos server.

Note that some of the issues/solutions may vary across Linux variants. The interface Nginx-Mono is FastCGI, which they both support. The Mono webserver is xsp4.

Of course, running cross platform is all much easier these days if you use .Net Core and Kestrel 🙂 And een for Net Framework running on Mono, the work done since Microsoft bought Xamarin has been really helpful for e.g. Mvc compatibility.

The Reading List

The Checklist

Ensure nginx has read execute permissions on your application directory and all parent directories

nginx conf section for webapp

With the Asp.Net website listening on port 9001

#
server {
listen 8080;
server_name your-public-facing-nginx-server-name.com;

# use /smoketest/ to confirm that nginx is reading this config 
# and has read/execute access to the 
# /usr/share/nginx directories and files:

location /smoketest/ {
root /usr/share/nginx/smoketestredir;
index index.html;
}

# pass XSP to FastCGI server listening on 127.0.0.1:9001
# use a different port for each ASP.NET site you create
# (port 9000 is often taken by PHP on default webserver setups)

location / {

# replace fastcgi with this to confirm nginx read address
# to files in root /usr/share/nginx/mywebapplication;
fastcgi_pass  127.0.0.1:9001;
fastcgi_index index.html;

include /etc/nginx/fastcgi_params;
fastcgi_param  PATH_INFO          "";
fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;

fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
}
}

Systemd Startup Script

vim /etc/systemd/system/fastcgi-mono-hdmbreas.service

[Unit]
Description=FastCgi-mono-server4 for Application
After=syslog.target

[Service]
Type=simple
ExecStart=/bin/fastcgi-mono-server4 --applications /:/usr/share/nginx/hdmbreas/hdm-fe-web --socket=tcp:127.0.0.1:9001
Restart=always

systemctl daemon-reload
systemctl start fastcgi-mono-app.service
systemctl status fastcgi-mono-app.service
systemctl status fastcgi-mono-app.service

Error: [error] 7216#0: *119 upstream sent too big header while reading response header

  • Considering setting the buffer size
  • Considering setting the busy buffer size
  • e.g. nginx config: fastcgi_buffers 16 16k; fastcgi_buffer_size 32k;

Watching Errors

Watch errors with tail -F /var/log/nginx/error.log

Adding your Outlook.com account to MacOs as an Exchange account.

Is simple when you know how. Possibly harder if, like me, your outlook login is not an outlook.com email address, but your own domain.

System Preferences -> Add Internet Account -> Choose the Big Exchange Button

MacOs System Preferences with “Internet Accounts” Highlighted
MacOs System Preferences with “Internet Accounts” Highlighted

First, get an app-password from your outlook.com account
Then note the Exchange server URL: https://outlook.office365.com/EWS/Exchange.asmx
Then, try to add your account just by typing in your email address and the app-password you got.
If that doesn’t work, and you get the “Unable to verify account name or password” paste the Exchange server URL into the two boxes for Internal URL and External URL:

MacOs Mojave Add an Outlook.com account
MacOs Mojave Add an Outlook.com account

And that works for me™ on MacOs Mojave in 2019.

PowerShell String.Split() Off-by-Method-Overload Error

This seemed to me an error and I and was on the point of raising it as a bug on the Powershell github repo:

PS> "\this".Split( [char]'\', [StringSplitOptions]::RemoveEmptyEntries).Length
# >> 2

Presumably it is because [StringSplitOptions]::RemoveEmptyEntries is coerced to a [char] and so the line is parsed as:

PS> "\this".Split( ([char]'\', [StringSplitOptions]::RemoveEmptyEntries) ).Length

Instead of as

PS> \this".Split( (,[char]'\'), [StringSplitOptions]::RemoveEmptyEntries).Length

If the first parameter is a string not a character then it works as expected:

PS> "\this".Split( '\', [StringSplitOptions]::RemoveEmptyEntries).Length
# >> 1

But the really unfortunate case is :

PS> "\this".Split( [System.IO.Path]::DirectorySeparatorChar, [StringSplitOptions]::RemoveEmptyEntries).Length
# >> 2

which results in

PS> "\this".Split( [System.IO.Path]::DirectorySeparatorChar, [StringSplitOptions]::RemoveEmptyEntries).[0]
# >> $null
# instead of
# >> "this"

It turns out that it’s fixed in Powershell 6 Beta; or to be more precise, it doesn’t happen in PowerShell 6. What changed is that the underlying .Net framework has added new overloads to String.Split():

string[] Split(char separator, System.StringSplitOptions options)                                                                                    
string[] Split(char separator, int count, System.StringSplitOptions options)                                                                         
string[] Split(string separator, System.StringSplitOptions options)                                                                                  
string[] Split(string separator, int count, System.StringSplitOptions options)                                                                       

Whereas PowerShell 5 only has these overloads available:

string[] Split(Params char[] separator)                                                                                                              
string[] Split(char[] separator, int count)                                                                                                          
string[] Split(char[] separator, System.StringSplitOptions options)                                                                                  
string[] Split(char[] separator, int count, System.StringSplitOptions options)                                                                       
string[] Split(string[] separator, System.StringSplitOptions options)                                                                                
string[] Split(string[] separator, int count, System.StringSplitOptions options)                                                                     

And so the best-match overload that PowerShell 6 chooses is different to PowerShell 5’s best match.

Using Windows keystrokes on a Mac

Alas. I’ve been using Windows so long that now I go back to the Mac and want Ctrl-C to work. Sad, but there it is. At last I found Karabiner, which does keyboard remapping, and created a set of Karabiner rules to map the main Windows Ctrl-keystrokes to the matching ⌘-keystroke on Mac:

Karabiner Rules for main Windows Ctrl-keystrokes on MacOs

Okay, so Ctrl-W isn’t strictly a windows keystroke. But who can work without Ctrl-W?

Karabiner Complex Rules Snippet

 
{
    "profiles": [
        {
            "complex_modifications": {
                "parameters": {  /* ... etc ... */ },
"README": "********************************************************************************************************",
"README": "*" COPY JUST THE ELEMENTS OF THIS "rules" array into your profiles.complex_modifications.rules array. "*",
"README": "********************************************************************************************************",
              
                "rules": [
                    {
                        "description": "Change Control+Z to Command+Z",
                        "manipulators": [ { "from": { "key_code": "z","modifiers": {"mandatory": ["control"],"optional": ["any"]}},"to": [{"key_code": "z","modifiers": ["command"]}],"type": "basic"}]
                    },
                    {
                        "description": "Change Control+X to Command+X",
                        "manipulators": [ { "from": { "key_code": "x","modifiers": {"mandatory": ["control"],"optional": ["any"]}},"to": [{"key_code": "x","modifiers": ["command"]}],"type": "basic"}]
                    },
                    {
                        "description": "Change Control+C to Command+C",
                        "manipulators": [ { "from": { "key_code": "c","modifiers": {"mandatory": ["control"],"optional": ["any"]}},"to": [{"key_code": "c","modifiers": ["command"]}],"type": "basic"}]
                    },
                    {
                        "description": "Change Control+V to Command+V",
                        "manipulators": [ { "from": { "key_code": "v","modifiers": {"mandatory": ["control"],"optional": ["any"]}},"to": [{"key_code": "v","modifiers": ["command"]}],"type": "basic"}]
                    },
                    {
                        "description": "Change Control+N to Command+N",
                        "manipulators": [ { "from": { "key_code": "n","modifiers": {"mandatory": ["control"],"optional": ["any"]}},"to": [{"key_code": "n","modifiers": ["command"]}],"type": "basic"}]
                    },
                    {
                        "description": "Change Control+S to Command+S",
                        "manipulators": [ { "from": { "key_code": "s","modifiers": {"mandatory": ["control"],"optional": ["any"]}},"to": [{"key_code": "s","modifiers": ["command"]}],"type": "basic"}]
                    },
                    {
                        "description": "Change Control+O to Command+O",
                        "manipulators": [ { "from": { "key_code": "o","modifiers": {"mandatory": ["control"],"optional": ["any"]}},"to": [{"key_code": "o","modifiers": ["command"]}],"type": "basic"}]
                    },
                    {
                        "description": "Change Control+Y to Command+Y",
                        "manipulators": [ { "from": { "key_code": "y","modifiers": {"mandatory": ["control"],"optional": ["any"]}},"to": [{"key_code": "y","modifiers": ["command"]}],"type": "basic"}]
                    },
                    {
                        "description": "Change Control+W to Command+W",
                        "manipulators": [ { "from": { "key_code": "w","modifiers": {"mandatory": ["control"],"optional": ["any"]}},"to": [{"key_code": "w","modifiers": ["command"]}],"type": "basic"}]
                    }
                ],
"README": "******************************************************************************************************",
"README": "******************************************************************************************************",
            /* ... etc ... */
            }
        }
    ]
}

Editing the Keyboard Map

This approach didn’t work for me. I’m not sure what I missed.