DotNetCurry Logo

Hosting your ASP.NET Web API Services without IIS

Posted by: Suprotim Agarwal , on 5/18/2013, in Category ASP.NET
Views: 139213
Abstract: This article gives us an overview of how to host Web API services without IIS using it’s built in Self Hosting capabilities. Along the way we build a plain vanilla html client to consume our service as well.

ASP.NET Web API was introduced as a framework for creating services over HTTP. However to serve up these services, you need an HTTP Server. When developing ASP.NET applications, it’s easy enough to assume presence of a web server. But what if your requirement wanted you to work without ASP.NET, maybe rich client frontends from various platforms? Well, WebAPI has Self Host capabilities built it and you can actually host it in something as simple as a Console application. Today we’ll explore how we can have ASP.NET Web API Services without having to worry about ASP.NET and IIS.

For a quick primer on how the Web API pipeline works and where hosting fits in, checkout Lifecycle of an ASP.NET Web API Message.

We start off by creating a simple Web API Self Host Server and then move on to add services that we access from an Html client using AJAX. If the reasons for having a Self Host Web API server are a little fuzzy to you, hang in there. Consider it as a flexibility you have when architecting complex solutions that may not be limited to normal CRUD operations. Think of something like a Document server or a Data Server that communicates with its clients over HTTP instead of re-inventing the wheel on a custom RPC protocol.

 

Setting up a Web API Self Host Server

To keep things simple, we will start off with a Windows Console application. We call it WebApiServer.

Installing the dependencies

Once the solution is created, we start the Nuget Package Manager and do an Online search for WebAPI. From the results returned, install Microsoft ASP.NET Web API Self Host package as shown below.

add-reference

It will bring in all the dependencies including Web API. You have to agree to the license of the Additional dependencies as shown in the dialog below.

additional-licenses

Setting up a Minimal Server

Once the dependencies are in, setting up the server is merely seven lines of code

1. Create a Self-host configuration

2. Add default route to the configuration

3. Create a server instance using the configuration

4. Start the server and listen to inputs asynchronously

5. Print the server started message and wait till someone hits Enter to close the server

static readonly Uri _baseAddress = new Uri("http://localhost:60064/");
static void Main(string[] args)
{
// Set up server configuration
HttpSelfHostConfiguration config = new HttpSelfHostConfiguration(_baseAddress);
config.Routes.MapHttpRoute(
  name: "DefaultApi",
  routeTemplate: "api/{controller}/{id}",
  defaults: new { id = RouteParameter.Optional }
);
// Create server
var server = new HttpSelfHostServer(config);
// Start listening
server.OpenAsync().Wait();
Console.WriteLine("Web API Self hosted on " + _baseAddress + " Hit ENTER to exit...");
Console.ReadLine();
server.CloseAsync().Wait();
}

Permissions Gotcha: To be able to open a port successfully, you’ll have to run visual studio or the resulting exe as an Administrator. Other workaround is to use an HTTP URL Namespace Reservation using Netsh.exe. The Command for that is

netsh http add urlacl url=http://+:60064/ user=[machine]\[username]

Replace [machine] and [username] appropriately and change Port Number if you are using a different one

Fire up your favorite browser and hit the above URL. On Firefox you will get this

firefox-error

IE gives a more demure Page Not found, but you can ‘coax’ it to show the response using Developer Tools. As you can see, by default IE requests for JSON unlike Firefox which calls for xml.

ie-error

Okay so our Server is running. What next? Let’s setup a controller that will return some data.

Adding a Model and Controller

Adding a controller to a Self-Host Server is the same as adding one to an ASP.NET MVC project. Simply add a class that inherits from ApiController. Before that, let’s add a Contact class that will be our Model

public class Contact
{
public int Id { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public string Phone { get; set; }
}

Add another class ContactsController to the project.

public class ContactsController : ApiController
{
Contact[] contacts = new Contact[] 

  new Contact { Id = 1, Name = "Suprotim", Country="India", Phone="101 101 1101"},
  new Contact { Id = 2, Name = "Sumit", Country="US", Phone="202 202 2202" }, 
  new Contact { Id = 3, Name = "Mahesh", Country="India", Phone="303 303 3303" } 
};

 
 public IEnumerable<Contact> Get()
{
  return contacts;
}

public Contact Get(int id)
{
  var product = contacts.FirstOrDefault((p) => p.Id == id);
  if (product == null)
  {
   throw new HttpResponseException(HttpStatusCode.NotFound);
  }
  return product;
}

public IEnumerable<Contact> GetContactsByCountry(string country)
{
  return contacts.Where(p => string.Equals(p.Country, country,
   StringComparison.OrdinalIgnoreCase));
}
}

With the controller set, build and run the server again.

In FireFox, navigate to http://localhost:60064/api/Contacts and you should get a list of all the contacts as follows

firefox-results-get-all

IE as usual, sends back JSON so we use Developer Tools (F12) to coax the JSON output from it

ie-results-get-all

Bingo, our service is ready to ‘serve’. Time to build clients that access this ‘Service’

Building an HTML Client

We could have built an ASP.NET, Console or WinForms client but to show that the self-host service is truly independent, we’ll build an HTML page with a few JavaScript references only and consume the service from it.

A BootStrapped UI

I quickly hacked together a BootStrap sample page from http://www.layoutit.com/build. All I wanted was some CSS that looked better than the Times New Roman that we get without CSS. Additionally LayoutIt gave me jQuery bundled and an empty script file called script.js. All this was neatly arranged in the following folder structure

layout-it-package

All I needed now was Knockout to do some client side binding. I added a reference to KO using the Microsoft Ajax CDN so I didn’t have to download that too.

The HTML Markup

The markup is simple enough. We have the header with all the required Scripts and CSS. This is the biggest reason I used LayoutIt.

<head>
<meta charset="utf-8">
<title>Web API Self Host</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/bootstrap-responsive.min.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/scripts.js"></script>
<script type="text/javascript" src="
http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js"></script>
</head>

Note the KO reference from CDN.

The Body is marked up as follows. We have a searchText textbox and a button to initiate the search. Next we have a <ul> that is bound to the contacts property using KO’s foreach binding. The template <li> has three labels each bound to the Name, Country and Phone properties.

<body>
<div class="container-fluid">
  <div class="navbar navbar-inverse">
   <div class="navbar-inner">
    <div class="brand">
     Self Host Web API: Contacts Service
    </div>
   </div>
   </div>
   <div class="row-fluid">
    Search by Country
     <input type="text"
            id="searchText"
            name="searchText"
            class="input-medium search-query" />
     <button id="search" class="btn btn-primary ">Search</button>
   </div>
   <div class="row-fluid">
    <ul data-bind="foreach: contacts">
     <li>
      <label data-bind="text:Name"></label>
      <label data-bind="text:Country"></label>
      <label data-bind="text:Phone"></label>
     </li>
    </ul>
   </div>
  </div>
</body>

Script and KO View Model

The entire script can be split into the ViewModel, the document.ready method and the search button click handler.

KO ViewModel

This is pretty simple, all it has is an observable array property called contacts that will have all the contacts.

var viewModel = {
    contacts: ko.observableArray()
}

The document.ready Function

This fires on page load completion and we do a Get on our service. This returns us the list of Contacts we have. Once the data is returned (in the done method), we loop through it and push it into our view Model.

Finally we tell KO to apply bindings. This binds the UI elements in the Index.html that we saw above.

$(document).ready(function ()
{
jQuery.support.cors = true;
$.ajax(
{
  url: "
http://localhost:60064/api/Contacts/",
  type: "GET"
}).done(function (data)
{
  for (i = 0; i < data.length; i++)
  {
   viewModel.contacts.push(data[i]);
  }
  ko.applyBindings(viewModel);
}).fail(function (data)
{
  alert(data);
});
});

Search Button Click handler

There is a search text box and button to pass in a country name to our service so that we get users from a particular country only.

The Click handler calls our service with the country parameter and retrieves the results. Once the results are back, we clean out the view model and add the new result elements. KO binding ensures that the UI is updated appropriately.

$(document).on("click", "#search", function ()
{
$.ajax(
{
  url: "
http://localhost:60064/api/Contacts?country=" + $("#searchText").val(),
  type: "GET"
}).done(function (data)
{
  viewModel.contacts.removeAll();
  for (i = 0; i < data.length; i++)
  {
   viewModel.contacts.push(data[i]);
  }
}).fail(function (data)
{
  alert(data.message);
});
});

Cross Site Posting Warning

One thing to note here is that by default browsers don’t allow cross site posting, so an HTML client like this will get a ‘no transport’ error unless we enable CORS in jQuery. Towards this we have the following statement in our script:

jQuery.support.cors = true;

With that out of the way, when we view Index.html in our browser we get the following

html-client-all-contacts

If we search for Country = India we get the following result

html-client-india-contacts

Sweet! With that we have now built a Web API service that runs without IIS and created a standalone HTML page that can use it. We could now host this HTML page on IIS, Nginx, Apache or whatever Web Server we choose, it would still be able to communicate with the Web API service. There lies the crux of why we have Self Host in the first place.

Conclusion

To wrap up, Web API is not dependent on IIS for hosting and comes with perfectly capable Self Host library. This gives you freedom to decouple Web API Services from other parts of your System and not worry about how the service will be hosted. So for a scenario where you want your services to be accessed by Rich Clients only, there is no dependency whatsoever on IIS.

Download the entire source code of this article (Github)

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Author
Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of DotNetCurry, DNC Magazine for Developers, SQLServerCurry and DevCurry. He has also authored a couple of books 51 Recipes using jQuery with ASP.NET Controls and a new one recently at The Absolutely Awesome jQuery CookBook.

Suprotim has received the prestigious Microsoft MVP award for nine times in a row now. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that represents premium web sites and digital publications comprising of Professional web, windows, mobile and cloud developers, technical managers, and architects.

Get in touch with him on Twitter @suprotimagarwal, LinkedIn or befriend him on Facebook



Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by David H on Sunday, June 2, 2013 2:15 PM
Indeed a good article, however none of the tutorials I've found on self-hosting with WebAPI have explained why the project cannot be accessed by other computers within the LAN.  Yours is the first to mention that the constructed page could now be hosted with IIS or Apache, but you don't explain if that would be a design decision or required for access.  Perhaps I misunderstand the concept of SelfHost but if the lightweight encapsulated implementation   cannot be accessed from even the same subnet it does not appear to be anything more than an inter-process communication medium.
I would appreciate any guidance on this issue.
Comment posted by David H on Sunday, June 2, 2013 3:38 PM
Indeed a good article, however none of the tutorials I've found on self-hosting with WebAPI have explained why the project cannot be accessed by other computers within the LAN.  Yours is the first to mention that the constructed page could now be hosted with IIS or Apache, but you don't explain if that would be a design decision or required for access.  Perhaps I misunderstand the concept of SelfHost but if the lightweight encapsulated implementation   cannot be accessed from even the same subnet it does not appear to be anything more than an inter-process communication medium.
I would appreciate any guidance on this issue.
Comment posted by Ray Sanchez on Tuesday, August 13, 2013 8:22 PM
The GET is failing for me.
$(document).ready(function () {
    jQuery.support.cors = true;
    $.ajax(
    {
        url: "http://localhost:8080/UIManager/api/Contacts/",
        type: "GET"
    }
    ).done(function (data) {
        for (i = 0; i < data.length; i++) {
            viewModel.contacts.push(data[i]);
        }
        ko.applyBindings(viewModel);
    }).fail(function (data) {
        alert(data);
    });
});

I get Javascript alert error [object OBJECT].

Please help.
Comment posted by Ray Sanchez on Tuesday, August 13, 2013 8:24 PM
$(document).ready(function () {
    jQuery.support.cors = true;
    $.ajax(
    {
        url: "http://localhost:60064/api/Contacts/",
        type: "GET"
    }
    ).done(function (data) {
        for (i = 0; i < data.length; i++) {
            viewModel.contacts.push(data[i]);
        }
        ko.applyBindings(viewModel);
    }).fail(function (data) {
        alert(data);
    });
});

I get javascript alert error [object Object].

Please help.
Comment posted by Meriat on Wednesday, October 9, 2013 1:06 PM
What URI the Client?
Comment posted by Michael McCarthy on Thursday, November 7, 2013 1:30 PM
I can't get this to work with my controller.  I extend ApiContoller, but I get:

<Error>
<Message>
No HTTP resource was found that matches the request URI 'http://localhost:60064/api/agris'.
</Message>
<MessageDetail>
No type was found that matches the controller named 'agris'.
</MessageDetail>
</Error>
Comment posted by Michael McCarthy on Thursday, November 7, 2013 1:36 PM
Resolved.  Stupid mistake.  Realized controller class was not public.
Comment posted by Joey Cultit on Wednesday, November 13, 2013 3:40 PM
Hey so I got the server up (the initial steps) but I dont' understand hwo the html is mapped to the server. How does the server put the html up ont he browser? Furthur more what would I have to do if I have a web application and I wanted to use the server? How would I tell the server to start the web application (aspx file as the start page)?
Comment posted by Daniel on Saturday, May 17, 2014 10:08 AM
Hi,
is your html client a project or just a folder with html and everything else inside? how do you start it?
Comment posted by John on Thursday, May 29, 2014 7:15 AM
I have tried the above example and tried to browse http://localhost:8080/api/Contacts  getting nothing in the browser F12 tools. 'Unable to connect' error page is getting in Mozilla.
I have tried to access it from a client console application,
   static HttpClient client = new HttpClient();
   HttpResponseMessage resp = client.GetAsync("api/Contacts").Result;
Then, an error 'Unable to connect to the remote server' is getting.. I have given the controller class as public.
Comment posted by John on Friday, May 30, 2014 4:57 AM
Its resolved. We nee to run the Self Host project first.
Comment posted by Daniel on Thursday, June 5, 2014 6:01 AM
I'm creating something simmilar to what you have presented and i have some questions:
- which version of VS can i run your example? i've tried VS2010 but it's not working.
- you added a folder in your solution for the html client, but can you reference the file so when you start your app it launches the web page?
Thk