Reverse Proxy in ASP.NET Web API

Updated: Be sure to check my follow up post

Due to infrastructure limitations my current team was in need of a reverse proxy that could point to our ASP.NET Web API endpoint. After failing to get IT to setup a reverse proxy in the load balancer I ended up experimenting with a reverse proxy based on a simple implementation using Web API.

The first requirement is to intercept all requests made to the reverse proxy endpoint. Fortunately the Web API pipeline allows this via the DelegatingHandler:

public class ProxyHandler : DelegatingHandler{} 

public class WebApiConfig
{
    public static void Configure(HttpConfiguration config)
    {
        config.MessageHandlers.Add(new ProxyHandler());
        config.Routes.MapHttpRoute("abe", "{*path}");
    }
}

The configuration above adds the ProxyHandler to the general pipeline thus allowing it to intercept all requests which are processed by the Web API pipeline. Then a single catch-all route is added to make sure all requests are processed by the pipeline.

In the proxy delegating handler all requests must now be forwarded to the desired location:

public class ProxyHandler : DelegatingHandler
{
    private async Task<HttpResponseMessage> RedirectRequest(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var redirectLocation = "http://localhost:61948";
        var localPath = request.RequestUri.LocalPath; 

        var client = new HttpClient(); 

        var clonedRequest = await HttpRequestMessageExtensions.CloneHttpRequestMessageAsync(request); 

        clonedRequest.RequestUri = new Uri(redirectLocation + localPath); 

        return await client.SendAsync(clonedRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
    } 

    protected override
        Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        return RedirectRequest(request, cancellationToken);
    }
}

I experienced some problems forwarding GET requests which is why the above code clone the entire HttpRequestMessage via the below snippet found on stack overflow:

public static class HttpRequestMessageExtensions
{
    public static async Task<HttpRequestMessage> CloneHttpRequestMessageAsync(HttpRequestMessage req)
    {
        var clone = new HttpRequestMessage(req.Method, req.RequestUri); 

        var ms = new MemoryStream();
        if (req.Content != null)
        {
            await req.Content.CopyToAsync(ms).ConfigureAwait(false);
            ms.Position = 0; 

            if ((ms.Length > 0 || req.Content.Headers.Any()) && clone.Method != HttpMethod.Get)
            {
                clone.Content = new StreamContent(ms); 

                if (req.Content.Headers != null)
                    foreach (var h in req.Content.Headers)
                        clone.Content.Headers.Add(h.Key, h.Value);
            }
        } 

        clone.Version = req.Version; 

        foreach (var prop in req.Properties)
            clone.Properties.Add(prop); 

        foreach (var header in req.Headers)
            clone.Headers.TryAddWithoutValidation(header.Key, header.Value); 

        return clone;
    }
}

I’m very impressed by the elegance of both Web API but more so the way the HttpRequestMessage/HttpResponseMessage is reused between Web API and HttpClient.

I tested the reverse proxy solutions against our current API and all our GET/POST requests went through. Furthermore all exception message was passed through the proxy as well.

A numeric comparison was attempted on TargetPlatformVersion evaluated to empty string

So after upgrading to Visual Studio 2013 update 1 and upgrading my web project to MVC 5.1 I could no longer load or build the project. I keep getting an error from MSBuild stating that the TargetPlatformVersion variable evaluated to an empty string. Naturally this caused some initial headache.

After comparing the project file with a clean project and finding no major differences I moved on to looking at the solution file. It turns out the VisualStudioVersion had been bumped to “12.0.30110.0” up from the previous “12.0.21005.1”. After updating this to the newest version the project successfully loaded again.

So if you experience this issue update your solution files visual studio version and reload the solution.

Let User Clear ComboBox Selection

The built-in ComboBox for Windows 8 does not come with a way to revert the control back to an unselected state. This is an issue in the scenario where the value in the ComboBox isn’t required an thus needs to be clearable.

A simple solution is to add an extra item to the Combox without any text and a value of -1. We can then wire up the CB with an attached property and handle the SelectionChanged event. Whenever we see a value of -1 being selected we can clear the selection.

One of the pros of clearing the CB is the placeholder text. Upon clearing the selection the placeholder text reappears.

The sample attached property can be seen below


public class ComboBoxHelper : FrameworkElement
{

public static bool GetClearOnEmptyValueSelection(DependencyObject obj)
{
 return (bool)obj.GetValue(ClearOnEmptyValueSelectionProperty);
}

public static void SetClearOnEmptyValueSelection(DependencyObject obj, bool value)
{
 obj.SetValue(ClearOnEmptyValueSelectionProperty, value);
}

public static readonly DependencyProperty ClearOnEmptyValueSelectionProperty =
DependencyProperty.RegisterAttached("ClearOnEmptyValueSelection", typeof(bool), typeof(ComboBoxHelper), new PropertyMetadata(false, (o, args) =&gt;
{
 var cb = (ComboBox)o;

 if ((bool)args.NewValue)
 {
  cb.SelectionChanged += (sender, eventArgs) =&gt;
  {
   if (eventArgs.AddedItems.Count == 1)
   {
    if (cb.SelectedValue == null || string.IsNullOrWhiteSpace(cb.SelectedValue.ToString()) || (cb.SelectedValue is int &amp;&amp; (int)cb.SelectedValue == -1))
    {
     cb.SelectedIndex = -1;
    }
   }
 };
}
}));
}

 

With the attached property at hand wiring the CB is as simple as:

<ComboBox ap:ComboBoxHelper.ClearOnSelection="true">

Portable Libraries at Campus Days 2013

I will speaking at Campus Days 2013 Wednesday at 10:15 in the Developer track.

This year I will cover Portable Class Libraries (PCL) in .NET.

The talk will briefly introduce the new features and the foundation it is built upon. The main focus of the presentation will be an exploration of the possible and most popular use cases for PCLs. This includes building a clean api, sharing api entities, sharing platform features and sharing view models (in MVVM).

It will be a light presentation. Hope to see you there.

‘The app didn’t start’ Issue

I was presented with this error message upon launching my newly upgraded 8.1 Windows Store app. In the process of upgrading I also upgraded all the related Nuget packages. What I didn’t see was that one of the packages added an App.config file to my Windows Store project.

One hour later…

apparently this App.config causes the app to crash upon activation with no additional information.

Solution: Delete the App.config file from your store project.

Single Column Unscrollable Panel

Let’s say we have a GridView in Windows Store. We want the items arrange according to this:

  • Single column
  • No overflow / scrolling
  • Excess items should not be displayed (and no half displayed items on the bottom edge of the screen)

For some reason there is a no built-in panel that will do this. So I rolled my own. You can see the implementation below:

public class SingleColumnUnscrollablePanel : Panel
{
&nbsp;&nbsp;&nbsp;&nbsp;protected override Size MeasureOverride(Size availableSize)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var requiredHeight = 0.0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var remainingHeight = availableSize.Height;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var largestDesiredWidth = 0.0;
&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (FrameworkElement child in Children)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.Measure(new Size(availableSize.Width, availableSize.Height));
&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var desiredHeight = child.DesiredSize.Height;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var desiredWidth = child.DesiredSize.Width;
&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (remainingHeight &gt;= desiredHeight)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;largestDesiredWidth = Math.Max(largestDesiredWidth, desiredWidth);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remainingHeight -= desiredHeight;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;requiredHeight += desiredHeight;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return new Size(largestDesiredWidth, requiredHeight);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;protected override Size ArrangeOverride(Size finalSize)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var availableHeight = finalSize.Height;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var currentHeight = 0.0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var mychildren = Children;
&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!mychildren.Any())
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return finalSize;
&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var largestDesiredWidth = mychildren.Max(s =&gt; s.DesiredSize.Width);
&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (var child in mychildren)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var desiredHeight = child.DesiredSize.Height;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var desiredWidth = child.DesiredSize.Width;
&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (desiredHeight + currentHeight &gt; availableHeight)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.Arrange(new Rect(0, currentHeight, desiredWidth, desiredHeight)); //largestDesiredWidth, desiredHeight));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentHeight += desiredHeight;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return new Size(largestDesiredWidth, currentHeight);
&nbsp;&nbsp;&nbsp;&nbsp;}
}

 

Using it in a GridView:

<GridView.ItemsPanel>
<ItemsPanelTemplate>
<controls:SingleColumnUnscrollablePanel />
</ItemsPanelTemplate>
</GridView.ItemsPanel>