Warming up your web applications and websites is something which we have been doing for quite some time now and will probably be doing for the next couple of years also. This warmup is necessary to ‘spin up’ your services, like the just-in-time compiler, your database context, caches, etc.

I’ve worked in several teams where we had solved the warming up of a web application in different ways. Running smoke-tests, pinging some endpoint on a regular basis, making sure the IIS application recycle timeout is set to infinite and some more creative solutions.
Luckily you don’t need to resort to these kind of solutions anymore. There is built-in functionality inside IIS and the ASP.NET framework. Just add an `applicationInitialization`-element inside the `system.WebServer`-element in your web.config file and you are good to go! This configuration will look very similar to the following block.

<system.webServer>
  ...
  <applicationInitialization>
    <add initializationPage="/Warmup" />
  </applicationInitialization>
</system.webServer>

What this will do is invoke a call to the /Warmup-endpoint whenever the application is being deployed/spun up. Quite awesome, right? This way you don’t have to resort to those arcane solutions anymore and just use the functionality which is delivered out of the box.

The above works quite well most of the time.
However, we were noticing some strange behavior while using this for our Azure App Services. The App Services weren’t ‘hot’ when a new version was deployed and swapped. This probably isn’t much of a problem if you’re only deploying your application once per day, but it does become a problem when your application is being deployed multiple times per hour.

In order to investigate why the initialization of the web application wasn’t working as expected I needed to turn on some additional monitoring in the App Service.
The easiest way to do this is to turn on the Failed Request Tracing in the App Service and make sure all requests are logged inside these log files. Turning on the Failed Request Tracing is rather easy, this can be enabled inside the Azure Portal.

image

In order to make sure all requests are logged inside this log file, you have to make sure all HTTP status codes are stored, from all possible areas. This requires a bit of configuration in the web.config file. The trace-element will have to be added, along with the traceFailedRequests-element.

<tracing>
  <traceFailedRequests>
    <clear/>
    <add path="*">
      <traceAreas>
        <add provider="WWW Server" 	
        areas="Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module,Rewrite,iisnode"
		verbosity="Verbose" />
      </traceAreas>
      <failureDefinitions statusCodes="200-600" />
    </add>
  </traceFailedRequests>
</tracing>

As you can see I’ve configured this to trace all status codes from 200 to 600, which results in all possible HTTP response codes.

Once these settings were configured correctly I was able to do some tests between the several tiers and configurations in an App Service. I had read a post by Ruslan Y stating the use of slot settings might help in our problems with the warmup functionality.
In order to test this I’ve created an App Service for all of the tiers we are using, Free and Standard, in order to see what happens exactly when deploying and swapping the application.
All of the deployments have been executed via TFS Release Management, but I’ve also checked if a right-click deployment from Visual Studio resulted in different logs. I was glad to see they resulted in having the same entries in the log files.

Free

I first tested my application in the Free App Service (F1). After the application was deployed I navigated to the Kudu site to download the trace logs.

Much to my surprise I couldn’t find anything in the logs. There were a lot of log files, but none of them contained anything which closely resembled something like a warmup event. This does validate the earlier linked post, stating we should be using slot settings.

You probably think something like “That’s all fun and games, but deployment slots aren’t available in the Free tier”. That’s a valid thought, but you can configure slot settings, even if you can’t do anything useful with it.

So I added a slot setting to see what would happen when deploying. After the deploying the application I downloaded the log files again and was happy to see the a warmup event being triggered.

<EventData>
  <Data Name="ContextId">{00000000-0000-0000-0000-000000000000}</Data>
  <Data Name="Headers">
    Host: localhost
    User-Agent: IIS Application Initialization Warmup
  </Data>
</EventData>

This is what you want to see, a request by a user agent called `IIS Application Initialization Warmup`.

Somewhere later in the file you should see a different EventData block with your configured endpoint(s) inside it.

<EventData>
  <Data Name="ContextId">{00000000-0000-0000-0000-000000000000}</Data>
  <Data Name="RequestURL">/Warmup</Data>
</EventData>

If you have multiple warmup endpoints you should be able to see each of them in a different EventData-block.

Well, that’s about anything for the Free tier, as you can’t do any actual swapping.

Standard

On the Standard App Service I started with a baseline test by just deploying the application without any slots and slot settings.

After deploying the application to the App Service without a slot setting, I did see a warmup event in the logs. This is quite strange, to me, as there wasn’t a warmup event in the logs for the Free tier. This means there are some differences between the Free and Standard tiers considering this warmup functionality.

After having performed the standard deployment, I also tested the other common scenario’s.
The second scenario I tried was deploying the application to the Staging slot and press the Swap VIP button on the Azure portal. Both of these environments (staging & production) didn’t have a slot setting specified.
So, I checked the log files again and couldn’t find a warmup event or anything which closely resembled this.

This means deploying directly to the Production slot DOES trigger the warmup, but deploying to the Staging slot and execute a swap DOESN’T! Strange, right?

Let’s see what happens when you add a slot setting to the application.
Well, just like the post of Ruslan Y states, if there is a slot setting the warmup is triggered after swapping your environment. This actually makes sense, as your website has to ‘restart’ after swapping environments if there is a slot setting. This restarting also triggers the warmup, like you would expect when starting a site in IIS.

How to configure this?

Based on these tests it appears you probably always want to configure a slot setting, even if you are on the Free tier, when using the warmup functionality.

Configuring slot settings is quite easy if you are using ARM templates to deploy your resources. First of all you need to add a setting which will be used as a slot setting. If you haven’t one already, just add something like `Environment` to the `properties` block in your template.

"properties": {
  ...
  "Environment": "ProductionSlot"
}

Now for the trickier part, actually defining a slot setting. Just paste the code block from below.

{
  "apiVersion": "2015-08-01",
  "name": "slotconfignames",
  "type": "config",
  "dependsOn": [
    "[resourceId('Microsoft.Web/Sites', 
				parameters('mySiteName'))]"
],
"properties": {
  "appSettingNames": [ "Environment" ]
}

When I added this to the template I got red squigglies underneath `slotconfignames` in Visual Studio, but you can ignore them as this is valid setting name.

What the code block above does is telling your App Service the application setting `Environment` is a slot setting.

After deploying your application with these ARM-template settings you should see this setting inside the Azure Portal with a checked checkbox.

image

Some considerations

If you want to use the Warmup functionality, do make sure you use it properly. Use the warmup endpoint(s) to ‘start up’ your database connection, fill your caches, etc.

Another thing to keep in mind is the swapping (or deploying) of an App Service is done after all of the Warmup endpoint(s) are finished executing. This means if you have some code which will take multiple seconds to execute it will ‘delay’ the deployment because of this.

To conclude, please use the warmup-functionality provided by IIS (and Azure) instead of those old-fashioned methods and if deploying to an App Service, just add a slot setting to make sure it always triggers.

Hope the above helps if you have experienced similar issues and don’t have the time to investigate the issue.

For a project on the side I’m creating a Web API which has to parse XML requests in a POST. The first method I’ve written looks like this:

[HttpPost]
public HttpResponseMessage IndexPost(RequestModel requestMessage)
{
	return new HttpResponseMessage(HttpStatusCode.Accepted) { Content = new StringContent("This is the POST API response from BusinessPartner!") };
}

To test the new API I’m using the Postman Chrome plugin. With this plugin you are able to send requests to an endpoint and see what the response is. Of course you can also use Fiddler for this, but I like the user experience of Postman a bit more.

There were a few problems I encountered during the development of the Web API.

The first problem was this error:

{
    "message": "An error has occurred.",
    "exceptionMessage": "No MediaTypeFormatter is available to read an object of type 'RequestModel' from content with media type 'text/plain'.",
    "exceptionType": "System.InvalidOperationException",
    "stackTrace": "   at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger)\r\n   at System.Net.Http.HttpContentExtensions.ReadAsAsync(HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger)\r\n   at System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage request, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger)\r\n   at System.Web.Http.ModelBinding.FormatterParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)\r\n   at System.Web.Http.Tracing.Tracers.FormatterParameterBindingTracer.<>c__DisplayClass3.b__1()\r\n   at System.Web.Http.Tracing.ITraceWriterExtensions.TraceBeginEndAsync(ITraceWriter traceWriter, HttpRequestMessage request, String category, TraceLevel level, String operatorName, String operationName, Action`1 beginTrace, Func`1 execute, Action`1 endTrace, Action`1 errorTrace)\r\n   at System.Web.Http.Tracing.Tracers.FormatterParameterBindingTracer.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)\r\n   at System.Web.Http.Controllers.HttpActionBinding.<>c__DisplayClass1.b__0(HttpParameterBinding parameterBinder)\r\n   at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()\r\n   at System.Threading.Tasks.TaskHelpers.IterateImpl(IEnumerator`1 enumerator, CancellationToken cancellationToken)"
}

This was quite surprising as I thought I was sending a proper request which could be serialized. For reference, I was sending a POST request to http://localhost:11210/api/businesspartnerapi/ in this format:

<RequestModel><RequestMessage>1</RequestMessage></RequestModel>

image

As I couldn’t find anything on the internet explaining what I did wrong, I started looking through the options of Postman. Apparently you can add information to the header of the request, like a content type. After having added the contenttype text/xml to the message the API returned the response which I had expected.

image

Success!

Apparently you need to specify the correct content type in order for ASP.NET to know how to deserialize the object.

But there is more. The second problem I faced was the requestMessage being null.

image

Quite strange as the model is serializable. I’ve also added the [Serializable]-attribute to the model, but to no avail.

From the back in the WCF days I remembered you have to add the [DataContract]- and [DataMember]-attributes to a model if you to use them in services. Therefore I added them to my model.

The RequestModel now looks like this:

[DataContract(Namespace="")]
public class RequestModel
{
	[DataMember]
	public string RequestMessage { get; set; }
}

After POST-ing another request to the API I was able to see the result.

image

As you can see in the Watch window, the object is successfully created with the value I expected.

So, if you want to debug a Web API project, don’t forget to add the proper content type to your request and make sure you specify the attributes to your model so it can be (de)serialized.

Some time ago Microsoft has introduced the ASP.NET Web API framework. It’s a framework you can use to create your own RESTful services. It’s much like WCF Data Services or WCF RIA Services, but a lot easier to use (IMO). I’ve never liked the WCF-stack much, probably because configuring it always posed to be a hassle.

Using the Web API framework is much easier and you have to configure a lot less, or at least I haven’t found all of the configurable options yet. Then again, it’s created for entirely different purposes of course.
To create a service you select the ASP.NET MVC4 project template and can create new controllers which inherit from the ApiController. It’s also possible to do some awesome queries on your client nowadays, because Web API is capable of supporting OData! Install the latest stable NuGet package and you are ready to go!

Because I was put on a new project, I was finally able to do some real work with ASP.NET Web API. We had to develop a solution containing a so called self-hosted Web API service which also supported OData queries. This sounds like a lot of work, but it’s actually rather easy to do so nowadays.

During this new project we had some time to investigate and learn a bit about the platform. Therefore we chose to use the latest version of everything we could find, just to keep us up-to-date. To get OData working we used the beta2 version which works great for the simple stuff we were doing.
Now, all you have to do to get OData working is return IQueryable<T> from your controller actions. For example:

[Queryable]
public IQueryable GetEverything()
{
    using (var ctx = new HMOEvents("TheRepositoryConnectionStringName"))
    {
		return ctx.TableContainingEverything.OrderByDescending(e => e.TimeCreated).ToList().AsQueryable();
    }
}

Hit F5 and you are ready to do some OData queries on this action. Awesome, huh?

After we had figured this out, we still had to make this Web API service self-hosted. The reason for this requirement was because the webserver wasn’t in our control and the administrators had the tendency of resetting IIS quite a lot of times during the day (don’t ask….). I was already pulling my hair on figuring out how we had to do this, but as it turns out this is quite easy to do also.

There’s a reference you can add called System.Web.Http.SelfHost. You just have to create a new Console Application, add the reference and paste this piece of code over your Main method

static void Main(string[] args)
{
    HttpSelfHostServer server = null;
    try
    {
        // Set up server configuration 
	var config = new HttpSelfHostConfiguration(new Uri("http://localhost:2132"));
        config.EnableQuerySupport();
	config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
	config.Routes.MapHttpRoute(
				name: "ActionApi",
				routeTemplate: "api/{controller}/{action}",
				defaults: new { action = "Get" }
	);
	config.Routes.MapHttpRoute(
		            name: "DefaultApi",
		            routeTemplate: "api/{controller}/{id}",
       			    defaults: new { id = RouteParameter.Optional }
        );

	config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
	config.ReceiveTimeout = new TimeSpan(0, 0, 3, 0);
	config.SendTimeout = new TimeSpan(0, 0, 3, 0);
	// Create server 
	server = new HttpSelfHostServer(config);

        // Start listening 
        server.OpenAsync().Wait();
        Console.WriteLine("Listening on http://localhost:2132");
        Console.ReadLine();
    }
    catch (Exception e)
    {
        Console.WriteLine("Could not start server: {0}", e.GetBaseException().Message);
        Console.WriteLine("Hit ENTER to exit...");
        Console.ReadLine();
    }
    finally
    {
        if (server != null)
        {
            // Stop listening 
            server.CloseAsync().Wait();
        }
    } 
}

After doing so you can add your Web API controllers to this Console Application and start the application. Once the application is started you are able to do your queries on http://localhost:2132.
Note: You might need to check the firewall and run this application as an Administrator, otherwise it might cause some problems. Something about Windows security and setting up new ‘webservers’. It’s possible to do this without Administrator priviliges, to do this isexplained on Stack Overflow.

Again, pretty awesome, huh? This saves up a lot of development time!

You might have noticed I’ve used the following line to enable CORS on the service:

config.EnableCors(new EnableCorsAttribute("*", "*", "*"));

This isn’t some extension method and class that I’ve written myself. There’s something called Microsoft.AspNet.WebApi.Cors which is a NuGet package you can implement. At the time of the project, beta2 was the most recent version of these bits. Once you’ve included this in your project, you are able to enable CORS with just this extra line in the configuration.
For self-hosted services you have use this, because it’s rather hard (impossible?) to add the ‘Acces-Control-Allow-Origin’ headers.