We had a requirement at OCC to build a RESTful web service that would be able to run on both Windows and Linux servers. Someone suggested we give Mono a look to see if we would be able to use the ASP.NET Web API framework served up by the Apache Web Server on Linux. That sounded great; we have a lot of experience with the .NET Framework and a lot of experience with Linux but so far have not brought the two together.

Mono is an implementation of the .NET framework that can be used to build applications that run on Linux and OS X in addition to Microsoft Windows. There are further details on Wikipedia.
In the past, some concerns have been expressed regarding licensing, software patents and their possible impact on Mono and the applications that depend upon it. The Mono team have addressed these concerns and recently (April 2014) Microsoft released Roslyn under the Apache 2 license and have committed to working closely with the Xamarin team, whose product is based around Mono, which may further calm concerns.
Getting Started on Linux
If you are lucky your system will have a package available to install Mono, if so then you should use this. At the time I was not so lucky so had to get the latest stable source and build that.
In Practice
Because the Mono team are attempting to keep up with developments by Microsoft the Mono framework does not fully implement the latest .NET framework. This can lead to some headaches where there is a partial implementation which can result in some methods of a class not being available under Mono but often these issues can be worked around.
However, Mono is under very active development and generally manages to keep up surprisingly well.
Pros:
- Can use Visual Studio for the bulk of development.
- Once something builds and runs on Windows it runs very reliably on Mono. I’ve only been looking at Web applications so I couldn’t comment on a Desktop application with a GUI.
Cons:
- NuGet has limited usefulness with Mono. I had to get the necessary binary files and manage a Libraries directory within the project. Not a big issue in my case but could be if large numbers of externals are required.
- Have to maintain a separate build on the Linux system. I used makefiles which was not too onerous but this might be mitigated by MonoDevelop or Eclipse but it did not seem enough of a problem.
Building Mono
Building Mono from source is pretty straightforward but there are a few gotchas.
First it is necessary to make sure a basic development environment is in place, on a CentOS system that’s something along the lines of:
yum -y install bison glib2 glib2 freetype freetype-devel
fontconfig fontconfig-devel libpng libpng-devel libX11
libX11-devel glib2-devel libgdi* libexif glibc-devel
urw-fonts java unzip gcc gcc-c++ automake autoconf
libtool wget giflib-devel libjpeg-devel libtiff-devel
libexif-devel httpd-devel
Source Code
Get the 2.10 source releases of [libgdiplus][15], [mod_mono][16] and [XSP][14] – at the time of writing the stable build of mono is at version 3.2.3. It is does not appear to be important to have all components with the same version as the main Mono release.
Unpack each in a local directory then configure and build in the following order:
libgdiplus
cd libgdiplus-2.10
./configure --prefix=/opt/mono
make
sudo make install
Mono
cd mono-3.2.3
./configure --prefix=/opt/mono --with-libgdiplus=/opt/mono
make
sudo make install
Add the /opt/mono/bin
path to the system path and also set the PKG_CONFIG_PATH
to /opt/mono/lib/pkgconfig
through the /etc/profile
(do not forget to export
the variables). These variables must be set before building xsp
as it needs the C# compiler otherwise the configure
part of the build will fail.
xsp
cd xsp-2.10
./configure --prefix=/opt/mono
make
sudo make install
mod_mono
cd mod_mono-2.10
./configure --prefix=/opt/mono --with-mono-prefix=/opt/mono
make
sudo make install
sudo mv /etc/httpd/conf/mod_mono.conf /etc/httpd/conf.d/
Configuration
It will probably be necessary to add the path to Mono’s shared libraries to the system wide library path. This can be done by either adding the path to /etc/ld.so.conf
or, if the /etc/ld.so.conf.d
directory exists, by adding a new file there (I suggest following the naming convention used by other files in that directory) with the path to the Mono shared libraries – these will be at /opt/mono/lib
. Once this has been done run the ldconfig
command as root to update the system.
After building and installing check the installation by running:
mono-test-install
Making .NET 4.5 work
When building from source code there is a problem when running applications which require the .NET framework 4.5 libraries. The xsp4
and mod_mono
shell scripts that are executed (located in the /opt/mono/bin
directory) refer to executables in the /opt/mono/lib/mono/4.0
directory. Typically the executables themselves are fine but they refer to the 4.0 libraries which can be missing some of the newer features. This can result in problems of the form:
Exception caught during reading the configuration file:
System.MissingMethodException: Method not found: blah blah blah
at System.Configuration.ClientConfigurationSystem.System..... yack yack
To fix this first make symbolic links in the 4.5 directory to the 4.0 files:
ln -s /opt/mono/lib/mono/4.0/xsp4.exe /opt/mono/lib/mono/4.5/xsp4.exe
ln -s /opt/mono/lib/mono/4.0/mod-mono-server4.exe
/opt/mono/lib/mono/4.5/mod-mono-server4.exe
Then edit /opt/mono/bin/xsp4
and /opt/mono/bin/mod-mono-server4
to reference the symbolic links.
Fixing errors caused by colons in the virtual path name
In our application the resources managed by the RESTful interface include the colon ‘:’ character. There appears to be a bug which creeps out when using ASP.NET applications in sub directories. The problem appears with the static initialisation in System.Web.VirtualPathUtility
which manages to not read the Web.config
system.web/monoSettings verificationCompatibility="1"
attribute so fixed by setting the monoSettingsVerifyCompatibility
member variable false
otherwise errors are generated when there is a colon in a virtual path name.
Apache
The Apache mod for Mono passes requests to the mod_mono_server
, which is able to support multiple ASP.NET processes.
With the above completed restart Apache web server and verify that mod_mono has been picked up.
httpd -M
You can also inspect the error log after a restart.
MonoServerPath
Mono’s support for ASP.NET under Apache uses a simple module which delegates requests to the mod-mono-server
. The MonoServerPath
setting in httpd.conf
specifies where the mono server is for each location:
MonoServerPath default "/opt/mono/bin/mod-mono-server4"
This configures mono for the default path which for a standard Apache configuration will be /var/www/html
. It is also necessary to configure the application and handler:
MonoApplications "/:/var/www/html"
<Location "/">
Allow from all
Order allow,deny
SetHandler mono
</Location>
In addition, the following options can be set:
MonoSetEnv default MONO_IOMAP=all
MonoDebug default true
Restart the server and check the error log file.
If other locations need to be configured much the same needs to be repeated, for example, if a /test application were to be created it would be configured as:
Alias /test "/var/www/test"
MonoServerPath test "/opt/mono/bin/mod-mono-server4"
AddMonoApplications test "/test:/var/www/test"
<Location "/test">
Allow from all
Order allow,deny
MonoSetServerAlias test
SetHandler mono
</Location>
Other Directives
It is recommended to disable KeepAlive
for performance reasons or at least restrict the time-out to 2 seconds.
KeepAlive Off
The CentOS installation of Apache web server sets the name for the log files as access_log
and error_log;
you may want to have the more conventional .log
file extension.
Configuration Tool
The mono site has a handy online tool that can help with setting up a basic configuration for either a virtual host or an application.
In conclusion
Building a RESTful ASP.NET Web API with Mono, to run on Windows and Linux servers, was pretty straightforward with only a few problems on the way.