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.
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
- Building Mono
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.
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.
- 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.
- 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 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
Get the 2.10 source releases of [libgdiplus], [mod_mono] and [XSP] – 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:
cd libgdiplus-2.10 ./configure --prefix=/opt/mono make sudo make install
cd mono-3.2.3 ./configure --prefix=/opt/mono --with-libgdiplus=/opt/mono make sudo make install
/opt/mono/bin path to the system path and also set the
/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.
cd xsp-2.10 ./configure --prefix=/opt/mono make sudo make install
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/
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:
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
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
/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
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.
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.
You can also inspect the error log after a restart.
Mono’s support for ASP.NET under Apache uses a simple module which delegates requests to 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>
It is recommended to disable
KeepAlive for performance reasons or at least restrict the time-out to 2 seconds.
The CentOS installation of Apache web server sets the name for the log files as
error_log; you may want to have the more conventional
.log file extension.
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.
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.