Using JSON.net to consume the JSON Stack Overflow API

On February 7, 2012, in ASP.net, MVC3, by jonathancreamer

JSON has rapidly grown arguably the most popular way to transfer data via API’s. It’s fast, simple, and most every language supports it. ASP.net has a few things like the JavaScript Deserializer Class that do a decent job, but one of the better tools available is JSON.net.

What a better way to test it than to use a real world example. StackOverflow has a public facing JSON api available at api.stackoverflow.com/1.1/ so I’ll show how to consume that one.

First, fire up whatever sort of app you want and then head to the Tools –> Library Package Manager –> Package Manager Console (aka. NuGet) and type in Install-Package Newtonsoft.Json

After some NuGet-y magic you’ll have JSON.net at your finger tips.

For starters we’ll consume the Users method of the API. Hitting the Url, api.stackoverflow.com/1.1/users/558672  It looks like…

spacer

The fastest way to consume it with JSON.net is by using the JsonConvert.DeserializeAnonymousType method.

var client = new WebClient();
byte[] response = client.DownloadData("api.stackoverflow.com/1.1/users/558672");
var decompress = new GZipStream(new MemoryStream(response), CompressionMode.Decompress);
var reader = new StreamReader(decompress);
string ret = reader.ReadToEnd();

var stackResponse = new
{
    total = 0,
    page = 0,
    pagesize = 0,
    users = new[]
    {
        new {
            user_id = 0,
            user_type = "",
            creation_date = "",
            display_name = "",
            reputation = 0,
            email_hash = "",
            age = 0,
            last_access_date = 0,
            website_url = "",
            location = "",
            about_me = "",
            question_count = 0,
            answer_count = 0,
            view_count = 0,
            up_vote_count = 0,
            accept_rate = 0,
            bagde_counts = new {
                gold = 0,
                silver = 2,
                bronze = 9
            }
        }
    }
};

var stackUser = JsonConvert.DeserializeAnonymousType(ret, stackResponse);

One thing to note is that StackOverflow Gzips all of their API responses, that’s what all that decompress stuff is about. So, THAT is it as far as the quickest way to consume a JSON Api with JSON.net.

The next more involved way to consume the API is to create your own classes that mirror the structure of the API and deserialize the response into them. This can look a variety of ways potentially, but here is my implementation.

public class StackUser : StackBase
{
    [JsonProperty("user_id")]
    public int UserId { get; set; }

    [JsonProperty("user_type")]
    public string UserType { get; set; }

    [JsonProperty("display_name")]
    public string DisplayName { get; set; }

    [JsonProperty("reputation")]
    public int Reputation { get; set; }

    [JsonProperty("email_hash")]
    public string Email { get; set; }

    [JsonProperty("age")]
    public int Age { get; set; }

    [JsonProperty("last_access_date")]
    public int LastAccessDateInt { get; set; }

    [JsonProperty("website_url")]
    public string Website { get; set; }

    [JsonProperty("location")]
    public string Location { get; set; }

    [JsonProperty("about_me")]
    public string AboutMe { get; set; }

    [JsonProperty("question_count")]
    public int QuesionCount { get; set; }

    [JsonProperty("answer_count")]
    public int AnswerCount { get; set; }

    [JsonProperty("view_count")]
    public int ViewCount { get; set; }

    [JsonProperty("up_vote_count")]
    public int UpVoteCount { get; set; }

    [JsonProperty("down_vote_count")]
    public int DownVoteCount { get; set; }

    [JsonProperty("accept_rate")]
    public int AccpetRate { get; set; }

    [JsonProperty("association_id")]
    public Guid AssociationId { get; set; }

   [JsonProperty("badge_counts")]
    public Dictionary Badges { get; set; }
}


This class represents all of the properties for a User. The JsonProperty attribute specifies what the actual property looks like in the JSON response.

public interface IUserRepository
{
    StackUser GetUserById(params string[] ids);
}

public class Users : StackRepository<UserResponse>, IUserRepository
{
    public Users()
    {
        Method = "users";
    }

    public StackUser GetUserById(params string[] ids)
    {
        var userResponse = Get(ids);
        return userResponse.Users.FirstOrDefault();
    }
}

public class StackResponse : IStackResponse
{
    public int Total { get; set; }
    public int Page { get; set; }
    public int PageSize { get; set; }
}

public class UserResponse : StackResponse
{
    public List Users { get; set; }
}


Here I created a UsersRepository to consume all of the methods available for users and declared UsersResponse and a base StackResponse for the paging information that comes back on every request. Then within the UserResponse I get back the StackUser object off of UserResponse.Users

public abstract class StackRepository : IStackRepository
{
    public string Method { get; set; }
    public RequestOptions Options { get; set; }
    private const string ApiUrl = "api.stackoverflow.com/1.1/";

    public string Request(string request, RequestOptions options = null)
    {
        var client = new WebClient();
        var stackRequest = new StringBuilder(ApiUrl + request);        

        byte[] response = client.DownloadData(stackRequest.ToString());

        if(response == null)
        {
            throw new Exception(string.Format("No response from request {0}", request));
        }

        var decompress = new GZipStream(new MemoryStream(response), CompressionMode.Decompress);
        var reader = new StreamReader(decompress);
        string ret = reader.ReadToEnd();
        return ret;
    }

    public T Get(params string[] ids)
    {
        var response = Request(Method + string.Join(",", strings);
        return Deserialize(response);
    }

    public T Deserialize(string response)
    {
        return JsonConvert.DeserializeObject(response);
    }
}

public class StackOverflow : IStackOverflow
{
    IUserRepository _userRepo;

    public StackOverflow(IUserRepository userRepository)
    {
        _userRepo = userRepository;
    }

    public StackUser GetUserById(params string[] ids)
    {
        var users = _userRepo.GetUserById(ids);
        return users;
    }
}


The StackRepository class here takes in a Generic class that will represent the type of Response that will be returned from the Api, such as the UserResponse that I created above. To use the StackOverflow object, just do this…

var stackOverflow = new StackOverflow();
var user = stackOverflow.GetUserById("558672");

 

So that is the slightly more complicated way to do it. It takes a little bit more setup, but it makes it a little bit easier to work with and makes the objects re-usable.

Have fun with JSON.net!

UPDATE 2/8/2012

As per a guest recommendation in the comments, there actually is a third way to deserialize the data.

var client = new WebClient();
var stackRequest = new StringBuilder(ApiUrl + request);        

byte[] response = client.DownloadData(stackRequest.ToString());

if(response == null)
{
    throw new Exception(string.Format("No response from request {0}", request));
}

var decompress = new GZipStream(new MemoryStream(response), CompressionMode.Decompress);
var reader = new StreamReader(decompress);
string ret = reader.ReadToEnd();
dynamic userResponse = JObject.Parse(ret);
var displayName = userResponse.users[0].display_name;
Console.Write(displayName); 

So there you go, 3 ways to use JSON.net to work with a JSON Api!

3 Comments
Leave A Response

Tagged with: deserialize • deserializeanonymous • json • json.net • json.net mvc3 api • serialize • stack overflow 

Minify JavaScript using UglifyJS and NodeJS

On January 10, 2012, in NodeJS, by jonathancreamer

Minifying JavaScripts has many benefits. A few advantages are it reduces the amount of code the user has to download, removes unnecessary comments, and reduces the number of Http requests. There are many minify-ers out there, things such as YUI Compressor, JSMin, Google Closure Compiler, and UglifyJS.

I recently started using the standalone UglifyJS tool that can run in NodeJS. This makes the tool available via command prompt as well as accessing it inside of Node applications with require.

To use Uglify in Windows. First, download and install NodeJS. Once it is installed, you can then run applications in Command Prompt simply by typing node. It installs by default into C:\Program Files\NodeJS or C:\Program Files(x86)\NodeJS on a 64-bit machine.

It drops the node.exe, and the npm.bat file into that directory and makes it available in CMD by adding it to your %PATH%.

NPM is the Node Package Manager that was just recently made available for Windows. It allows you to download Node packages to be easily used in your projects.

The next step is to install UglifyJS and make it global so it’s available everywhere.

Open Command Prompt and run…

npm -g install uglify-js

This put’s the files for Uglify in the %AppData% file, usually around C:\Users\{username}\AppData\Roaming\npm\

As of 1/9/2012, there is a slight bug in the command to actually run Uglify, so in order to fix it, navigate to the folder just mentioned and find uglifyjs.cmd and replace it with…

:: Created by npm, please don't edit manually.
@IF EXIST "%~dp0"\"node.exe" (
  "%~dp0"\"node.exe"  "%~dp0\.\node_modules\uglify-js\bin\uglifyjs" %*
) ELSE (
  node  "%~dp0\.\node_modules\uglify-js\bin\uglifyjs" %*
)

Now, you will be able to just run Uglify by…

cd c:\place\where\files\are\
uglifyjs -o myFile.build.js myFile.js

And wham bam, you’ll find a nicely compressed file. NOTE: You also may need to restart the computer. Sometimes the changes to the path don’t kick in right away. You can string along as many files as you need to compress; the –o flag tells Uglify where to place the built file. Then you could save the commands out to some build.bat file and run it at your plessure.

4 Comments
Leave A Response

Tagged with: build • compress • JavaScript • minify • nodejs • uglifyjs 

Espresso Tip: Changing those nasty constraint names in Code First

On December 20, 2011, in Uncategorized, by jonathancreamer

When working with Entity Framework Code First, the database get’s generated for you… Duh!

You have control over several aspects of how this database get’s created.

First off are Data Annotations, such as Required, DefaultValue, ForeignKey, Key, etc.

Second is the Fluent Api which allows you to tap into the creation of the Database and remove all of those attributes off of your data classes.

You can specify the name of the database by passing a Name, or even a connectionString into the base constructor of your DbContext.

 

public class MyContext : DbContext
{
    public MyContext()
        base: ("MyDbName")
    {}
}

Lot’s of different control over the DB.

However, one thing that seems to be left out for now is explicitly naming your Primary Key constraints. By default, they come out looking something like… PK_USERS_FBC301?!?!?!? 

I think this is a default of SQL more than Code First’s fault or anything, but none the less it is kinda nasty.

The only way I have found to fix this problem is to tap into the Initialize method of the Context using either DropCreateDatabaseAlways, DropCreateDatabaseIfModelChanges, or by implementing your own version of IDatabaseInitializer. I did it like this…

 

// Somewhere in Global.asax
Database.SetInitializer(new DbInitializer());

// DbInitializer
public class DbInitializer : IDatabaseInitializer<MyContext>
{
    public void InitializeDatabase(MyDbcontext context)
        {
            if (context.Database.Exists())
            {
                if (!context.Database.CompatibleWithModel(true))
                {
                    string singleUserModeCommand =
                    string.Format("ALTER DATABASE [{0}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE",
                              context.Database.Connection.Database);
                    if (context.Database.Exists())
                    {
                        context.Database.ExecuteSqlCommand(singleUserModeCommand);
                        context.Database.Delete();
                    }
                    context.Database.Create();
                    Seed(context);
                }

            }
            else
            {
                context.Database.Create();
                Seed(context);
            }
        }

        protected void Seed(MyDbContext context)
        {
            FixPrimaryKeyConstraints(context);
        }

        private void FixPrimaryKeyConstraints(MyDbContext context)
        {
            const string sql =
                @"  SET NOCOUNT on
                    DECLARE @cnt int
                    DECLARE @table varchar(128)
                    DECLARE @cmd varchar(500)
                    --create table #rowcount (tablename varchar(128), rowcnt int)
                    DECLARE tables cursor for
                    SELECT table_name from information_schema.tables
	                    WHERE table_type = 'base table'
	                    ORDER BY table_name
                    OPEN tables
                    FETCH NEXT FROM tables into @table
                    WHILE @@fetch_status = 0
                    BEGIN
	                    DECLARE @TableName NVARCHAR(128)
	                    DECLARE @IndexName NVARCHAR(128)
	                    DECLARE @OldName NVARCHAR(128)
	                    DECLARE @NewName NVARCHAR(128)
	                    SELECT  @TableName = @table

	                    SELECT  @IndexName = C.CONSTRAINT_NAME
	                    FROM    INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ,INFORMATION_SCHEMA.KEY_COLUMN_USAGE C
	                    WHERE   pk.TABLE_NAME = @TableName
		                    AND   CONSTRAINT_TYPE = 'PRIMARY KEY'
		                    AND   C.TABLE_NAME = PK.TABLE_NAME
		                    AND   C.CONSTRAINT_NAME = PK.CONSTRAINT_NAME

	                    SELECT  @OldName = @TableName + '.' + @IndexName
	                    SELECT  @NewName = 'PK_' + @TableName
	                    exec sp_rename @OldName, @NewName, 'INDEX'
                      FETCH NEXT FROM tables into @table
                    END
                    CLOSE tables
                    DEALLOCATE tables";

            context.Database.ExecuteSqlCommand(sql);
        }
}

This file does a few things. First it detects changes to the DB, forces a drop if neccessary, then runs the seed.
The seed then calls the FixPrimaryKeyConstraints which loops over all of the tables in the Database and renames them PK_TableName.

Hope you enjoyed this Espresso Tip!

3 Comments
Leave A Response

 

BackboneJS modular app using RequireJS

On December 9, 2011, in BackboneJS, JavaScript, RequireJS, by jonathancreamer

Write the App

Everyone knows that large scale JavaScript applications can get out of control really quickly really fast. Tools like jQuery are helpful in providing easy access to the DOM, but provide little to no help when piecing together a full fledge application. You’ll begin writing a few selectors here and there, manipulating some data, making some Ajax requests, and next thing you know your knee deep in a big bowl of Spaghetti code!

Bring on RequireJS.

RequireJS is a framework that adheres to the ComonJS specification for Asynchronous Modular Definitions (AMD). See this post by a fellow brewer, Derek Greer for some more info on getting started with Requirejs.

The basic idea behind RequireJS and AMD is to separate your code into Modules, and by Modules, I mean this definition found by Nicholas Zakas…

Module: An independent self-contained unit of a spacecraft.

spacer

Here, each module can exist separately, or together with the rest of the space ship.

This is a common way to solve JavaScript architecture as well.

To build a BackboneJS app with RequireJS…

  1. Download RequireJS (I used the Sample Require + jQuery App)
  2. Download the Order plugin
  3. Download UnderscoreJS (Backbone depends on Underscore)
  4. Get one of the AMD versions of Backbone from @jrburke off of one of his optamd branches. I used this one https://github.com/jrburke/backbone/blob/optamd/backbone.js
  5. Create a folder structure, and files like… (Bold means create a new empty file)
    /r.js (Copy from the RequireJS download)
    /app/index.htm
    /app/scripts/
    /app/scripts/require-jquery.js (Copy from the RequireJS download)
    /app/scripts/main.js
    /app/scripts/app.js
    /app/scripts/order.js
    /app/scripts/lib/underscore.js
    /app/scripts/lib/backbone.js
    /app/scripts/routers/home.js
    /app/scripts/views/welcome.js
    /app/scripts/models/movie.js
    /app/scripts/collections/movies.js
  6. Build models, views, routers, and collections at your leisure

First up is the index.htm file…

<!DOCTYPE html>
<html>
<head>
<title>BackboneJS Modular app with RequireJS</title>
<script data-main="scripts/main" src="/img/spacer.gif"> 

Then there’s main.js. This is where you’ll define the base of how your application will load…

require.config({
    'paths': {
        "underscore": "libs/underscore",
        "backbone": "libs/backbone"
    }
}); 

require([
	'order!libs/underscore',
	'order!libs/backbone',
	'order!app'
	],
	function(_,Backbone,app){
          app.init();
});

The require.config set’s up some paths for the modules that are defined with a name. Next, the require function takes in 2 arguments. First is an array of dependencies that main.js will load in. The order plugin makes sure that Underscore and Backbone get loaded in order as Require typically loads things in asynch which is bad for Backbone since it requires Underscore. Using the require-jquery verion includes jQuery 1.7 which now has AMD built in.  app references the app.js file you will create in a second. The function callback takes the loaded dependencies and passes them as arguments in the order you loaded them in, _, Backbone, and app.

Now for the app.js

define(['routers/home'], function(router){
    var init = function(){
        console.log("App Started...");
    };

    return { init: init};
});

Here we use define to create a new module. The first argument is an array of dependencies, and the second is a callback like the one on main.js which will fire once the dependencies have loaded. For now I am just requiring the home.js router.

The home.js router looks like…

define(['backbone','views/welcome'],function(Backbone,welcome){
    var homeRouter = Backbone.Router.extend({
        initialize: function(){
            Backbone.history.start();
        },
        routes: {
            '': 'home' // Default route
        },
        'home': function(){
            welcome.render();
        }
    });

    return new homeRouter();
});

Here we load in Backbone, and the welcome.js view…

define(['jquery','backbone','underscore', 'collections/movies'], function($, Backbone, _, movies){
    var welcomeView = Backbone.View.extend({
        el: "#main",
        initialize: function(){
            this.movies = new movies();
            this.template = _.template($("#moviesTemplate").html());
            this.movies.bind('add', this.addMovie, this);
        },
        render: function(){
            this.movies.add({ name: 'Empire Strikes Back'});
            this.movies.add({ name: 'Jurrasic Park'});
            this.movies.add({ name: 'The Last Crusade'});
        }
        addMovie: function(model){
            $(this.el).append(this.template({ model: model.toJSON() });
        }
    });
});

This one loads in jQuery, backbone, underscore, and our movies collection which looks like…

define(['backbone','underscore','models/movie'],function(Backbone,_,movie){
    var movies = Backbone.Collection.extend({
        model: movie,

    });

    return movies;
});

From here we get down to the model, movie…

define(['backbone','underscore'],function(Backbone,_){
    var movie = Backbone.Model.extend({});

    return movie;
});

So that’s how to create a basic app with a router, view, collection, and model.

Build with Node or Java

One of the cool parts about Require is it’s ability to build and compress your application for you. It requires an app.build.js file, r.js(which you download with the RequireJS) jQuery app, and if you want to compile with Java, you’ll need Rhino and Clojure (Thanks to @jrburke again for this help page on r.js) or if you want to use NodeJS you’ll just need Node for your OSOC (Operating System of Choice) . The app.build.js file will typically look something like…

({
    appDir: "../",
    baseUrl: "scripts/",
    dir: "../../app-build/",
    //Comment out the optimize line if you want
    //the code minified by UglifyJS
    //optimize: "none",

    paths: {
        "jquery": "require-jquery",
		"underscore": "libs/underscore",
		"backbone": "libs/backbone"
    },

    modules: [
        //Optimize the require-jquery.js file by applying any minification
        //that is desired via the optimize: setting above.
        {
            name: "require-jquery"
        },

        //Optimize the application files. Exclude jQuery since it is
        //included already in require-jquery.js
        {
            name: "main",
            exclude: ["jquery"]
        }
    ]
})

This file is used to tell r.js how to build the application. There is help on requirejs.org/docs/optimization.html about the file. Basically r.js will look at the main module, and trace all of it’s dependencies, minify them, and combine them for you into one fun happy little minified package at your dir from the app.build.js file.

To run the optimizer in Java, go to the file above the root of app where r.js is located and use the command…

java -cp c:/path/to/rhino/js.jar;c:/path/to/compiler/compiler.jar; org.mozilla.javascript.tools.shell.Main r.js -o "c:/path/to/your/app/scripts/app.build.js"

If you are using node, then run this command…

node r.js app/scripts/app.build.js

And next thing you know, you’ll have an app-build folder with a shiny new minifed version of your app. The node optimizer is quite a bit faster than the java one, so I’d use that one.

So, that’s basically it! If you have any questions, let me know as I spent many hours figuring this thing out. The biggest help was when jQuery and UnderscoreJS both added AMD, and then once the Backbone AMD branches came out, it was much easier!

I have a simple working app called Savefavs up here at js.jcreamerlive.com and the source for it on Github. So take a look at that for a little more advanced stuff

I also have a Starter application here https://github.com/jcreamer898/RequireJS-Backbone-Starter that you can fork and use to build your own app.

2 Comments
Leave A Response

Tagged with: amd • backbonejs • commonjs • module • requirejs • underscorejs 

QUnit layout for JavaScript testing in ASP.net MVC3

On December 8, 2011, in JavaScript, by jonathancreamer

Testing JavaScript is something that a lot of people say they want to do, but never really get around to doing.

Why Not?!?

It’s extremely important stuff since JS is what the All-Important-User sees, especially these days with so much Javascript code being punched out.

One of the many testing frameworks out there is called QUnit and was written by the jQuery team to test jQuery. It has a very straightforward API, and is quite easy to use. The basic idea is this…

// calculator.js
var Calculator = function(){};

Calculator.prototype.add = function(x,y){
    return x + y;
}

// calculator_tests.js
module("A group of tests get's a module");
test("First set of tests", function(){
    var calc = new Calculator();
    ok(calc, "My caluculator is a O.K.")
    equals(calc.add(2,2), 4, "If this doesn't equal 4, the laws of the universe will be broken");
});

 

The module  just defines a set of tests, then each test in that module is defined with a name and a function callback to run the test. The two basic assertion functions in QUnit are ok(value, [successMessage]) and equals(actual, expected, [successMessage]).

The are also, notEqual, deepEqual, notDeepEqual, strictEqual, notStrictEqual, and raises.

The other piece needed to render the QUnit test looks like this.

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" class="/content/css/qunit.css" />
    <script src="/img/spacer.gif"> 

 

Below is a an easy way to share certain pieces of the tests layout in an ASP.net MVC3 application.

First create a Tests Controller. Each action method will correspond to different tests.

public class TestsController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Calculator()
    {
        return View();
    }
}

Then in the /Views/Tests folder you’ll create your views. The Index view I put a little voodoo in…

<ul>
@{
    var controller = ViewContext.Controller.GetType();
    var methods = controller.GetMethods()
        .Where(m => m.ReturnType.ToString() == "System.Web.Mvc.ActionResult");

    foreach(var method in methods)
    {
        <li>@Html.ActionLink(@method.Name, @method.Name, "Tests")</li>
    }
}
</ul>

This will render out a bulleted list with links to all of the tests you have in your TestsController. That way you’ll be able to go to Tests/ and see all of the tests in your application.

The next thing is to create Views/Tests/_ViewStart.cshtml

@{
    Layout = "_TestsLayout.cshtml";
}

And then for the Views/Tests/_TestsLayout.cshtml

<!DOCTYPE html>

<html>
    <head>
        <title>@ViewBag.Title</title>
        <link rel="stylesheet" class="/content/css/qunit.css" />
        <script src="/img/spacer.gif"> 

And each view will look like this…

@{
    ViewBag.Title = "Home Tests";
}

@section Javascript{
    <script src="/img/spacer.gif"> 

And last but not least the _Qunit.cshtml partial view

<h2>@ViewBag.Title</h2>

<h1 id="qunit-header">QUnit example</h1>
<h2 id="qunit-banner"></h2>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests">
</ol>

@Html.ActionLink("Back to Tests", "Index", "Tests")

Now all you have to do to create a new test is…

  1. Create the JS and JS_Tests files
  2. Add an Action Method to the Tests Controller
  3. Add a new View for the Method
  4. Change the JS references in the Javascript section

P.S. Checkout MockJax for mocking Ajax requests

5 Comments
Leave A Response

Tagged with: ASP.net • JavaScript • MVC3 • QUnit • testing • unit testing 

Nuget pre-build script

On December 2, 2011, in ASP.net, by jonathancreamer

NuGet is a great new open source library sharing tool by Microsoft. It makes downloading, installing, and using 3rd party libraries into your application easy as 1.2.3.

There are many ways to add packages to an application, first is through the Package Manager Console window found under Tools -> Library Package Manager -> Package Manager Console and running scripts that look like… Install-Package Package.Of.Your.Choice

Another way to do it is by Tools -> Library Package Manager -> Manage NuGet Packages

One issue some projects shared on version control such as Git or Svn is placing those Libraries into the app can waste a lot of space on your version control. An easy way to avoid this problem is by using this short script in your pre-build event for your project.

"$(SolutionDir)Tools\nuget" install "$(ProjectDir)packages.config" -o "$(SolutionDir)Packages"

Download the NuGet.exe console application and place it in some directory in your solution. In my example I have it in Tools\

Then, this script will examine the packages.config file in your project that get’s created when you add new packages and will download and install them.

This allows you to remove your packages from your version control and just have the developers download the packages locally when they build the project.

 

2 Comments
Leave A Response

Tagged with: .net • ASP.net • nuget • pre-build 

Global HandleErrorAttribute in ASP.net MVC3

On November 29, 2011, in ASP.net, MVC3, by jonathancreamer

My boss once told me, “Good programming is good Error Handling”. No user enjoys the infamous YSOD(Yellow Screen of Death). It’s just ugly, and shows a general lack of concern over your code if it occurs in a production environment!

However, let’s face it…Bug’s happen. Sometimes there’s nothing you can do about it, others you can. Either way, the End-User should never see the YSOD.

Fortunately in MVC3, there is a built in piece of functionality that comes standard when you create a new MVC project.

It’s called the HandleErrorAttribute, and it can be applied in two different ways. First off is the method by method or class by class way of doing it.

// Use it here
[HandleError]
public class HomeController : Controller
{
    // Or here
    // [HandleError]
    public ActionResult Index()
    {
        return View();
    }
}

The other way is by utilizing the Global Filters of MVC3. This comes default in a new MVC3 project, and is located in the Global.asax file.

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
}

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
    );

}

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
}

The part of this to note is the filters.Add(new HandleErrorAttribute()); This turns on error handling for every controller in your app.

Please Note: It will only handle 500 errors, so 404’s need to be handled differently. I’ll get to that shortly.

The next step in the process is to turn on customErrors in the web.config file at the root of the web app, (NOT the one in the /views folder).

Find <system.web> in your web.config, and then put this in there…


<customerrors mode="On" defaultredirect="~/Error/HttpError">
    <error redirect="~/Error/NotFound" statuscode="404" />
</customerrors>

The defaultredirect allows you to have somewhere to go in case your error page errors, and the error allows you to have different places to go depending on the error code. You can handle 404 errors that way…

The last thing you’ll need for the defaultredirect is a simple ErrorController.

public class ErrorController : BaseController
{
    //
    // GET: /Error/
    public ActionResult HttpError()
    {
       return View("Error");
    }
     public ActionResult NotFound()
    {
        return View();
    }
    public ActionResult Index()
    {
        return RedirectToAction("Index", "Home");
    }
}

The default error view comes in the /views/shared/Error.cshtml folder. This is where MVC will look to find the view to render and display the error message.

@model System.Web.Mvc.HandleErrorInfo
@{
    ViewBag.Title = "General Site Error";
}

<h2>A General 


gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.