Linked In Twitter RSS

Request throttling in ASP.NET Web API

3. March 2012  · Comments (0)

 

Real world scenario

 

When you start to build real world REST API you will surely try to limit access to you API. You can limit number of concurrent requests by using http://www.iis.net/download/dynamiciprestrictions

or some other tools.

 

In this post I will try to limit access to my ASP.NET Web API by using my own code building my own custom DelegatingHandler.

 

What you will see here

 

In this code bellow is ThrottleRateHandler that will be used to limit number of calls to your API in hour. Also these same code will limit how many times you can call API in seconds.

These class is using code similar as StackOverflow has used for their throttle rate handler.

Also these code is based on Token authorization approach.

 

I will also use HTTPRuntime.Cache to implement this functionality. I plan for my real world REST API to use MemBase caching. So question for my readers: Is it smart to replace HTTPRuntime.Cache with MemBase later ?

 

Here is code for ThrottleRateHandler :

public class ThrottleRateHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            string query = request.RequestUri.Query;
            string accessToken = HttpUtility.ParseQueryString(query).Get("accessToken");
            double seconds = Convert.ToDouble(WebConfigurationManager.AppSettings["throttleTimeoutForMethodCall"]);
            int numberRequests = Convert.ToInt32(WebConfigurationManager.AppSettings["tokenRequestsPerHour"]);

            if (accessToken != null)
            {
                var throttleKey = string.Format(
                    "{0}-{1}", request.RequestUri.AbsolutePath, accessToken);
                var tokenKey = accessToken;
                var allowExecute = false;

                if (HttpRuntime.Cache[throttleKey] == null)
                {
                    // this check was added for integration testing
                    if (!seconds.Equals(0.0))
                    {
                        HttpRuntime.Cache.Add(
                            throttleKey,
                            true,
                            null,
                            DateTime.Now.AddSeconds(seconds),
                            Cache.NoSlidingExpiration,
                            CacheItemPriority.Low,
                            null);
                    }

                    allowExecute = true;
                }

                if (allowExecute)
                {
                    var hit = (HitInfo)(HttpRuntime.Cache[tokenKey] jQuery152020203585348297026_1330795835814 new HitInfo());

                    if (hit.Hits > numberRequests)
                    {
                        allowExecute = false;
                    }
                    else
                    {
                        hit.Hits++;
                    }

                    if (hit.Hits == 1)
                    {
                        HttpRuntime.Cache.Add(
                            tokenKey,
                            hit,
                            null,
                            DateTime.Now.AddMinutes(60),
                            Cache.NoSlidingExpiration,
                            CacheItemPriority.AboveNormal,
                            null);
                    }

                    if (!allowExecute)
                    {
                        return this.ReturnException(string.Format("You can call API {0} times per hour", numberRequests));
                    }
                }
                else
                {
                   return this.ReturnException(string.Format("You can call API every {0} seconds", seconds));
                }
            }
            else
            {
                var response = new HttpResponseMessage
                {
                    Content =
                        new StringContent("You must supply valid token to access method!"),
                    StatusCode = HttpStatusCode.Unauthorized
                };
                return Task<HttpResponseMessage>.Factory.StartNew(() => response);
            }

            return base.SendAsync(request, cancellationToken);
        }

        private Task<HttpResponseMessage> ReturnException(string message)
        {
            var response = new HttpResponseMessage
            {
                Content = new StringContent(message),
                StatusCode = HttpStatusCode.Conflict
            };
            return Task<HttpResponseMessage>.Factory.StartNew(() => response);
        }

        public class HitInfo
        {
            public HitInfo()
            {
                Hits = 1;
            }

            public string Token { get; set; }
            public int Hits { get; set; }
        }
    }
}

ASP.NET Web API Authorization using Tokens

3. March 2012  · Comments (2)

 

Planning real world REST API

 

When you try to plan how to build real world REST API like other major players like Facebook or Foursquare have you will soon realize that all major players use OAuth 2.0 .

ASP.NET Web API comes with support for authorize attribute and that’s nice, but for real world API I want to support token based approach.

 

OAuth 2.0 Server

 

For supporting token based approach  you must have some kind of server that will issue tokens. Building token server can be complex and most major players have implemented OAuth 2.0 server based on draft 10 OAuth documentation.

We hope that Microsoft will provide us with their own OAuth 2.0 server for free in final version of ASP.NET MVC 4.

Meanwhile I will just assume that you already have your own OAuth 2.0 server.

 

Building ActionFilterAttribute

 

I have solved my problem with authorization by implementing RequireAuthorize ActionFilterAttribute. This attribute also have scope property. Scope property is used for limiting access to your REST API.

 

You just need to decorate controllers or actions in controllers with this attribute and optionally set required scope for accessing these actions.

 

Here is RequireAuthorizeAtribute:

public class RequireAuthorization : ActionFilterAttribute
    {
        public string Scope { get; set; }

        public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            string[] scope = null;
            if (!string.IsNullOrEmpty(Scope))
            {
                scope = Scope.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
            }

            string query = actionContext.Request.RequestUri.Query;
            string accessToken = HttpUtility.ParseQueryString(query).Get("accessToken");

            // we first check for valid token
            if (accessToken != null)
            {
                IAccessTokenValidator accessTokenValidator = new AccessTokenValidator();
                bool validToken = accessTokenValidator.ValidateToken(accessToken, scope);

                if (!validToken)
                {
                    var response = new HttpResponseMessage
                    {
                        Content =
                            new StringContent("This token is not valid, please refresh token or obtain valid token!"),
                        StatusCode = HttpStatusCode.Unauthorized
                    };
                    throw new HttpResponseException(response);
                }
            }
            else
            {
                var response = new HttpResponseMessage
                {
                    Content =
                        new StringContent("You must supply valid token to access method!"),
                    StatusCode = HttpStatusCode.Unauthorized
                };
                throw new HttpResponseException(response);
            }

            base.OnActionExecuting(actionContext);
        }
    }



And here is AccessTokenValidator class:

public class AccessTokenValidator : IAccessTokenValidator
    {
        public bool ValidateToken(string token, string[] scope)
        {
            // replace this logic with dataBase access to table with tokens
            if (token != "someToken")
            {
                return false;
            }
            return true;
        }
    }

Force JSON DelegatingHandler for ASP.NET Web API

3. March 2012  · Comments (0)

 

Building real world REST API-s

 

When you decide to build real world REST API-s like Facebook or Foursquare and many others have you must think on lot of things such as security, scoping, throttling and similar.

One of the first thing you will encounter is need to support various media formats such as JSON, XML, PNG … in easy way.

ASP.NET Web API support JSON and XML formats out of the box and provide us with easy way to support any custom mediaType format.

I will try to provide you with series of posts in which I will try to explain how I have created real world REST API using ASP.NET Web API.

 

Common ways to enforce custom format

 

There are several ways to enforce custom format in REST API :

1) You can pass mediaType in request header .

2) You can pass mediaType in URI using following syntax : http://api.mySite.com/v1/contacts/1/JSON

3) You can pass mediaType in URI using following syntax : http://api.mySite.com/v1/contacts/1.JSON

4) You can pass mediaType in URI using following syntax : http://api.mySite.com/v1/contacts/1?mediaType=JSON

 

 

My preferred solution using ASP.NET Web API

 

First approach – I will support 

In REST API you will sure want to support first approach in which user passes mediaType in request header.

Second approach – I will not support

For second approach things are little difficult .

To support this way you will probably need to change routes like this where you pass optional parameter ext for mediaType extension :

config.Routes.MapHttpRoute(
                "Default", 
                "{controller}/{id}/{ext}", 
                new { id = RouteParameter.Optional, ext = RouteParameter.Optional } 
            );

Problem with this approach is that your code will only work for URI like this where you try to get particular contact with id 1 in JSON format:

http://api.mySite.com/v1/contacts/1/JSON

When you try to get all contacts in JSON format using URI like this it will fail:

http://api.mySite.com/v1/contacts/JSON

Instead of trying to get all contacts in that format in will try to find contact which has id with value “JSON” .

One possible way to resolve this is to have two controllers for same model. One called ContactController another ContactsController.

I just don’t prefer this solution. If you know any other solution to resolve this feel free to comment. You can read more about this in this sites :

http://codebetter.com/glennblock/2012/02/28/why-are-there-2-controllers-in-the-asp-net-web-api-contactmanager-example-rest-has-nothing-to-with-it-2/

http://wekeroad.com/2012/02/28/someone-save-us-from-rest/

Third approach – I will not support

You may be wondering why it is so nice and clean? I don’t like dots in URI in that way. You cannot parse that QueryString safely to get that extensions behind dot.

You can try do something like this :

requestString.EndsWith(".JSON")

But then you will get another wall. In this version of ASP.NET Web API when you try to change Request.RequestUriinside DelegatingHandlers method SendAsync and pass changed RequestUri to base, ASP.NET Web API still try to goto old RequestUri. It seams like routing is performed before DelegatingHandler…So if we try to get mediaType in DelegatingHandler from RequestUri :

http://api.mySite.com/v1/contacts/1.JSON

then substring URI to become:

http://api.mySite.com/v1/contacts/1

and pass new RequestUri to base we will not be successful.

Fourth approach – I will support

I have choose to support fourth approach. For using this approach you will need to add this line of code in GlobalAsax.cs :

config.MessageHandlers.Add(new UriFormatExtensionMessageHandler());

Here is my code for UriFormatExtensionMessageHandler :

public class UriFormatExtensionMessageHandler : DelegatingHandler
   {    
       private bool shouldForceJson;
       private bool sholudForceXml;
       public void CheckFormats(HttpRequestMessage request)
       {
           // uri must contain mediaType parameter to enforce that media type
           string query = request.RequestUri.Query;
           string mediaType = HttpUtility.ParseQueryString(query).Get("mediaType");
           if (mediaType != null)
           {
               switch (mediaType)
               {
                   case "JSON":
                       shouldForceJson = true;
                       break;
                   case "XML":
                       sholudForceXml = true;
                       break;
               }
           }
       }

protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, 
System.Threading.CancellationToken cancellationToken)
       {
           CheckFormats(request);
           if (shouldForceJson)
           {
               // clear the accept and replace it to use JSON.
               request.Headers.Accept.Clear();
               request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
               shouldForceJson = false;
           }

           if (sholudForceXml)
           {
               request.Headers.Accept.Clear();
               request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
               sholudForceXml = false;
           }
             
           return base.SendAsync(request, cancellationToken);
       }
   }

UPDATE :

For fourth approach Microsoft has provided solution in this version of WebApi.You should you code bellow :

config.Formatters.JsonFormatter.MediaTypeMappings.Add(
              new QueryStringMapping("mediaType", "json", "application/json"));

            config.Formatters.XmlFormatter.MediaTypeMappings.Add(
               new QueryStringMapping("mediaType", "xml", "application/xml"));
 

Blog Redesign

7. October 2011  · Comments (2)

 

Blog design revamped

 

I have decided after long period of not blogging to start over again and best thing you can do for fresh start is to redesign your blog. My choice this time is light background instead of dark background. It seams at least for me that is easier to read and focus on content on light backgrounds. This is tiny screenshot how my old blog looked like :

 

clip_image001

 

I have also tried to incorporate minimalistic approach but I don’t know if I have succeeded.

Because I am not designer I have hired designer to do all work for me. You will be able to contact him soon if you want by link in bottom of page “Designed by…” that I will insert in few days.

 

Migrate old content to new design

 

I have decided to not migrate old blog content to new design. What I am mean by migrate is to change “Syntax highlighting” for code, reposition pictures, content and insert missing H1,H2 headers for some posts and similar.

Because this is fresh start I want to start fresh and not go back to old content.

 

My writing focus

 

My old readers probably are aware that my focus was Silverlight and .NET development. I will continue to have focus on Silverlight because Silverlight probably will remain best technology for development intranet browser applications for long time.

I will also have strong focus on Kinect, Azure, Windows Phone and Win 8 development.

I have worked on some projects that involve these technologies and I will try to write about things I have learned on this blog.

 

Your comments?

 

What do you think about my new blog design and choice of my writing focus ?