For a typical .NET developer, building a distributed application of web pages and services involves using IIS. But do you really need a full blown web server like IIS? Building a functional distributed application outside IIS is possible and quite easy. Inspired by lightweight frameworks like Java's Jetty and Python's Bottle, we'll look at creating lightweight sites and services in .NET using Nancy. We'll see how using lightweight frameworks allow you to focus on your business logic without letting heavyweight frameworks and processes get in the way.
3. About Me
C# MVP (Since April 2011)
Sr. Director of Web Solutions at RGP
Conference Director for Pittsburgh TechFest
Co-Founder of BrainCredits (braincredits.com)
Past President of Pittsburgh .NET Users Group and organizer of recent Pittsburgh Code Camps and
other Tech Events
Twitter - @DavidHoerster
Blog – http://geekswithblogs.net/DavidHoerster
Email – david@agileways.com
4. The Minimalist’s Goals
Easy setup and install
Able to deploy almost anywhere (low dependency)
Contained intent
Anti-Scavenger Hunt Development
Convention favored over heavy configuration
But I have control if needed
No significant performance hit
Improvement preferred!
PhD not required
5. What Do These Have in Common?
Run from a console
Full featured web app aspects
No need for IIS or installed web server
Jenkins/Solr are JVM; Raven is .NET
6. My Project
Have a demo app for using Solr in .NET
Solr
Works great, but dependent on IIS
Want to make it more like Solr
Install anywhere
Client
Nancy
IIS
(.EXE)
(WP)
Easy to set up and get running
Very configurable
Multiple instances running
SQL
8. Basic ASP.NET MVC Operation
Controller derives from
Controller
public class QuoteController : Controller {
Specific return types
private readonly QuoteRepository _repo;
public ActionResult Index() {
var quotes = _repo.GetAll();
return View(quotes);
}
}
Helpers that do help…
But what’s the route??
(Have to look in global.asax.)
Web API improves on this with declared return
types (e.g. List<T>) and Web API 2 will have
annotated routes (which takes a lot from Nancy).
9. What About Bottle (Python)?
Here’s my route
Intuitive method name
@bottle.route('/quote')
def get_all_quotes():
l = solr.query(‘*:*’)
# do some Python projection here to get ll
Tells I’m returning a
template, what the
template is, and what
model(s)
return bottle.template(‘quotes_template', dict(myquotes=ll))
10. Simplicity over Power
ASP.NET MVC (and Web API) has a lot of power
With power comes great responsibility
Conform
Sometimes lose intuitiveness of code
Routes defined elsewhere
Changing in Web API
Other configuration throughout app
11. Get Quotes in Nancy
public class QuoteModule : NancyModule {
private readonly IQuoteRepository _repo;
public QuoteModule(IQuoteRepository repo) {
_repo = repo;
Here’s my route and method
Get["/quote"] = _ =>
{
var quotes = _repo.GetAll();
return View["Index.cshtml", quotes];
};
Returning dictionary with
template and model
}
}
12. Nancy Differences with ASP.NET MVC
Simple design to create web methods
No method names – dictionary of routes and funcs
Route configuration right in with method definitions
Good or bad? Hmmm….
Bare bones distributed service environment
Low overhead / low ceremony service definitions
Not heavy on configuration
However, full async/await support not there…yet
13. Modules and Routing
Modules are like Controllers
Contain routes and route rules
public class QuoteModule : NancyModule {
private readonly QuoteRepository _repo;
public QuoteModule() {
_repo = new QuoteRepository();
Get["/quote"] = _ =>
{
var quotes = _repo.GetAll();
return View["Index.cshtml", quotes];
};
Essentially all defined in Module constructor
Watch that business logic doesn’t creep in
Modules could get unwieldy
}
}
14. Modules and Routing
In MVC, what’s my action?
Needs to be part of the route, unless default
What happens here?
http://localhost/quote/100 (GET)
http://localhost/quote/100 (GET)
http://localhost/quote/delete/100 (DELETE)
http://localhost/quote/100 (DELETE)
Nancy has dictionaries for actions
Get[“/quote/{id}”] = args => { … }
Delete [“/quote/{id}”] = args => { … }
15. Modules and Routing
Nancy’s routing is based on
Method
Get[“/quote/{id}”] = args => { … }
Delete [“/quote/{id}”] = args => { … }
Pattern
Action
Condition (routes can have conditions)
/quote/getall
/quote/{id?} (optional capture segment)
/quote/(?<id>[a..zA..Z]*) (regex)
Post[“/quote”, x => x.id > 0] = args => {…}
Post[“/quote”, x => x.id < 0] = args => {…}
16. Finding Modules
Nancy scans app for NancyModules
Loads them
No need to define routes in config or global
Nancy favors convention over configuration generally
17. Changing Behavior
Nancy is a pipeline
Series of events for each request
Request
Want forms authentication?
Add it to the pipeline
Auth
Error
Handler
Custom
Before
Actions
Want custom error page
Add it to the pipeline
Steps can be added at application, module and route level
Allows fine grain control for distributed app dev
Module
18. Changing Behavior
Creating a custom bootstrapper allows for custom behavior at app level
Before and After Hooks for application and module
Logging and events
Page Handlers for page type specific behavior
Handle custom 404’s
Static files
If outside of /Content, need to configure in Bootstrapper
pipelines.BeforeRequest += (ctx) =>
{
Logger.Log("starting…");
Command.Enqueue(new Command());
return null;
};
pipelines.AfterRequest += (ctx) =>
{
Logger.Log("ending request");
};
19. Authentication
By default, none
Somewhat bare bones, but gets the job done
var formsAuthCfg =
new FormsAuthenticationConfiguration()
{
RedirectUrl = "~/login",
UserMapper = container.Resolve<IUserMapper>(),
};
Configure through Bootstrapper
FormsAuthentication.Enable(pipelines, formsAuthCfg);
Forms Authentication module is available
Get it from NuGet
20. Content Negotiation
Nancy detects the Accept header on a request
If applicable, will return data in that form
Default formats are
JSON
XML
View
Get["/api/quote"] = _ =>
{
return _repo.GetAll()
.Quotes
.ToList();
};
Also configurable via Bootstrapper
Gotcha! XML needs to be materialized, not deferred (List<> vs. IEnumerable<>)
21. Dependency Injection
By default, TinyIoc is built in
private readonly IQuoteRepository _repo;
public QuoteModule(IQuoteRepository repo)
{
_repo = repo;
Nancy co-developer’s project
Works well – rated as average on IoC Benchmark by Daniel Palme
Able to use IoC of choice
Configure through Bootstrapper
Example with Ninject
Built in IoC allows automatic (magic?) injection of instances into Modules
How did this get here?
24. The Minimalist’s Goals - Recap
Easy setup and install
Able to deploy almost anywhere (low dependency)
Contained intent
Anti-Scavenger Hunt Development
Convention favored over heavy configuration
But I have control if needed
No significant performance hit
PhD not required
25. What’s Next?
Nancy + Katana (OWIN)
Other ASP.NET components moving to pipeline model
Be able to plug in SignalR and other pieces into Katana
Great article in MSDN Magazine about Nancy + Katana + SignalR + WebApi
Custom view renderers
Custom pipeline components
26. Resources
NancyFx Site: http://nancyfx.org/
Documentation: https://github.com/NancyFx/Nancy/wiki/Documentation
Howard Dierking on Nancy and Katana: http://msdn.microsoft.com/enus/magazine/dn451439.aspx
Dependency Injection Rankings: http://www.palmmedia.de/Blog/2011/8/30/ioccontainer-benchmark-performance-comparison
Session Code: https://github.com/DavidHoerster/Minimalist.Solr
Slides: http://www.slideshare.net/dhoerster/a-minimalists-attempt-at-building-adistributed-application