Introduction
In this article I'll show You how to play with OWIN and transform a DLL in a web site. This library will be integrated inside an MVC application to create standalone web site part that can be reused.
I learnt this technology when I was planning to create InfoPage, that's one of my personal projects. InfoPage is just a page that show you some useful information inside a MVC application (build number, change logs, loaded assemblies...). If you can't image it think about php_info() function and try to transpose it in a .net environment and scale it on all .net need.
For that project I would to create something that don't need more than one click. Firstly I imaged just a nuget package download but i was worried about shipping CSS and js. I didn't want to dirty original project where InfoPage will be installed with tons of files, and I didn't want to suppose that some library was present or ask to the user to manage them too. Moreover I would use something that could be updatable without the risk to override some changes made from the user.
After some investigation, I understand that an OWIN application was the best solution good because it embed all code in a single file (that I can ship with nuget), but.... how to create a OWIN module? How to serve static contents form the DLL? How to render html?
In this article I'll tell you some lesson I learnt from my training and I'll try to answer to these and more further question about the topic. And then, of course, We will see in practice how I implemented InfoPage using that technology.
Why OWIN?
Yes, OWIN is just a "standard". You can find many diagrams that explain what OWIN said, but in practices It defines just a standard interface between .NET web servers and web applications. How OWIN can help us? Main purpose of OWIN is to decouple server and application. That means You cannot develop "using OWIN" because it is It is just a specification (no tools, no framework, no DLL...).
To develop using OWIN We need a framework, that's clear, and it's name is Katana.
Katana is is a framework made to make a bridge between ASP.NET and OWIN specification.
So using that technology you can implement a site or service and serve it as part of an IIS site or in Self-Hosted mode. The second way approach is very interesting because it allow you to create standalone service that doesn't require IIS or any web server to run.
Why NANCY?
After few minute playing with OWIN \KATANA I missed some template engine and some framework in general that could help me with presentation. I made a quick search and I found Nancy, that seems to be very interesting. I didn't have made too much questions at the beginning because for what I saw It seems very interesting. It support many template engines (Razor is one of them) and it implements MVC pattern and that was what I looked for. So I installed it and tested.
Nancy born as a lightweight framework for building HTTP based services, so it doesn't require System.Web or to be run inside a web server. It has very powerful feature but for what I needed for realize my InfoPage project what come out-of-the-box is not enough, so I had to make some tuning on configuration and solve some little problem i found in the path.
After that experience I learn some things about Nancy, You can find most important points in next section.
Tutorial: How to start with owin \ Nancy website
I started with a demo project where I experimented most important features of Nancy, you can find the fully working example on my personal git-hub account. In this example I give me some simple goals:
- Set up a Nancy \ Owin site in a library
- include in a web application based on MVC
- serve static content form DLL
- Override view from main web application
Let's see how I do it in few simple steps.
Set up a Nancy \ Owin site in a library
First of all you need to create a web site MVC and a library inside the solution, and of course You have to add Nancy.Owin to your library. This part is very simple, we just need to include nancy.owin from nuget. Installing this package will resolve all dependency from Nancy and OWIN, so most is done just by clicking a button. After that you should find a solution like this one:
Nancy project works using conventions, that simply means you have to put things in the right places. Standard folders are:
- Views: contains views in any rendering template (Razor, SuperSimpleViewEngine..)
- Model: contains all classes used in model of MVC
- Contents: contains all static resources. Each resource placed here (CSS,js,imgs) must be marked as "embedded resource"
- Modules: this contains Nancy "modules", just some controllers
Nancy uses some similar to MVC controller that are called "modules", and you have to implement a module creating a class that inherits from NancyModule. In this class constructor you can map paths e configure what model an what view will be used for that request. Thinking about MVC we implement some similar to action, but in this case we use an explicit mapping instead of routing and we use a lambda instead of a function.
A very simple module could be something like that:
public class WelcomeModule : NancyModule
{
public WelcomeModule()
{
string baseHome = ConfigurationManager.AppSettings["TestOwinNancy:home"];
baseHome = (baseHome) ?? "home";
Get["/"+baseHome] = _ => {
var model = new { title = "We've Got Issues..." };
return View["home.html", model];
};
}
}
In this sample I take a url from config and I use it to serve html. My model is just an object with title and I use home.html view to transform model to html using this template.
Include in a web application based on MVC
To include our module inside a web application, first of all, we need to include the library by referencing project or assembly. That's easy. But how to tell to MVC that there is something to send to our Nancy module? Simple, we just need to add a little piece of configuration in Startup.cs
[assembly: OwinStartupAttribute(typeof(TestOwinNancy.Startup))]
namespace TestOwinNancy
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app
.UseNancy(options =>
{
options.Bootstrapper = new ResourceBootstrapper();
options.PerformPassThrough = (context => context.Response.StatusCode == HttpStatusCode.NotFound);
});
app.UseStageMarker(PipelineStage.MapHandler);
}
}
}
This piece of code just tell to main MVC application that there is an OWIN module to integrate and pass some configuration settings to Nancy. In this example there are some setting to use ResourceBootstrapper insthead of standard one (it allow to include views or other resources inside referenced DLL) and the PerformPassThrough option that simply define that wen nancy is not serving that resource all have to goes like it it wasn't.
Serve views content form DLL
This part is a little bit complicate because we need to make some tuning on configuration. Nothing of special, just few steps to follow.
- First of all, all resource to be server at runtime must be inside the DLL. This means is needed to mark every resource as "Embedded Resource". This will include files inside DLL maintaining relative paths
- We need to tell to Nancy to look for files inside DLL and not into web site. This can be done by implementing our bootstrapper. The part we need to do this is shown in the snippet below, but pay attention we'll need more customization later....
public class ResourceBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container);
ResourceViewLocationProvider.RootNamespaces.Add(GetType().Assembly, GetType().Assembly.GetName().Name+".Views");
}
protected override NancyInternalConfiguration InternalConfiguration
{
get { return NancyInternalConfiguration.WithOverrides(OnConfigurationBuilder); }
}
private void OnConfigurationBuilder(NancyInternalConfiguration x)
{
x.ViewLocationProvider = typeof(_ResourceViewLocationProvider);
}
}
Serve static content from DLL
We need to let ASP.NET manage request from module, so if you haven't already done in your application, you have to place runAllManagedModulesForAllRequests="true" inside web.config. This step is not relate with owin or Nancy, but is needed because ASP.NET require it to serve dynamically static file extensions.
public class ResourceBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container);
ResourceViewLocationProvider.RootNamespaces.Add(GetType().Assembly, GetType().Assembly.GetName().Name+".Views");
}
protected override void ConfigureConventions(NancyConventions nancyConventions)
{
nancyConventions.StaticContentsConventions.Add(EmbeddedStaticContentConventionBuilder.AddDirectory("Content", GetType().Assembly, "Content"));
base.ConfigureConventions(nancyConventions);
}
protected override NancyInternalConfiguration InternalConfiguration
{
get { return NancyInternalConfiguration.WithOverrides(OnConfigurationBuilder); }
}
private void OnConfigurationBuilder(NancyInternalConfiguration x)
{
x.ViewLocationProvider = typeof(_ResourceViewLocationProvider);
x.StaticContentProvider = typeof(DefaultStaticContentProvider);
}
}
In this second step we added the StaticContentProvider and we add come conventions to explain what folder use to store files.
Note: I copied EmbeddedStaticContentConventionBuilder.cs form official repo in git-hub to my demo app because it seems to be missing inside Nancy framework at the time when I wrote this article. Please check it before follow my way because in next releases it could be included.
Override view from main web application
All settings above works right because but doesn't allow you to override default behavior simply overriding files from main application. In that kind of application it is expectable, in example, to override a view simply implementing it inside main project. After some search I found that placing a view with same name of the one inside DLL into Views folder of main application cause an application breaks. This is because in implementation of ResourceViewLocationProvider there is a check for duplicate views that throw exception. I my case it was enough to extend that class and override this part of code, where I simply change this piece of code.
if (resourceStreams.Count() == 1 && !RootNamespaces.ContainsKey(assembly))
{
}
In your projects you can simply copy my file from git-hub.
So after that, you have to place a file with the same name of the file you want to override inside view folder of main application in order to override the one with same name shipped from library.
InfoPageLibrary: OWIN\NANCY in action
Implementation
After my trip on OWIN and Nancy, i got all I needed to start with my original project. So first of all I follow all steps above (I'll omit to don't be too much boring...) and I got a fully working project with a web application used as test and a core library. This library have same configuration of last step so it support embedded resources, view override and can serve static content. At that point was very natural to add bootstrap, jquery to the project to start working with a Nancy template inside an OWIN DLL.
After that,My project looks like this screenshot:
First of all I managed configuration issues by creating a configuration classes and a configurator one. My target was to call function passing an action to setup configuration. This approach helps people to quickly configure settings using autocomplete and i found very useful.This is how use and then some snippet to explain how I did it.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
InfoPageConfigurator.Configure(app,
x => {
x.BaseUrl = "custom-info";
x.ApplicationName = "My Sample Application";
});
}
}
public class InfoPageConfiguration
{
public String BaseUrl { get; set; }
public String ChangeLogPath { get; set; }
public String LicensePath { get; set; }
public String InfoPath { get; set; }
public bool ShowInfo { get; set; }
public bool ShowLicense { get; set; }
public bool ShowChangeLog { get; set; }
public Assembly MainAssembly { get; set; }
public String ApplicationName { get; set; }
public String ApplicationSubtitle { get; set; }
}
public static class InfoPageConfigurator
{
private static InfoPageConfiguration _current;
public static InfoPageConfiguration Configuration { get { return _current ?? (_current = new InfoPageConfiguration()); } }
public static void Configure( IAppBuilder app, Action<infopageconfiguration> conf)
{
app.UseNancy(options =>
{
options.Bootstrapper = new ResourceBootstrapper();
options.PerformPassThrough = (context => context.Response.StatusCode == HttpStatusCode.NotFound);
});
InfoPageConfiguration settings = new InfoPageConfiguration();
conf.Invoke(settings);
InfoPageConfigurator._current = settings;
}
}</infopageconfiguration>
After this we need to implement the service and first of all we start from model. This is quite simple, I just created class InfoPageModel.cs where I place all data I need in render.
public class InfoPageModel
{
public string ChangeLogs { get; set; }
public string License { get; set; }
public string Info { get; set; }
public AssemblyMetadata MainAssembly { get; set; }
public List<assemblymetadata> LoadedAssemblies { get; set; }
public InfoPageConfiguration Conf { get; set; }
}
</assemblymetadata>
After model was ready I created some helper method to fill model with data, computed from configuration and runtime values. This will produce following Nancy module.
public class WelcomeModule : NancyModule
{
public WelcomeModule()
{
string baseUrl = InfoPageConfigurator.Configuration.BaseUrl;
baseUrl = (baseUrl) ?? "info";
Get["/" + baseUrl] = _ =>
{
var model = InfoHelper.GetInfoPage(InfoPageConfigurator.Configuration);
return View["home.html", model];
};
}
}
About view "home.html, i use standard Nancy render engine and i copy here just a little piece of code just to show how it appear, full version can be found in source code.
<div class="tab-pane " id="assembly" role="tabpanel">
<h2>Assembly info</h2>
@Each.LoadedAssemblies @EndEach
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Title</th>
<th>FullName</th>
<th>Version</th>
<th>Architecture</th>
</tr>
</thead>
<tbody>
<tr>
<td>@Current.Title</td>
<td>@Current.FullName</td>
<td>@Current.Version</td>
<td>@Current.Architecture</td>
</tr>
</tbody>
</table>
</div>
<p>@If.Conf.ShowChangeLog</p>
<div class="tab-pane " id="changelogs" role="tabpanel">
<h2>Changelogs</h2>
@Model.ChangeLogs</div>
<p>@EndIf</p>
How to use
Usage of InfoPage is very simple, you just need to download from nuget, configure and use it.
1. download from nuget
2. configure it
Simply write this if you don't mind too much about customization. Of course, if You are curious, just type x. and press CTRL+SPACE
InfoPageConfigurator.Configure(app,
x => {
x.ApplicationName = "My Sample Application";
});
3. Start tracking change logs
To start with changelog track just create a file called changelog.md inside your project.
In this file You'll write changes using Markdown support. A sample of changelog can be the following:
#2016-03-15
Minor changes to core application, new feature "XXX" was introduced
* Css breaks home page: fixed
* Home page not compatible with IE9: fixed
#2016-03-10
Just few bug fixed and some css edited.
4.Start to use InfoPage
It's easy. Type /info in browser and you'll get something like this. You can see some useful information about your application.
You can customize InfoPage behaviour settings configuration. All property of InfoPageConfiguration.cs can be changed by user.
Property |
Value |
Meaning |
BaseUrl |
String |
is the url where Info Page Respond. /info is the default value |
ChangeLogPath |
String |
the path relative to root application for change log file. This file support markdown syntax |
LicensePath |
String |
the path relative to root application for License file. This file support markdown syntax |
InfoPath |
String |
the path relative to root application for info about application. This file support markdown syntax |
ShowInfo |
bool |
By default it is true if Info file is found. Can be edited to force to hide file even if present o show it anyway |
ShowLicense |
bool |
By default it is true if license file is found. Can be edited to force to hide file even if present o show it anyway |
ShowChangeLog |
bool |
By default it is true if change file is found. Can be edited to force to hide file even if present o show it anyway |
ApplicationName |
String |
This is the application name. If empty main assembly name will be used.This field has only presentation purpouse |
ApplicationSubtitle |
String |
This is a subtitle to show inside info page. If empty main assembly name will be used.This field has only presentation purpouse. Set it as an empty string to hide |
Then You can use InfoPage in some Ways:
Keep track of application changes
Yes we know that there is repository to comment you commits, but this menaning of "changes" is more related with stackholders and final user instead of developers. In lot of projects we need to track and let user know what set of changes or new feature are included in running software version. This can be simply achieved introducing InfoPage and keeping track of most important changes inside changelog.md file. In my project's I write a line h2 entry for each iteration and I describe inside what changes was done in iteration. When usefull I also add a bullet list to report all fixes and minor changes. So, after release, final user or stackholder can look what features or fixes have been released.
Show license
This feature is quite self-explaining. Just create a file called license.md in the root of the appliction and copy inside your license to sho that to the user
Show info about application:What you place inside info.md will be displayed on info page main page and is something like this readme. There are no written rules about what you can place here and no limits of lenght. In example in an open source project could be some notes about the author and the project.
Show info about build and assembly
This feature is an out-of-the-box feature very useful to manage multiple enviroments expecially when you are not basing deploy on continuous integration. In practice just browsing infopage path you can see main assembly version and all loaded assembly with version. So, simply looking at that, you can understand if there are some difference between environments or if the code runnind on production is the one you expect.
How can I integrate it in my application
Most convenient way is to place a link somewhere that open infopage path, because this is a "system" page that don't need to be shown to the final user. Alternatively you can override a view loading your css styles and integrate it in your theme, if you need. stadnard view don't use Razor, so if you would use it you need to include Nancy.Razor extension. Keep in mind this is an OWIN application so integration is limited. Main concept behind this library is something easy to integrate as-is, if you need more customization think about integrate InfoPage in your application without using OWIN. Ask me how by opening a issue. Final walkaround to easily integrate InfoPage is to edit view removing all styles and add an iframe in your application that load InfoPage path.
Conclusions
In this article we learn basics to use Nancy to implement a OWIN application that can be served inside an ASP.NET MVC standard application. This technique can be useful to create small "standalone" applications that can be reused and easily shared, like in nuget channel. This article is not a guide but would be a good entry point to start in practices implementation of an OWIN application.
If you want to know more about InfoPage project you can look at the git-hub project or download the library from nuget and play with that.
As ever suggestion ad answer are always welcome, in this case for this article and also for the InfoPage project.
History
- 03-04-2016: codeproject submission
- 30-03-2016: first draft of this article
- 25-03-2016: First release on nuget
- 24-03-2016: InfoPage implementation;
- 20-03-2016: First tutorial on Nancy Owin
References