Abstracting APIs – Abstract Potentially Insane?

There’s no doubt about it, things that help are good.  This topic of abstraction came up in a chatroom the other day and I found myself advocating not abstracting in the client.  Having had more time to think about it I finally realised my own thoughts about it.  Just crept up from my subconscious.

At the moment I’m writing my own mobile apps, consuming an API that I’ve also written and maintain for use by mobile apps and web sites.  So naturally looking at anything to help is always worth considering.  The basic premise of my thinking is that in my particular situation, where I’m developing the API, dogfooding is the way to go.

Having to manually integrate with the API could be beneficial in a number of ways.  Having to deal with its security, data and inevitable idiosyncrasies can all potentially reveal design improvements.  If you immediately abstract your API behind someone else’s logic and expectations you’ll miss these opportunities during development.  You could argue the reverse is also true.  If Refit has troubles integrating with your API that could also reveal other problems or design improvements.

The particular client abstraction we were discussing is Refit.  It’s really very cool and if you aren’t in control of the API you’re interacting with it’s ideal, I’ll definitely be using it.  But I’m sure the authors of that API are at least testing that API if not also manually integrating with it at some point for some of the reasons outlines above.

Share

Enable ASP.NET 4.5 On Windows 10 or Windows 8/8.1

By default IIS and ASP.NET aren’t configured as part of a Windows setup (for obvious reasons) so developers are used to having to register IIS manually before being able to run and develop ASP.NET web sites on their desktops.  Prior to Windows 8 it was a simple command like this:

aspnet_regiis -r

This no longer works and requires a different command. Depending on what you already have enabled this may work:

dism /online /enable-feature /featurename:IIS-ASPNET45

If you haven’t enabled anything related to IIS yet you can do that at the same time with:

dism /online /enable-feature /all /featurename:IIS-ASPNET45

However!  That might not appear to solve the problem even when it has!  A post from Microsoft makes a bug apparent:

After the installation of the Microsoft .NET Framework 4.6, users may experience the following dialog box displayed in Microsoft Visual Studio when either creating new Web Site or Windows Azure project or when opening existing projects.

Configuring Web http://localhost:64886/ for ASP.NET 4.5 failed. You must manually configure this site for ASP.NET 4.5 in order for the site to run correctly. ASP.NET 4.0 has not been registered on the Web server. You need to manually configure your Web server for ASP.NET 4.0 in order for your site to run correctly.

NOTE: Microsoft .NET Framework 4.6 may also be referred to as Microsoft .NET Framework 4.5.3

This issue may impact the following Microsoft Visual Studio versions: Visual Studio 2013, Visual Studio 2012, Visual Studio 2010 SP1

Workaround:

Select “OK” when the dialog is presented. This dialog box is benign and there will be no impact to the project once the dialog box is cleared. This dialog will continue to be displayed when Web Site Project or Windows Azure Projects are created or opened until the fix has been installed on the machine.

Resolution:

Microsoft has published a fix for all impacted versions of Microsoft Visual Studio.

Visual Studio 2013 –

Visual Studio 2012

  • An update to address this issue for Microsoft Visual Studio 2012 has been published: KB3002339
  • To install this update directly from the Microsoft Download Center, here

Visual Studio 2010 SP1

  • An update to address this issue for Microsoft Visual Studio 2010 SP1 has been published: KB3002340
  • This update is available from Windows Update
    • To install this update directly from the Microsoft Download Center, here
Share

View IISEXpress Hosted Sites On Your Local Network

This is a useful little tidbit of knowledge to have.  Suppose you can browse to your new spanky site using IISExpress at http://localhost:54275/ … now you want to look at it on your phone?  Or your laptop? Or your … you get the picture …

Pre Visual Studio 2015

  1. Open up C:\Users\<yourname>\Documents\IISExpress\config\applicationhost.config
  2. Find your site definition and add in a new binding <binding protocol=”http” bindingInformation=”*:54275:<your-ip-address>” />
  3. Open Command Prompt (as admin) netsh http add urlacl url=http://<your-ip-address>:54275/ user=everyone
  4. Then execute netsh advfirewall firewall add rule name=”IISExpressWeb” dir=in protocol=tcp localport=54275 profile=private remoteip=localsubnet action=allow
  5. Then point your remote machines to http://<your-ip-address>:54275
  6. Voila!

That wasn’t so hard eh!

Visual Studio 2015

You need to complete steps 2 to 5 above, but Visual Studio 2015 by default doesn’t use the global configuration file for these IISExpress bindings.  In order to configure this you know have a couple of options.

The first option is to configure your project to use the global configuration files by added this to your *.csproj file:

<UseGlobalApplicationHostFile>True</UseGlobalApplicationHostFile>

Or you can add your addition bindings to the solution specific configuration file Visual Studio 2015 generates here:

<solution_dir>\.vs\config\applicationhost.config

Now running your solution under Visual Studio 2015 will behave as required.

Potential Errors

There are obviously too many potential errors to keep track of on a single blog post but I thought I’d detail a few fixes to issues I’ve personally experienced.

Access Denied

sometimes you may see this message when trying to launch your solution in Visual Studio.  To get around this close everything down and re-launch Visual Studio “as admin”.  This should fix the issue and then subsequent launches should work without running Visual Studio as an admin.

Failed to register URL "http://192.168.0.8:51258/" for site "<name>" application "/"
Error Description: The Network location cannot be reached.

This was a particularly annoying issue and took quite some time to track down.  It seems that the Threshold 2 update to Windows 10 removed all my listening IP address entries!  You can check that by executing this command in a Command Window:

netsh http show iplisten

If your own IP address isn’t listed here you need to add it.  You can do that by using this command (use your own IP address obviously):

netsh http add iplisten 192.168.0.8

You can see more information about netsh here.

Share

Upgrading MVC4 to MVC5 and WebAPI1 to WebAPI2 – MEF – DotNetOpenAuth

Originally Written 20 June 2014

So, if you have found this blog post as you are going through this process and have hit numerous brick walls I’m afraid I’ve got no answers.

I wasted 3 days on this process and by 3pm on the 3rd day I simply gave up and reverted my working copy back to MVC4.

it seems there are a number of breaking changes between MVC4 and MVC5 that just simply are not being talked about much and since I have a workload of 10 people at the moment I simply do not have spare capacity to sit down for days on end poking the framework to get it to work for me.

The security model in MVC5 is different meaning that DotNetOpenAuth is no longer working, which means Andrew Arrnott who WAS the lead (yes you read that right – WAS) had to issue a quick fix to DNOA so that it worked – DotNetOpenAuth MVC 5 extensions.

On top of all that faffing around with DNOA I found that I had yet more issues with the Microsoft.Bcl.Build stuff which is frankly a joke. Also the Attribute routing implementation that is now built in to WebAPI isn’t a patch on AttributeRouting. Crap.

Then for some reason which I could never get to the bottom of (hence the rolling back to MVC4) all my MEF code in my WebAPI solution simply stopped working. I have absolutely no idea why but all of a sudden my custom controller factory was simply bypassed by the whole pipeline meaning that anything marked as an import was left null. There is nothing talking about this online and I tried all mannor of code changes to try and figure out the problem and get it working but nothing fixed it. I have two open StackOverflow questions which I will keep an eye on:

http://stackoverflow.com/questions/24324228/updating-mvc4-webapi-to-mvc5-webapi-2-broken-mef-implementation

http://stackoverflow.com/questions/24326270/webapi-custom-controllerbuilder-not-being-called

So basically, upgrade at your peril, it may just eat through your time like a bastard, I gave up … for now.

UPDATE 30 June 2015

Amazingly this is almost a year to the date of the original post.  Again I have tried to migrate my MVC WebAPI and MVC Site over to the newer versions and again I have utterly failed and rolled it all back.  So many breaking changes and libraries that I’m dependent on that still just won’t play nicely.  I’ve wasted so much time and effort on getting this done that I have simply not got the time to carry on trying to push forwards with it all.

I have absolutely fallen out of love with DotNetOpenAuth.  It is a hugely over complicated library and I cannot wait to ditch it for something better.  It works for now and will have to do but I am not happy about how this library is put together.  It is hugely brittle and even the build process in the source code is horribly complex.

When will package authors realise that the more trickery you put into something the harder and harder it gets to support and for others to pick up and run with.  Not impressed.  Version 5 of this library has been in development since 2013 and is still only in an alpha state.  I’m going to move away from using this package at the first available opportunity I get.

I’ve also come to the conclusion that I’m not going to migrate to a newer version of MVC I’m going to build the MVC6 or maybe even MVC7 version from the ground up to make the most of the new features of the chosen replacement platform.  It seems the changes in 6 are profoundly deep, ASP.NET 5 doesn’t even support Web Forms for instance.  THAT is a huge development on its own.

Anyway, for anyone reading this and planning on doing the migration, good luck!

Share

Wrapping HttpClient

I’ve been recently revisiting a WebAPI client implementation that uses DotNetOpenAuth for authorising calls to an OAuth 2.0 protected WebAPI.  So I thought I’d share the generic methods I wrote to perform the actual calls.

The call to CreateAuthorizingHandler(AuthorizationState) is the part of the implementation that appends the required access tokens using some of the internals of DotNetOpenAuth so your own implementation of this bit will be different as will the exception that gets thrown (you may not even want to throw anything at all.  Anyway, here’s the methods:

Post Json

        /// <summary>
        /// Posts the json asynchronous.
        /// </summary>
        /// <typeparam name="TReturn">The type of the return.</typeparam>
        /// <typeparam name="TData">The type of the data.</typeparam>
        /// <param name="url">The URL.</param>
        /// <param name="payload">The payload <see><cref>{TData}</cref></see></param>
        /// <returns>an instance of <see><cref>{TReturn}</cref></see>
        /// </returns>
        /// <exception cref="MoodApiClientException">thrown if object fails validation or the request fails</exception>
        private async Task<TReturn> ExecutePostJsonAsync<TReturn, TData>(string url, TData payload) where TData : class
        {
            ICollection<ValidationResult> validationResults;
            if (!payload.IsValid(out validationResults))
            {
                var validationErrorMessage = ObjectValidationExtensions.BuildValidationErrorMessage(validationResults);
                var typeName = payload.GetType().ToString();
                throw new MyClientException(HttpStatusCode.BadRequest, string.Format("payload type: {0} Failed validation: {1}", typeName, validationErrorMessage));
            }

            using (var httpClient = new HttpClient(CreateAuthorizingHandler(AuthorizationState)))
            {
                var response = await httpClient.PostAsJsonAsync(ApiRootUrl.WellFormedUri(url), payload);

                if (response.IsSuccessStatusCode)
                {
                    var data = await response.Content.ReadAsAsync<TReturn>();
                    return data;
                }

                throw new MyClientException(response.StatusCode, response.ReasonPhrase);
            }
        }

Post Async

        /// <summary>
        /// Executes the post asynchronous.
        /// </summary>
        /// <typeparam name="TReturn">The type of the return.</typeparam>
        /// <param name="url">The URL.</param>
        /// <returns>an instance of type TReturn</returns>
        /// <exception cref="MoodApiClientException">thrown if not successful</exception>
        private async Task<TReturn> ExecutePostAsync<TReturn>(string url)
        {
            using (var httpClient = new HttpClient(CreateAuthorizingHandler(AuthorizationState)))
            {
                using (var response = await httpClient.PostAsync(ApiRootUrl.WellFormedUri(url), null))
                {
                    if (response.IsSuccessStatusCode)
                    {
                        return await response.Content.ReadAsAsync<TReturn>();
                    }

                    throw new MyClientException(response.StatusCode, response.ReasonPhrase);
                }
            }
        }

Get Async

        /// <summary>
        /// Executes the get asynchronously.
        /// </summary>
        /// <typeparam name="T">The Type to attempt to deserialise from the response</typeparam>
        /// <param name="url">The URL.</param>
        /// <returns>an instance of type T</returns>
        /// <exception cref="MoodApiClientException">throw if not successful</exception>
        private async Task<T> ExecuteGetAsync<T>(string url)
        {
            using (var httpClient = new HttpClient(CreateAuthorizingHandler(AuthorizationState)))
            {
                using (var response = await httpClient.GetAsync(ApiRootUrl.WellFormedUri(url)))
                {
                    if (response.IsSuccessStatusCode)
                    {
                        return await response.Content.ReadAsAsync<T>();
                    }

                    throw new MyClientException(response.StatusCode, response.ReasonPhrase);
                }
            }
        }

Delete Async

        /// <summary>
        /// Executes the delete asynchronous.
        /// </summary>
        /// <typeparam name="T">The expected type to deserialise from the response</typeparam>
        /// <param name="url">The URL.</param>
        /// <returns>an instance of type T</returns>
        /// <exception cref="MoodApiClientException">thrown if not successful</exception>
        private async Task<T> ExecuteDeleteAsync<T>(string url)
        {
            using (var httpClient = new HttpClient(CreateAuthorizingHandler(AuthorizationState)))
            {
                using (var response = await httpClient.DeleteAsync(ApiRootUrl.WellFormedUri(url)))
                {
                    if (response.IsSuccessStatusCode)
                    {
                        return await response.Content.ReadAsAsync<T>();
                    }

                    throw new MyClientException(response.StatusCode, response.ReasonPhrase);
                }
            }
        }
Share

MVC Deployment – JavaScript – ReferenceError is not defined …

This had me stumped for a moment.

All the Javascript was there (linkable from bundle links in the page source), all the files were there, everything worked in dev (doesn’t it always?).

Anyway, if you’re seeing this error in your deployed applications there are a couple of steps to take.

Firstly you should download and install the Visual Studio tool JSLint.VS2012. Then set this up to show warnings and don’t set it up to fail the build. JSLint is not kind and very strict about things (your adherence to BP is your choice but recommended for sure!)

So, you deploy your app and BANG all your lovely JS is twatted. Never fear … pop over to your web apps Global.asax file and in the Application_Start method include this:

BundleTable.EnableOptimizations = true;

So now with this setting set you can launch your app in debug mode with all the Optimizations forced on to test minification and more closely model your testing to the deployed version.

Once you have that working and are seeing the problems more clearly you can start to work through the potential issues using the reports from your new JSLint VS plugin to fix the syntax and other formatting issues.

Share

Using HttpClient to Send Dates in URL Using AttributeRouting

After doing a lot of reading it appears that it is possible to do what I was attempting to do but it requires relaxing a lot of useful security measures in order to do so. Since there is a simple workaround it just doesn’t make sense to relax these measures in light of increased security risks.

The error I was getting at the API was:

A potentially dangerous Request.Path value was detected from the client (:)

Obviously this is the colon characters used to separate the elements of the time portion of the DateTime string. So I have made the following changes.

My Api action method now looks like this:

[System.Web.Http.HttpGet]
[GET("range?{startDate:datetime}&{endDate:datetime}")]
public HttpResponseMessage Get(DateTime startDate, DateTime endDate)

The dates are now defined as part of the query string rather than parts of the path itself.

To handle the creation of the query string I also have the following extension method:

public static string ToQueryString(this NameValueCollection source, bool removeEmptyEntries)
{
    return source != null ? "?" + String.Join("&", source.AllKeys
        .Where(key => !removeEmptyEntries || source.GetValues(key).Any(value => !String.IsNullOrEmpty(value)))
        .SelectMany(key => source.GetValues(key)
            .Where(value => !removeEmptyEntries || !String.IsNullOrEmpty(value))
            .Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), value != null ? HttpUtility.UrlEncode(value) : string.Empty)))
        .ToArray())
        : string.Empty;
}

Which is used in my client code like this:

var queryStringParams = new NameValueCollection
    {
        {"startDate", start.ToString(_dateService.DefaultDateFormatStringWithTime)},
        {"endDate", end.ToString(_dateService.DefaultDateFormatStringWithTime)}
    };

var response = httpClient.GetAsync(ApiRootUrl + "plots/range" + queryStringParams.ToQueryString(true)).Result;

The date service in my application simply provides the default date formatting string and uses this pattern:

“yyyy-MM-ddTHH:mm:ss”

The complete URI that is produced from this looks like:

http://localhost:51258/plots/range?startDate=2013-07-30T21%3A48%3A26&endDate=2013-08-06T21%3A48%3A26

Share

C# Query String Builder

Here is a nice little extension method for building your query strings in C#


public static string ToQueryString(this NameValueCollection source, bool removeEmptyEntries)
{
    return source != null ? "?" + String.Join("&", source.AllKeys
        .Where(key => !removeEmptyEntries || source.GetValues(key).Any(value => !String.IsNullOrEmpty(value)))
        .SelectMany(key => source.GetValues(key)
            .Where(value => !removeEmptyEntries || !String.IsNullOrEmpty(value))
            .Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), value != null ? HttpUtility.UrlEncode(value) : string.Empty)))
        .ToArray())
        : string.Empty;
}

Share