Redirecting to login page with AngularJs and .net WebAPI

So here’s the scenario, you have a web application which people log into and some of the pages (like a dashboard) contain ajax functionality. Inevitably the users session expires, they return to the window change a filter and nothing happens. In the background, your JavaScript is making http calls to the server which triggers an unauthorised response. The front end has no way to handle this and some errors appear in the JS console.

A few things are actually combining to make life hard for you here. Lets take a look at each in more detail.

WebAPI and the 301 Response

To protect your API’s from public access a good solution is to use the Authorize attribute. i.e.

[Authorize]
public ActionResult GetDashboardData(int foo)
{
   // Your api logic here
            
}

However chances are your solution also has a login page configured in your web.config so that your regular page controller automatically trigger a 301 response to the login page.

    <authentication mode="Forms">
      <forms timeout="30" loginUrl="/account/sign-in/" />
    </authentication>

So now what happens, is instead or responding with a 401 Unauthorised response, what’s actually returned is a 301 to the login page.

With an AJAX request from a browser you now hit a second issue. The browser is making an XMLHttpRequest. However if that request returns a 301, rather than returning it your JavaScript code to handle, it “helpfully” follows the redirect and returns that to your JavaScript. Which means rather than receiving a 301 redirect status back, your code is getting a 200 Ok.

So to summarise your API was set up to return a 401 Unauthorised, that got turned into a 301 Redirect, which was then followed and turned into a 200 Ok before it gets back to where it was requested from.

To fix this the easiest method is to create are own version of the AuthorizedAttribute which returns a 403 Forbidden for Ajax requests and the regular logic for anything else.

using System;
using System.Web.Mvc;

namespace FooApp
{
    [AttributeUsage(AttributeTargets.Method)]
    public class CustomAuthorizeAttribute : AuthorizeAttribute
    {
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.Result = new HttpStatusCodeResult(403, "Forbidden");
            }
            else
            {
                base.HandleUnauthorizedRequest(filterContext);
            }
        }
    }
}

Now for any Ajax requests a 403 is returned, for everything else the 301 to the login page is returned.

Redirect 403 Responses in AngularJs to the login page

As our Ajax request is being informed about the unauthorised response, it’s up to our JavaScript code trigger the redirect in the browser to the login page. What would be really helpful would be to define the redirect logic in one place, rather than adding this logic to every api call in our code.

To do this we can use add an interceptor onto the http provider in angular js. The interceptor will inspect the response error coming back from the XmlHttpRequest and if it has a status of 401, use a window.locator to redirect the user to the login page.

app.factory('httpForbiddenInterceptor', ['$q', 'loginUrl', function ($q, loginUrl) {
    return {
        'responseError': function (rejection) {
            if (rejection.status == 403) {
                window.location = loginUrl;
            }
            return $q.reject(rejection);
        }
    };
}]);

app.config(['$httpProvider', function ($httpProvider) {
    $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
    $httpProvider.interceptors.push('httpForbiddenInterceptor');
}]);

You’ll notice a line updating the headers. This is to make the IsAjaxRequest() method on the api recognise the request as being Ajax.

Finally you’ll also notice the loginUrl being passed into the interceptor. As it’s not a great idea to have strings like urls littered around your code, this is using a value recipe to store the url. The code to do this is follows:

app.value('loginUrl', '/account/sign-in?returnurl=/dashboard/');

Advertisements

Two Google Maps Tips

Google Map

Centre a map on a collection of pins

The basic way to centre a Google Map is to give it co-ordinates of where you want the map to centre. But what if you have a collection of pin/markers and you want to show all of them but don’t know beforehand where they will be.

The solution is to create a LatLngBounds object and for each of your pins call the extend method of your bounds object. Once this is done call fitBounds on your map.

var bounds = new google.maps.LatLngBounds();

$.each(mapMarkers(), function(index, value) {
    bounds.extend(value.marker.position);
});

map.fitBounds(bounds);

 

Loading a map in a hidden div

The reason for doing this could be that you have a set of tabs and a non-visible one contains the Google Map. If you instantiate a Google Map when it isn’t visible you end up with the smallest map size possible.

One popular solution for this is to only create the map when the tab is being displayed, which is a good option as it means the map is only loaded when it’s viewed. However if your using something like Knockout to bind you’ve views to a model it may not be possible to attach an event to the tab change.

Google Maps actually have an event handler for just this scenario called resize. You simply need to trigger it at the point in which you can size the map.

google.maps.event.trigger(map, 'resize')

HTML5 Series – Array

If your going to do any sort of JavaScript programming it’s not going to be long until you have to work with an array, so there’s a few array functions you need to know about.

Push and Pop
Push and Pop are functions that add and remove items from an array. The easiest way to understand what they are doing is to imagine your array as a stack of paper. When you push and item onto the array it’s the same as putting it on the top of your pile. When you Pop an item off it’s the same as taking the top item from the pile.

        var myArray = [] // Declare a new array
        myArray.push("Red");
        myArray.push("Blue");

        var value1 = myArray.pop();
        var value2 = myArray.pop();

        alert(value1); // should alert the value Blue
        alert(value2); // should alert the value Red

Foreach loop
Probably one of the most useful functions for an array is the for each loop. A for each loop is essentially calling a function for each item in the array (hence for each). This is particularly useful in all kinds of scenarios.

        var myArray = ["blue", "red", "green"];

        myArray.forEach(function (x) {
            alert(x);
        });

Filter
As the name suggests filtering is a way to find items in your array. If you know C# then it works in a similar way to a lambda expression. The filter function takes a parameter of a function. Like the forEach loop the function is called on each item in the array, the function must then returns either true or false depending on if the filter criteria matched.

In this example notice that the result of myArray.filter is being assigned to another variable. This is because applying the filter wont actually remove items from the myArray array.

        var myArray = ["blue", "red", "green"];

        var results = myArray.filter(function(x) {
            if (x == "blue")
                return true;
            else
                return false;
        })

        results.forEach(function (x) {
            alert(x);
        });

Some, Every
The some and every functions can be used to see if some items in the array match a criteria of if all of them do. They return either true or false.

Like the filter function, a function is passed as the criteria and returns either true or false.

        var myArray = ["blue", "red", "green"];

        alert(myArray.some(function (x) {
            if (x == "blue")
                return true;
            else
                return false;
        })); // Alerts true as 1 item in the array is blue

        alert(myArray.every(function (x) {
            if (x == "blue")
                return true;
            else
                return false;
        })); // Alerts false as not every item in the array is blue

Concat
Concat is used to combine 2 arrays into 2 new array.

        var myArray = ["blue", "red", "green"];
        var myArray2 = ["yellow", "orange"];

        var myArray3 = myArray.concat(myArray2);

Slice
Slice lets you create a new array from an existing by letting you specify the start and end item. Those items and the others between then form the new array.

        var myArray = ["blue", "red", "green"];
        var myArray2 = myArray.slice(2, 3); // selects red and green

Splice
Splice can be used to add and remove items in an array. The function has the syntax:

arrayName.splice(index, how many, items to add);

Note: the index value starts at 0.

For example in our colour array we could add yellow and orange in between red and green with the following:

        var myArray = ["blue", "red", "green"];
        myArray.splice(2, 0, "yellow", "orange");

        myArray.forEach(function (x) {
            alert(x);
        });

Alternatively we could replace red and green with yellow and orange.

        var myArray = ["blue", "red", "green"];
        myArray.splice(1, 2, "yellow", "orange");

        myArray.forEach(function (x) {
            alert(x);
        });

A couple of others

Sort Sorts the array into alphabetical order
Reverse Reverses the order of the array

HTML5 Series – Error Handling

One thing you can say for certain about writing code is at some point your going to write something that error’s. It may be picked up in code reviews, or by a tester, and then fixed but for certain as you are human at some point you will make a mistake. What’s important though is what happens after the mistake.

If your a C# developer then good news JavaScript has the same try, catch, finally functionality that C# has and it works in the same way.

Wrap you code that could error with a try statement and curly braces and following it add in a catch statement with the code that you want to execute in the event of an error. Finally if you want some code to execute either way after both, place this in the finally block.

try
{
    functionThatDoesntExists();
}
catch (err)
{
     alert(err.message);
}
finally
{
    //Code to execute at the end
}

Notice the catch section is passed a parameter and I am then alerting a property of this called message. The error parameter can be useful for debugging what actually went wrong and contains a few other parameters.

  • Description
  • Message
  • Name
  • Number
  • Stack

Sometimes you may also want to cause an error to be thrown. You may be asking why, but consider people calling your functions may in-fact pass values that are not valid. You could detect this and throw an error containing some useful information before there incorrect parameter values causes your code to error and a less helpful message given.

var err = new Error(1, "Oh dear, that's not going to work"); // First parameter is the error number, second is the message
throw err;

HTML5 Series – Web Storage

One of the best benefits of HTML5 particularly for Web Apps is the ability to store data locally within the users browser. This means in many places you can speed your Web App up by fetching data once and then retrieving it locally in the future, rather than going back to a server each time.

There are 2 types of storage available localStorage and sessionStorage. The simple difference is that localStorage doesn’t expire while sessionStorage expires at the end of the users session.

Both types store data in a key/value list and could not be simpler:

// local storage
localStorage.variableName = value;

// session storage
sessionStorage.variableName = value;

One thing to note though is you can only store text. If you want to store something more complex the simple solution is to convert it to and from JSON. E.g.

var myColours = [ 'red', 'blue', 'green', 'yellow'];

// Save to local storage
localStorage.colours = JSON.stringify(myColours)

// Retrieve from local storage
var retrievedColorus = JSON.parse(localStorage.colours);

URL parameters with Javascript

So this took me by surprise the other day. JavaScript doesn’t have a function to retrieve a URL parameter value. i.e. There is no Request.Querystring command! Of course it wouldn’t start Request as it’s executing on the browser rather than the server but you would have thought there would be a built in command to do it.

After a bit of searching though I came across this script that does it:

// Read a page's GET URL variables and return them as an associative array.
function getUrlVars()
{
  var vars = [], hash;
  var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&amp;');
  for(var i = 0; i &lt; hashes.length; i++)
  {
      hash = hashes[i].split('=');
      vars.push(hash[0]);
      vars[hash[0]] = hash[1];
  }
  return vars;
}

The function returns an array/object containing the parameters and their values. e.g. The following URL:

http://www.example.com/?Name=Tim&Sex=Male

Would return:

{
    "Name": "Tim",
    "Sex": "Male"
}

To use it in your code you would write

var name = getUrlVars()["Name"];

And that’s it

Add keyboard shortcuts to your web app

Keyboard shortcuts are common place in desktop apps, to such an extent that you would probably be surprised if holding ctrl+s didn’t save a document, or if holding ctrl+n didn’t do some kind of new action. But with website’s its a lot less common. The bigger email providers all now provide the support, but for the sites produced smaller companies out there it’s still not the norm. Actually adding the support for it though is actually qutie straight forward.

In this example I’m going to use jQuery but it’s just as easy in everyday JavaScript.

    var isCtrl = false;

    $(document).keyup(function (e) {
	    if(e.which == 17) isCtrl=false;
    }).keydown(function (e) {
	    if(e.which == 17) isCtrl=true;
	    if(e.which == 83 && isCtrl == true) {
		    alert("Hello World");
		    return false;
	    }
    });

What is happening here is relively straight forward. When you hold a key down, JavaScript will pick up an event that says which key was pressed by it’s number. However what it can’t do is pick up a combination, so if you do a ctrl+s what you get is two events (one for the ctrl and one for the s). So on every keydown if it’s a ctrl we set the variable isCtrl to true, then on the next keydown we know it’s a combination. The keyup detects the end of the ctrl being held down and sets the variable back to false.

The return false, when we’ve detected a ctrl+s is also very important as what this will do is stop the browser from carrying out any shortcuts it may have had for that combination.

So there we have it, in around 10 lines of code you can add some functionality to your web app, that seems really advanced but is actually really really simple.

Doloto

Doloto

Meant for Web 2.0 Apps that use a lot of JavaScript, Doloto will speed up the download time of all the JavaScript by basically removing anything that isn’t initially needed and replacing it with stubs. It will then download in the background or wait until it is first called. The result, a seemingly much faster Web App for the user.

For more info have a look at the Doloto page on Microsofts Research site or head over to the download page on MSDN.