Backbone.JS + ASP.NET WebAPI



Introduction

Backbone.js is a powerful JS library built on top of Underscore.js library. It enables to create one page javascript applications using MVC pattern.

Backbone.js could be downloaded from http://backbonejs.org/, also there exists plenty of examples and tutorials.

ASP.NET WebAPI is newest Microsoft framework, which is a part  of ASP.NET MVC 4. It provides developers with abilitity to quickly create REST services, which could be available from any device and client.

More information about WebAPI could be found on http://www.asp.net/web-api/overview

Goal


In this post I would like to describe how to create basic Backbone.js application, which is using WebAPI as a storage.

Step 1 Create WebAPI REST service




First lets create a REST service which will provide and modify all data in our app.

Our service will be able to provide a list of skills, which we'll be defined by id, title and description. Also service will be able to add new skill to list.

We'll create new WebAPI project (New Project -> Web -> ASP.NET MVC 4 App -> WebAPI) and define a controller and model.

Model definition:


   1:  public class Skill
   2:      {
   3:          [Required]
   4:          public string title { get; set; }
   5:   
   6:          [Required]
   7:          public string description { get; set; }
   8:   
   9:          public int id { get; set; }
  10:      }

Then let's define a controller. Since it's WebAPI service the controller is derived from ApiController class.


   1:  public class SkillsController : ApiController
   2:      {

For easy of implementation we'll store our data in a static variable. Later it could be any other storage, for example Entity Framework with MSSQL database.


   1:  static List<Skill> skills = new List<Skill>();
   2:   
   3:  static int skillId = 0;

Let's fill our collection with some test data, so it'll be available right after REST service is started.


   1:  static SkillsController()
   2:          {
   3:              skills = new List<Skill>{
   4:                  new Skill() { id = 1, title = "Skill1", description = "Desc1" },
   5:                  new Skill() { id = 2, title = "Skill2", description = "Desc2" },
   6:                  new Skill() { id = 3, title = "Skill3", description = "Desc3" }
   7:              };
   8:          }

And now finally REST methods. In this app only 2 methods will be implemented - PUT, GET.


   1:          [HttpGet]
   2:          public IEnumerable<Skill> GetSkills()
   3:          {
   4:              return skills;
   5:          }
   6:   
   7:          [HttpPut]
   8:          public HttpResponseMessage AddSkill(Skill skill)
   9:          {
  10:              if (ModelState.IsValid)
  11:              {
  12:                  skill.id = skillId++;
  13:   
  14:                  skills.Add(skill);
  15:   
  16:                  return new HttpResponseMessage(HttpStatusCode.OK);
  17:              }
  18:              else
  19:              {
  20:                  return new HttpResponseMessage(HttpStatusCode.ExpectationFailed);
  21:              }
  22:          }

Now the service is almost ready to use - you can host it on IIS (for example on localhost:8080) and try to call its methods from the browser, but if you'll try to access it from another domain you'll get an error.
It's happening because cross-domain requests are a potential security vulnerability and by default are disabled for web server.

So the finishing stroke would be to enable requests from other domains.

We could do it in web.config:


   1:   <system.webServer>
   2:      <!-- Enable cross-domain requests -->
   3:      <httpProtocol>
   4:        <customHeaders>
   5:          <add name="Access-Control-Allow-Origin" value="*" />
   6:          <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS"/>
   7:          <add name="Access-Control-Allow-Headers" value="Content-Type, X-Requested-With"/>
   8:        </customHeaders>
   9:      </httpProtocol>

Here we enable requests from any domain, enable REST methods and enable requests headers.

By the way may be you noticed that response content type is not defined anywhere - that's because WebAPI webservice is returning response in format that is defined in Content-Type header property. It could be XML, JSON or other format.

Step 2 Create a Backbone.js application

Backbone.js

Now our webservice is available from anywhere, so let's create a Backbone.js app.

For this we'll add additional web project which will host this app and add required Backbone.js libraries:
First we need to define Backbone.js model:

In backbone.js all models should extend from Backbone.Model class.


   1:  $(function () {
   2:      Item = Backbone.Model.extend({
   3:          defaults: function () {
   4:              return {
   5:                  id: -1,
   6:                  title: null,
   7:                  description: null
   8:              }
   9:          }
  10:      });
  11:   
  12:      ItemSet = Backbone.Collection.extend({
  13:          url: "http://localhost:8080/api/Skills",
  14:          model: Item
  15:      });
  16:   
  17:   
  18:  });

Here I define Item model with id, title and description fields, and also I define collection of Item as ItemSet.

In ItemSet we need to define the address where the model resource is available, which is the address of our REST service. In my case it's hosted on localhost:8080.

Now when the model is defined we could define the markup for our application:


   1:      <div id="app">
   2:          <div class="skillsTab">
   3:              <h1>Skills:</h1>
   4:              <div id="skillList">
   5:              </div>
   6:   
   7:              <div class="addSkill">
   8:                  <label>Add skill:</label>
   9:                  <input type="text" id="skillName" />
  10:                  <input type="text" id="skillDescription" />
  11:                  <input id="addSkill" type="button" value="Add" />
  12:              </div>
  13:          </div>
  14:      </div>

This would be the sceleton of our application:
  • skillList would contain a list of Item
  • addSkill contains UI for adding of the new item.

Now we need to define JS template for the presentation of the Item:



   1:  <script type="text/template" id="skill-template">
   2:      <div class="view item skill">      
   3:        <label><%- title %></label>           
   4:        <div class="skill-description">
   5:          <%= description %>
   6:        </div>
   7:      </div>
   8:    </script>

Now when markup is defined we could create Backbone.js view:



   1:  $(function () {
   2:      SkillView = Backbone.View.extend({
   3:          template: _.template($('#skill-template').html()),
   4:   
   5:          initialize: function () {
   6:              this.listenTo(this.model, 'change', this.render);
   7:              this.listenTo(this.model, 'destroy', this.remove);
   8:          },
   9:   
  10:          render: function () {
  11:              this.$el.html(this.template(this.model.toJSON()));
  12:              return this;
  13:          }
  14:      });
  15:  });

All backbone.js views are derived from Backbone.View.

Here we subscribe to model "change" and "destroy" events. They are fired when view model is changed or deleted.

Render function defines how model is presented in the UI. In this case we are using defined before template for Item.

Now when Item view is ready we need to define view for the whole Backbone.js app:


   1:  $(function () {
   2:   
   3:      // Our overall **AppView** is the top-level piece of UI.
   4:      AppView = Backbone.View.extend({
   5:   
   6:          // Instead of generating a new element, bind to the existing skeleton of
   7:          // the App already present in the HTML.
   8:          el: $("#app"),
   9:   
  10:          events: {
  11:              "click #addSkill": "addSkill"
  12:          },
  13:   
  14:          addSkill: function () {
  15:              var item = {
  16:                  title: this.newSkillName.val(),
  17:                  description: this.newSkillDescription.val()
  18:              };
  19:   
  20:              skillsList.create(item);
  21:   
  22:              this.newSkillName.val('');
  23:              this.newSkillDescription.val('');
  24:          },

Here since we already defined markup for the application we are retrieving the DOM element using jQuery and using later in our view.
Next we are binding to addSkill button click event by adding addSkill method as subscriber.

At last in addSkill we are calling skillList.create method which is the Backbone.js method that creates the item in the collection.

This is the most interesting part, because underneath it's calling Backbone.js sync method which is syncing the state of the application with the REST service (in fact by calling the PUT method).

Now when views, models and markup is defined we only need to write several lines of code to run our backbone.js app:


   1:  $(function () {
   2:   
   3:      skillsList = new ItemSet();
   4:   
   5:      skillsList.fetch({ data: { page: 'no' } });
   6:   
   7:      var app = new AppView({ model: skillsList });
   8:      app.render();
   9:  });

In this code we are defining skillList variable, then fetching it's data from the server, and at last create AppView with it as a model.

Vuola!

Vuola!


Now we have a backbone.js app which sync with our WebAPI webservice.

Sources: 

Sources could be downloaded from GitHub:
https://github.com/JackTheHack/Samples/tree/master/BackboneAndWebApiSample

Useful books:

Here you can find some useful books on Backbone.js and WebAPI

Комментарии

Популярные сообщения из этого блога

Структуры данных ( АВЛ-дерево , обход графа и построение минимального остовного дерева графа)

2D Физика для игр - Separate Axis Theorem

HLSL шейдеры для чайников