Saturday, May 21, 2016

AngularJS orderBy filter does not work with object collection

When working with AngularJS, we become familiar with the use of the ng-repeat directive to iterate thru an object collection or an array of objects. This kind of implementation works well when displaying the data without sorting it.

The Problem

When there is a need to filter to data and sort the information by a particular property, we reach for the orderBy filter.  It is at this point that we realize that depending on the data structure we use, the results may not be the same. To visualize the differences, let’s take a look at an example in which we try to sort vehicles by their year. We want the most recent year at the top.

var list = {};
list[2009] = {make:'Nissan',model:'Altima', year:2009};
list[2010] = {make:'Nissan',model:'XTerra', year:2010};
list[2012] = {make:'Nissan',model:'Maxima', year:2012};
$scope.list= list;

 <ul>
<li ng-repeat="item in list | orderBy:'-year'" ng-init="item.name = item.year + ' '+ item.make +' '+ item.model">{{item.name}}</li>
 </ul>

As shown on the previous example, the data from the object collection is not sorted properly. The problem is that the orderBy filter expects an array data type which it can access via a numeric index. An object collection or associative array can only be accessed with a key which can be numeric or a string. We should note that in the most recent releases of AngularJS, the framework now raises an error indicating this problem.

The Solution

To address the problem, we need to provide the filter with an array like structure. This can be done by pushing the elements of our object collection into an array and using the result on the ng-repeat directive. Let’s take a look of how this can be done by modifying our previous example:

var arr = Object.keys(list).map(function(key) {
                 return list[key];
  });
  
$scope.arr = arr;
<ul>
<li ng-repeat="item in arr | orderBy:'-year'" ng-init="item.name = item.year + ' '+ item.make +' '+ item.model">{{item.name}}</li>
 </ul>

We can see how the list is sorted properly with the right order. We first created an array from our object collection (list) by getting all the keys and returning the data associated to the key. The end result is an array type. The orderBy filter uses the year property on the object and sorts the data in descending order by using the (-year) expression. This time the filter is able to access each object on the array and sort the data properly.

The one item to take away from this is that we should remember that arrays and associative arrays (hash tables) are different data structures, and the results on our view may be different than expected.

Thanks.

Originally published by ozkary.com

Monday, April 11, 2016

AngularJS Logging Client Errors to the Server

AngularJS provides an application exception handling ($exceptionHandler ) service that traps unhandled errors. The default implementation of this service simply delegates the exception to the $log service which just displays the error on the browser console.  As we can imagine, the user does not view these errors, and since the data is not collected, there may be some valuable information that goes unnoticed.

In order to help us improve the quality of our app, we need to add client side instrumentation that can enable us to track exceptions and send them to a centralized location for inspection. Since AngularJS already sends uncaught exception to the $exceptionHandler service, we can override this service and provide an implementation that can help us capture and report these errors to a server service.

Server Logger ($svcLog)

In order to send the client errors, we first need to create a service that can be used for server side logging.  This service just needs to accept an object with the exception information and call the corresponding API.  This is the service that is injected into our implementation of $exceptionHandler, so we are able to handle and send the error. Notice in the code below how we are using JQuery Ajax and not the $http service.  The reason behind this is problem with circular reference. We cover this concern in a later section.

var app = angular.module("app", []);
app.factory('$svcLog', [svcLog]);

function svcLog(){

      var svc = {
            add: add
      }

      function add(exception){
      //simulate sending the error here
            var data = angular.toJson(exception);
            console.log ('Sending to the server - ' + data );
            $.ajax({
            type: "POST",
            url: "/api/log",
            contentType: "application/json",
            data: data});
      }

      return svc;
}


Overriding $exceptionHandler

We can override this service by declaring a factory with the same name.  In this factory, we can inject the $svcLog service that we created. This way this works is that AngularJS delegates all the unhandled error calls by calling the service handler function. In this function, we get the exception object. For our simple logging service, we are just passing the message and stack information from the exception object.  We also want to continue to log the error to the console window and allow the application to continue its execution. To enable that, we need to call  $log.error.apply($log, arguments).


app.factory('$exceptionHandler',  
                 ['$log','$svcLog',svcExceptionHandler]);

function svcExceptionHandler($log, $svcLog) {

  var handler =  function (exception, cause) {
    var ex = { message: exception.message, stack:exception.stack };
    //log to console and allow the app to continue
    $log.error.apply($log, arguments);

    try {
        //send the error
        $svcLog.add(ex);
    } catch (err) {
        $log.log(err);
    }
};


Create an Error

We are now ready to simulate an error on our application.  In our home controller, we are going to make references to an undefined object. This should cause an error on our application.  AngularJS should trap this unhandled error and delegate it to our implementation of $excetionHandler which sends the error to our server.  The createError function is called by a button on our view. We are also handling an app-error broadcast message which can enable us to display the information on the controller.


app.controller("ctrlHome", ["$scope",
    function($scope) {
          console.log('controller init');

          $scope.createError = function(){
               invalid.data = 'this is an error on the controller';
          }

          $scope.$on('app-error', function (event, args) {
             $scope.error = args.message;
             $scope.stack = args.stack;
        });
    }
]);


About Circular Reference

We need to keep in mind that depending on the implementation of our $svcLog service, we may run into a circular reference exception.  If we change our implementation of $svcLog to use the $http service, we will get this error:

$rootScope <- $http <- $svcLog <- $exceptionHandler <- $rootScope

To get around this problem, we just need to inject the $injector service into our $exceptionHandler service and then inject our $svcLog using code. This is a way to force an injection without the circular reference. To show this, we create the $svcLogHttp service which uses the $http service to make the AJAX call.


app.factory('$exceptionHandler', ['$log', '$injector', svcExceptionHandler]);
app.factory('$svcLogHttp', ['$http', svcLogHttp]);

function svcLogHttp($http){
          
      var svc = {
            add: add
      }

      function add(exception){
            //simulate sending the error here        
            $http.post('/api/log', exception).then(function (resp) {
                  console.log();
            }, function (err) {
                  console.log(err);
            });
      }
      return svc;
}

//$svcLog is undefined.
function svcExceptionHandler($log, $injector, $svcLog) {
    var $svc =  $svcLog;
    var handler = function (exception, cause) {
        var ex = { message: exception.message, stack: exception.stack };
        //log to console and allow the app to continue
        $log.error.apply($log, arguments);

        try {
            if (!$svc) {
                $svc = $injector.get('$svcLogHttp');
            }
            //send the error
            $svc.add(ex);
        } catch (err) {
            $log.log(err);
        }
    };

    return handler;
}



Demo


(Open Dev tools to see console messages)

As we can see on this demo, we are not only maintaining the same behavior by logging into the console and allowing the application to run, but we are also able to send (simulate as there is no server accepting those requests - 404 error on console) the messages to our server which enable us to discover some interesting stuff that may be happening with our application.

Originally published by ozkary.com


Saturday, April 9, 2016

Web API OWIN CORS Handling No Access-Control-Allow-Origin Header



XMLHttpRequest cannot load http://localhost:7802/token. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:7812' is therefore not allowed access


This error means that there is an attempt to access a resource from a different domain by a web app, and the requested resource does not have any policies that would allow the apps from the origin domain to consume it. This error can be reproduced by loading a web app on a different domain (or port) and making an AJAX call to an API. The code would be something similar to the following:

Client App
API App

http://localhost:51223

http://localhost:61677

$.ajax({
type: "POST",
7/api/contact",
contentType: 'application/json',
data: JSON.stringify(contact),
headers: {
'Authorization': 'Bearer ' + tokenProvider.token,
},
success: function (res) {
    callback();
},
error: function (req, status, error) {
    callbackError();
}
});


[HttpPost]
[ResponseType(typeof(Contact))]
[Route("~/api/contact")]
public IHttpActionResult PostContact
        ([FromBody]Contact contact)
{
    IHttpActionResult result = null;

    if (!ModelState.IsValid)
    {
          result=BadRequest(ModelState);
    }
    else
     {
          //handle request
     }

     return result;
}

When making a CORS request, we need to understand that for every request that we send to a different domain, there may be two requests made to the server, preflight and actual requests. For each of these requests, the server must respond with the Access-Control-Allow-Origin header set with the name of the origin domain.

Preflight Request

A preflight or OPTIONS (HTTP verb) request is created by the browser before the actual request (PUT,POST) is sent for a resource in another domain. The goal is to have the browser and server validate that the other domain has access to that particular resource. This is done by setting the Access-Control-Allow-Origin header with the host or origin domain.

Actual Request

Once the preflight request has a response with the corresponding headers, the browser sends the actual request. For this request, the server also checks the CORS policies and adds the Access-Control-Allow-Origin header with the host domain.

OWINS CORS

The way to implement CORS using OWIN and MVC Web API is by first configuring the oAuth server options with an implementation of OAuthAtuhorizationServerProvider on the Startup class.


[assembly: OwinStartup(typeof(ozkary.Startup))]

namespace ozkary
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
        }

        private void ConfigureAuth(IAppBuilder app)
        {          
            // Configure the application for OAuth based flow
            var oAuthOptions = new OAuthAuthorizationServerOptions
            {
                Provider = new AuthorizationServerProvider(),
                                
            };

            // Enable the application to use bearer tokens to authenticate users
            app.UseOAuthAuthorizationServer(oAuthOptions);
           
        }  
    }
}

The OwinStartup directive allows OWIN to run the StartUp class and inject the AppBuilder instance in the Configuration method. This is what we need in order to configure the custom provider that can handle our token management, user validation and CORS headers. (token and user validation is not in the scope of this article). The implementation of AuthorizationServerProvider is listed below: (listing only the areas that are relevant to support CORS)

public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
    {                      
        /// <summary>
        /// match endpoint is called before Validate Client Authentication. we need
        /// to allow the clients based on domain to enable requests
        /// the header
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task MatchEndpoint(OAuthMatchEndpointContext context)
        {
            SetCORSPolicy(context.OwinContext);
            if (context.Request.Method == "OPTIONS")   
            {               
                context.RequestCompleted();
                return Task.FromResult(0);
            }

            return base.MatchEndpoint(context);
        }
       
      
        /// <summary>
        /// add the allow-origin header only if the origin domain is found on the     
        /// allowedOrigin list
        /// </summary>
        /// <param name="context"></param>
        private void SetCORSPolicy(IOwinContext context)
        {
            string allowedUrls = ConfigurationManager.AppSettings["allowedOrigins"];

            if (!String.IsNullOrWhiteSpace(allowedUrls))
            {
                var list = allowedUrls.Split(',');
                if (list.Length > 0)
                {

                    string origin = context.Request.Headers.Get("Origin");
                    var found = list.Where(item => item == origin).Any();
                    if (found){
                        context.Response.Headers.Add("Access-Control-Allow-Origin",
                                                     new string[] { origin });
                    }                   
                }
                
            }
            context.Response.Headers.Add("Access-Control-Allow-Headers"
                                   new string[] {"Authorization", "Content-Type" });
            context.Response.Headers.Add("Access-Control-Allow-Methods"
                                   new string[] {"OPTIONS", "POST" });

        }                
    }


The code above overrides the MatchEndpoint handler. This area of the code is called for every request including the preflight request (OPTIONS). Note that this handler is called before a call is made to Validate Client Authentication. This is where we need to manage the logic of adding the headers using OWIN.  We do this by calling the SetCORSPolicy method.

The SetCORSPolicy method looks for a configuration setting (allowedOrigins) that contains the whitelist of domains that can consume the resources from our API. It then reads the Origin header to get the domain name (including scheme and port i.e. http://mydomain.com:80). If the origin domain is found in the configuration, the Access-Control-Allow-Origin response header is set to the origin domain value. This is what helps us resolve the “No 'Access-Control-Allow-Origin' header is present on the requested resource”.

The SetCORSPolicy method is also a good place to add other response headers that may be required for accessing our API. For example, we may need to add the Access-Control-Allow-(Header and Methods) for additional access. (Not CORS related)

We can use the browser developer tools to take a look at the network request and response headers. When CORS is configured properly, the response headers should look similar to this:


What to Avoid

When using the OWIN middleware to handle our CORS calls, we want to stay away from adding these headers in other areas of the application as this causes a duplicate header exception. Some of the things to avoid include:

  • Do not add these headers on the web.config file
  • Do not use app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll) as this allows all origin domains to have access. We could use this option for public APIs with no restrictions.
  • Do not use the config.EnableCors from WebApiConfig (HttpConfiguration)  as we are already using a middleware to handle this concern. This option can be used when OWIN is not being used and we need to allow CORS
  • When configuring the whitelist for CORS, we need to make sure to include the scheme, domain and port number  (especially when testing with Visual Studio).   http://localhost is not the same as http://localhost:5733 and  www.ozkary .com is not the same as http://ozkary.com.  We need to match the Origin header value

Conclusion


We need to keep in mind that once we start using OWIN, we are essentially passing the CORS concern to the middleware. We do not want to start making changes in different areas of the application as that just makes thing confusing. We just need to focus on the AuthorizationServerProvider and add the necessary implementation to enable CORS in our apps.