Category Archives: Web API

Web API, HttpError and the behavior of Exceptions – ‘An error has occurred’

Api Error

When you deploy an ASP.Net Web Api project to a server in RELEASE mode configuration and you have Custom Errors set to On, you’ll likely notice that your once nicely formatted error responses are no longer so friendly.

During local development web api errors are formatted nicely with messages, stack trace, etc.

XML
<Error><Message>An error has occurred.</Message><ExceptionMessage>The method or operation is not implemented.</ExceptionMessage><ExceptionType>System.NotImplementedException</ExceptionType><StackTrace> at AppCenter.Web.Controllers.ApplicantsController.&lt;Post&gt;d__a.MoveNext() in e:\Sample.Web\Controllers\HomeController.csline 86</StackTrace></Error>

JSON
{
"Message":"An error has occurred.",
"ExceptionMessage":"The method or operation is not implemented.",
"ExceptionType":"System.NotImplementedException",
"StackTrace":" at AppCenter.Web.Controllers.ApplicantsController.d__a.MoveNext() in e:\Sample.Web\Controllers\HomeController.cs:line 86"
}

And here is the same thing when deployed:

XML
<Error><Message>An error has occurred.</Message></Error>

JSON
{
"Message":"An error has occurred."
}

As you can see, if you want your errors to flow to the consuming app, this is not ideal. You likely will (and should) want to return your errors in an object that has a friendly error message, and optionally, detailed message, error code, and even an error reference for lookup.

Here is an excerpt from the Apigee e-book “Web API Design – Crafting Interfaces that Developers Love”:

How to think about errors in a pragmatic way with REST?

Let’s take a look at how three top APIs approach it.

Facebook
HTTP Status Code: 200
{
“type”:”OauthException”,
“message”:”(#803) Some of the aliases you requested do not exist: foo.bar”
}

Twilio
HTTP Status Code: 401
{
“status”:”401″,
“message”:”Authenticate”,
“code”:20003,
“more info”:”http://www.twilio.com/docs/errors/20003&#8243;
}

SimpleGeo
HTTP Status Code: 401
{
“code”:401,
“message”:”Authentication Required”
}

I like these patterns, but I especially like the following format:
{
"developerMessage":"Verbose, plain language description of the problem for the app developer with hints about how to fix it.",
"userMessage":"Pass this message on to the app user if needed.",
"errorCode":12345,
"more info":"http://dev.teachdogrest.com/errors/12345"
}

When dealing with web api and exceptions, there are a few things that you must realize:

ALL errors eventually are serialized into an HttpError object.

* manually thrown exceptions
* uncaught exceptions
* responses created using the Request.CreateErrorResponse extension method

HttpResponseException’s are treated as “caught” or handled errors

That means that when you manually throw an HttpResponseException OR you use Request.CreateErrorResponse – the errors will not flow to any ExceptionFilterAttributes you may have created. That means, if you use a library like Elmah to handle your Exception reporting, these will NOT be reported.

The Ideal Developer Experience for Exceptions and Errors (at least this is my ideal)

I want to be able to consistently report my exceptions in a consistent and friendly format.
I don’t want to have to worry about the different overloads of Request.CreateErrorResponse.
I want to be able to configure the way exceptions are dealt with. I don’t want developers on my projects to have to worry about getting creative with their exception handling and reporting. I don’t want it done one way here, one way there, etc.

My Solution

I created an ExceptionFilterAttribute that allows me to configure all my exceptions in one central place a static class in the App_Start folder (this is the preferred method these days it seems).

Here is my code:


    public class MvcApplication : System.Web.HttpApplication
    {

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            WebApiExceptionConfig.RegisterExceptions(
                 GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }


using System;
using System.Collections.Generic;
using System.Net;
using System.Web.Http;
using System.Web.Security;

    public static class WebApiExceptionConfig
    {
        public static void RegisterExceptions(HttpConfiguration config)
        {

            /* /// this is the easiest way to shove the exception messages into the httperror message property for ALL unhandled exceptions
            
            config.Filters.Add(new GlobalApiExceptionFilterAttribute(catchUnfilteredExceptions: true));
            
            */

            config.Filters.Add(new GlobalApiExceptionFilterAttribute(new List<GlobalApiExceptionDefinition>
            {

                /* /// Example 1 -- setting the error code and reference properties
                new GlobalApiExceptionDefinition(typeof(NotImplementedException)) { ErrorCode = "123456.cows", ErrorReference = "http://www.google.com?q=cows" },
                */

                /* /// Example 2 -- using the friendly message string overload
                new GlobalApiExceptionDefinition(typeof(NotImplementedException), "This method is really wonky", HttpStatusCode.NotAcceptable) { ErrorCode = "123456.cows", ErrorReference = "http://www.google.com?q=cows" },
                */

                /* /// Example 3 -- using the friendly message predicate overload
                new GlobalApiExceptionDefinition(typeof(MembershipCreateUserException), (ex) => MembershipHelper.MembershipCreateStatusToString((ex as MembershipCreateUserException).StatusCode), HttpStatusCode.Conflict)
                */

                new GlobalApiExceptionDefinition(typeof(MembershipCreateUserException)) 
                {
                    Handle = (ex) => // we want to make sure the server error status codes are respected - we want to send back a 500
                    {
                        if (ex is MembershipCreateUserException) 
                        {
                            var mex = ex as MembershipCreateUserException;
                            switch (mex.StatusCode)
                            {
                                case MembershipCreateStatus.DuplicateProviderUserKey:
                                case MembershipCreateStatus.InvalidProviderUserKey:
                                case MembershipCreateStatus.ProviderError:
                                    return true;
                                default:
                                        break;
                            }
                        }
                        return false;
                    }
                },
                new GlobalApiExceptionDefinition(typeof(MembershipCreateUserException), statusCode: HttpStatusCode.Conflict) // this will send back a 409, for all other types of membership create user exceptions
            }, catchUnfilteredExceptions: true));
        }
    }


using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Filters;


    public class GlobalApiExceptionFilterAttribute : ExceptionFilterAttribute
    {

        const string ERROR_CODE_KEY = "ErrorCode";
        const string ERROR_REFERENCE_KEY = "ErrorReference";

        List<GlobalApiExceptionDefinition> exceptionHandlers;
        bool catchUnfilteredExceptions;

        public GlobalApiExceptionFilterAttribute(
            List<GlobalApiExceptionDefinition> exceptionHandlers = null, bool catchUnfilteredExceptions = false)
        {
            this.exceptionHandlers = exceptionHandlers ?? new List<GlobalApiExceptionDefinition>();
            this.catchUnfilteredExceptions = catchUnfilteredExceptions;
        }

        public override void OnException(HttpActionExecutedContext actionExecutedContext)
        {
            var exception = actionExecutedContext.Exception;
            GlobalApiExceptionDefinition globalExceptionDefinition = null;
            HttpStatusCode statusCode = HttpStatusCode.InternalServerError;

            if (LookupException(actionExecutedContext.Exception, out globalExceptionDefinition) || catchUnfilteredExceptions)
            {
                // set the friendly message
                string friendlyMessage = globalExceptionDefinition != null ? globalExceptionDefinition.FriendlyMessage(exception) : exception.Message;

                // create the friendly http error
                var friendlyHttpError = new HttpError(friendlyMessage);

                // if we found a globalExceptionDefinition then set properties of our friendly httpError object accordingly
                if (globalExceptionDefinition != null)
                {
                    
                    // set the status code
                    statusCode = globalExceptionDefinition.StatusCode;

                    // add optional error code
                    if (!string.IsNullOrEmpty(globalExceptionDefinition.ErrorCode))
                    {
                        friendlyHttpError[ERROR_CODE_KEY] = globalExceptionDefinition.ErrorCode;
                    }

                    // add optional error reference
                    if (!string.IsNullOrEmpty(globalExceptionDefinition.ErrorReference))
                    {
                        friendlyHttpError[ERROR_REFERENCE_KEY] = globalExceptionDefinition.ErrorReference;
                    }

                }

                // set the response to our friendly http error
                actionExecutedContext.Response = actionExecutedContext.Request.CreateErrorResponse(statusCode, friendlyHttpError);

            }

            // flow through to the base
            base.OnException(actionExecutedContext);
        }

        private bool LookupException(Exception exception, out GlobalApiExceptionDefinition exceptionMatch)
        {
            exceptionMatch = null;

            var possibleMatches = exceptionHandlers.Where(e => e.ExceptionType == exception.GetType());
            foreach (var possibleMatch in possibleMatches)
            {
                if (possibleMatch.Handle == null || possibleMatch.Handle(exception))
                {
                    exceptionMatch = possibleMatch;

                    return true;
                }
            }

            return false;
        }
        
    }

    public class GlobalApiExceptionDefinition
    {

        const string ARGUMENT_NULL_EXCEPTION_FMT = "Argument '{0}' cannot be null.";
        const string ARGUMENT_MUST_INHERIT_FROM_FMT = "Type must inherit from {0}.";

        public Type ExceptionType { get; private set; }
        public Func<Exception, string> FriendlyMessage { get; private set; }

        public Func<Exception, bool> Handle { get; set; }
        public HttpStatusCode StatusCode { get; set; }

        public string ErrorCode { get; set; }
        public string ErrorReference { get; set; }

        public GlobalApiExceptionDefinition(Type exceptionType, string friendlyMessage = null, HttpStatusCode statusCode = HttpStatusCode.InternalServerError) :
            this(exceptionType, (ex) => friendlyMessage ?? ex.Message, statusCode) { }

        public GlobalApiExceptionDefinition(Type exceptionType, Func<Exception, string> friendlyMessage, HttpStatusCode statusCode = HttpStatusCode.InternalServerError)
        {

            AssertParameterIsNotNull(friendlyMessage, "friendlyMessage");
            AssertParameterIsNotNull(exceptionType, "exceptionType");
            AssertParameterInheritsFrom(exceptionType, typeof(Exception), "exceptionType");

            ExceptionType = exceptionType;
            FriendlyMessage = friendlyMessage;
            StatusCode = statusCode;
        }

        #region "Argument Assertions"

        private static void AssertParameterInheritsFrom(Type type, Type inheritedType, string name)
        {
            if (!type.IsSubclassOf(inheritedType))
            {
                throw new ArgumentException(string.Format(ARGUMENT_MUST_INHERIT_FROM_FMT, inheritedType.Name), name);
            }
        }

        private static void AssertParameterIsNotNull(object parameter, string name)
        {
            if (parameter == null)
            {
                throw new ArgumentNullException(name, string.Format(ARGUMENT_NULL_EXCEPTION_FMT, name));
            }
        }

        #endregion

    }

You can download the source here.

Matt Milner Web API presentation

Twin Cities Code Camp 2012 – Day 1

This post will be a compilation of my notes from Twin Cities Code Camp 2012.

Understanding WebAPI in ASP.NET MVC 4 – Matt Milner – Rapson 56

What is web api?

  • API for HTTP Services – client and server components
  • WCF vs MVC – why do we need this api?
  • WCF – some people just want HTTP, REST services is complicated. Global error handling is almost impossible. Goal was to make WCF extremely flexible. Transport neutral just like SOAP. Because WCF was trying to cover ALL transports and protocols, HTTP became buried.

MVC was primarily built to pass back a View – typically HTML. JSON/JSONP were after thoughts. This is not a perfect model for RESTful services.

HTTP is a protocol in and of itself. Don’t need another protocol (e.g. SOAP). The protocol has been optimized over the years.

HTTP is widely accessible and MUCH easier to reach ALL devices.

Web API MVC Integration

  • MVC is for web applications
  • Web API is for web APIs
  • what they have in common is usually the model or domain

Cool new features: media type formatters for resource type handling. validation at the handler level which supports cancelation and pass-through instead of lowest level – the action. Better json support with the new System.Json library. Self-hosted testing built upon a partial wcf service stack. Support for OData URI queries. Simply return IQueryable. CONS: partial support for OData – only $page, $filter, and $sort – not $select. Doesn’t seem like the will be adding support for this. There is talk about adding built in support for Expression for DTO transformation on the server side.

There is also talk about combining the ServiceResolver configuration (configuration.ServiceResolver.SetResolver) to support both MVC and WebAPI in the same call. For example, right now you configure your MVC service resolving and a separate configuration for WebAPI service resolving. Simplification of this is on the table. Hopefully by the time the release comes around, we see a unified method.

TAKEAWAY MESSAGE: FORGET COMPLICATED SOAP PROTOCOL which is the standard for RIA Services and WCF. Simplify your api with REST. Forget about the mvc paradigm of method based access to your resources. You now access your resources via GET, POST, PUT, DELETE. You access a resource from a Controller. One controller per type of resource. Web API has content negotiation. It returns the data in the format that your request based on the Accept header. If I want JSON, I get JSON. You could expand upon this to return resources in whatever format you need; image, doc, resume, and more. The same for xml. Etc… Input validation is handled by the model binding mechanisms of MVC which builds a model state object.

Note: WebAPI is NOT a replacement for WCF. If you still need to support multiple endpoints in different protocols (for example pure TCP), then WCF is still the best option out there. If you want a simplified API that will be accessible for the widest audience, consider WebAPI.

NOTE TO SELF: check out $.validator.unobtrusive.revalidate(form, validationResult) – validationResult is the json object; { “Speaker”: “Speaker is required” } – where in this example Speaker is the name attribute of the form input element – this takes a json object and interprets it using jquery.validation unobtrusive. Is this a NEW method?

Async Today and Tomorrow – Joe Mayo – Rapson 54

Talk about the Async CTP. How we use async programming today and how we will use it in the future. The Async CTP hides the complexity of async programming. It leverages the TPL (Tasks Parallel Library).

Why do we need async for UI responsiveness? Ex. click on a button in a Windows Form. The program does some long running operation. The user doesn’t know what is happening until all of a sudden, the UI shows something. NOT A GOOD USER EXPERIENCE.

TPL at it’s core revolves around the Task class. It has a fluent api. Uses continuation methods (similar to callbacks).

  • Language Integration – Changes to C# and VB in .NET. Async – the await and async Context Keywords.
  • Every platform – Desktop, Web, and Device
  • leverage Opportunities – Clarity, lifetime, progress, UI, and exceptions

void functions – regular event handlers
functions that return typeof Task<T>– represents the ongoing operation – get exceptions off of that task. The Task wraps the <T> – it holds information about what happened when it returned the <T>.

Async CTP code looks like synchronous code except for the await and async keywords.

Previous convention was to write methods with the Async as the end of the method name. For example WriteAsync. The new paradigm is to write the method as WriteTaskAsync.

Everything after the await keyword is the continuation.

Multiple tasks in parallel… Task.WhenAll.  Or alternatively, Task.WhenAny.  NEAT!

Task based Async Patterns – continuation, cancellation, etc. Task.Wait(), AggregateException, ae.InnerExceptions, CancellationToken

Notes:  this is going to be a fun library to work with.  I can hardwly wait to get home and start coding!  That was not sarcasm but true enthusiasm.

Android Development for the .NET Developer

This is about native Android development, not developing using PhoneGap or MonoDevelop.

http://mike-marshall.net outlines the way to install the JDK, SDK, and everything you need to get an Android IDE up and running here.

  • android.app.Activity class
  • Combined with layouts (views), represents a single type of work that can be accomplished in your application
  • Reusable
    • Can be chained within your application to build a workflow
    • Can be published for use by other applications through :Intents” (more later)

Outlines how to create an Android Application Project within Eclipse.

The .net delegate event handler does not apply.

Working with resources can be annoying.

“Form” designer is very similar to XAML designer in visual studio.  Nothing is absolutely positioned unless you tell it to be as such.