DotNetCurry Logo

KnockoutJS and ASP.NET MVC – Alternate techniques and using the Anti Forgery Token

Posted by: Suprotim Agarwal , on 9/8/2013, in Category ASP.NET MVC
Views: 67299
Abstract: Part 2 of the introductory KnockoutJS & ASP.NET MVC Article. We look back at the suggestions we received in the comments of that article and answer these questions and see alternate techniques of doing things

In our Getting Started with KnockoutJS in ASP.NET MVC article, we started off with the intention of weaning web client devs away from hacking up the DOM and using client side ViewModels with Knockout as much as possible. We got some good questions regarding the article of which the main ones are:

1. Why do we have to get the empty View using the Get Action method and then do a second HTTP Get using AJAX after Document load to get the actual data? We’ll see one of the ways to avoid the second Get.

2. Why are we using jQuery’s $(element).on(…) to assign click handlers instead of having the click handler functions in the ViewModel itself. Well we can, so we’ll see how to do so.

3. Why did we disable AntiForgeryToken? Do we really need to make our code ‘insecure’ to use JavaScript based clients? Nope, we don’t have to make our code ‘insecure’, we’ll see how we can put the AntiForgeryToken back and use it to validate input.

Finally we’ll see how we can avoid manual mapping between the Server side ViewModel and Client Side ViewModel.

 

We start off with the code from the first article so you can download the code or fork the code on Github. This article has been written by Sumit Maitra and I. 

Avoid doing a second AJAX GET to get Data as JSON

On way to send data to Client as JSON from the server is to serialize the Data as a JSON string, stick it in a ViewModel property and bind it to an HTML element (like a Hidden Field) or use Razor syntax to include the data in a Hidden field directly.

We’ll use the second technique today.

Changes in LookupsController

We update the Index action method to pass the list of Lookups into the ViewResult.

public ActionResult Index()
{
    return View(db.Lookups.ToList());
}

Just to ensure that we are not calling old code by mistake, we’ll comment out the GetIndex Action method too.

Updating the Index View

We put back the Model declaration on top of the View and add the Hidden Input variable that will save the JSON serialized Model.

@model IEnumerable<KnockoutSamples.Models.Lookup>
<title>Index</title>
<h2>Index</h2>
<input type="hidden" id="serverJSON"
value="@Newtonsoft.Json.JsonConvert.SerializeObject(Model)" />
<!-- rest of the markup -->

If you went through the first article carefully, you’ll realize this works because all Razor syntax is evaluated on the server when generating final HTML markup that’s sent back to the Browser. So here the @Newtonsoft.Json.JsonConvert.SerializeObject will be evaluated on the server for the data that we passed into the View(…) function in the Index Action method. So value of the hidden field will essentially end up with a long JSON string.

Updating the JavaScript to use the Hidden Field as the Data Source

We update the $(document).ready function to retrieve the data object from the Hidden Field instead of a AJAX call to the server. I’ve kept the code that we remove as commented and highlighted in gray. The code to convert the JSON string to JS object is highlighted in Yellow. Note the rest of the code remains the same.

$(document).ready(function () {
 //$.ajax({
//    type: "GET",
//    url: "/Lookups/GetIndex",
//}).done(function (data) {

var data = JSON.parse($("#serverJSON").val());
$(data).each(function (index, element) {
  var mappedItem =
  {
   Id: ko.observable(element.Id),
   Key: ko.observable(element.Key),
   Value: ko.observable(element.Value),
   Mode: ko.observable("display")
  };
  viewModel.lookupCollection.push(mappedItem);
});
ko.applyBindings(viewModel);
 //}).error(function (ex) {
//    alert("Error");
//});

//… Rest of the code
}

With that we are all done, if we run the application now, we’ll see the three default items come up in the Index.

If you run F12 dev tools, you’ll see that there is only one AJAX Get that’s made by Visual Studio’s browser link tech by the injected SignalR code.

knockout-ajax-get-server-side

Handling Edit, Update, Delete in ViewModel functions rather than external Event handlers

There were some protestations on why were we using jQuery ‘On’ function to attach Click event handlers for Edit, Delete etc. and it was suggested that we add the functions to the View Model and be done with it.

To Use KO View Model functions, we need to declare two more functions in the ViewModel. As you can see below, we’ve added two functions Edit and Update. KO automatically passes the data in current context, so we don’t have to extract it using ko.dataFor. The rest of the implementation is copied over from the older event handler.

$(data).each(function (index, element) {
var mappedItem =
{
  Id: ko.observable(element.Id),
  Key: ko.observable(element.Key),
  Value: ko.observable(element.Value),
  Mode: ko.observable("display"),
  Edit: function (current) {
   //var current = ko.dataFor(this);
   current.Mode("edit");
  },
  Update: function (current) {
   //var current = ko.dataFor(this);
   saveData(current);
   current.Mode("display");
  }
};
viewModel.lookupCollection.push(mappedItem);
});

Binding the Button Click handlers

We now have functions to handle the events but we need to assign them to the respective button’s click events. Since the new methods are a part of the View Model, we’ll use KO’s click binding in the markup.

The Edit Button:

<button class="btn btn-success kout-edit" data-bind="click: Edit">Edit</button>

The Update Button:

<button class="btn btn-success kout-update" data-bind="click: Update">Update</button>

If you run the app now, things will work as before. Pretty neat and doesn’t seem to be a troublemaker, so let’s keep it as is and go ahead!

Getting the Anti Forgery token back

We have covered what Anti Forgery tokens are and how they help prevent CSRF attacks here. We also saw how to use it in Web API services. So lets see how we can continue using it in our KO based app.

1. First we restore the AntiForgeryToken Razor syntax in the Index.cshtml. We add the following at the top of the page just below the @model declaration.

@Html.AntiForgeryToken()

This simply adds a Hidden field to the final HTML page.

2. Next we enable the Token Verification on the Controller method, so we uncomment the highlighted line below in the LookupsController

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Lookup lookup)
{
if (ModelState.IsValid)
{
  db.Entry(lookup).State = EntityState.Modified;
  db.SaveChanges();
  return Json(lookup.Id);
}
return View(lookup);
}

3. If we try submitting now, we’ll get the AntiForgeryToken validation failed error. So finally, we update the saveData function in the knockout.samples.js. We make three simple changes:

a. Add the __RequestVerificationToken to the JS object that we are submitting on Save. The property name is so because that’s what MVC looks for in the submitted form and that also happens to be the name of the hidden field in which it is stored.

b. Change contentType to from-url-encoded

c. And finally since we are sending a url-encoded form, so we no longer JSON.stringify submitData and send the JavaScript object instead

function saveData(currentData) {
var postUrl = "";
var submitData = {
  Id: currentData.Id(),
  Key: currentData.Key(),
  Value: currentData.Value(),
  __RequestVerificationToken: $("input[name='__RequestVerificationToken']").val()
};
if (currentData.Id && currentData.Id() > 0) {
  postUrl = "/Lookups/Edit";
}
else {
  postUrl = "/Lookups/Create";
}
$.ajax({
  type: "POST",
  contentType: "application/x-www-form-urlencoded",
  url: postUrl,
  data: submitData
}).done(function (id) {
  currentData.Id(id);
}).error(function (ex) {
  alert("ERROR Saving");
})
}

Okay, that’s three different questions solved and a different perspective towards using KO achieved.

Conclusion

Today we revisited some of the questions that rose in our previous article and explored some alternate techniques when using Knockout JS and ASP.NET MVC. We also saw how we could secure our application using the AntiForgery Token that we had ignored previously. In the next part of the series, we’ll step it up and learn more techniques using Knockout JS in near production scenarios.

Download the entire source code of this article (Github)

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on Google+
Further Reading - Articles You May Like!
Author
Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of DotNetCurry, DNC Magazine for Developers, SQLServerCurry and DevCurry. He has also authored a couple of books 51 Recipes using jQuery with ASP.NET Controls and a new one recently at The Absolutely Awesome jQuery CookBook.

Suprotim has received the prestigious Microsoft MVP award for nine times in a row now. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that represents premium web sites and digital publications comprising of Professional web, windows, mobile and cloud developers, technical managers, and architects.

Get in touch with him on Twitter @suprotimagarwal, LinkedIn or befriend him on Facebook



Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by Nikhil on Wednesday, September 11, 2013 5:57 AM
The create functionality has stopped working after doing changes related to Edit/Update.
Comment posted by Mike S. on Friday, October 4, 2013 8:42 AM
First, I really enjoy these great articles you guys make....keep up the GREAT work!

Why add edit/update functions to each model instance when you can just add a single edit/update function and use for all? Can you discuss pros/cons of why do one or the other?


Comment posted by Mike S. on Friday, October 4, 2013 11:13 AM
First, I really enjoy these great articles you guys make....keep up the GREAT work!

Why add edit/update functions to each model instance when you can just add a single edit/update function and use for all? Can you discuss pros/cons of why do one or the other?


Comment posted by Rohit on Wednesday, December 11, 2013 1:13 AM
what add on your are using on ie to inspect
Comment posted by Scott A. on Wednesday, January 22, 2014 8:44 AM
How would you handle the Cancel button click? One can cancel an edit of an existing item or the creation of a new item. In the latter case, the buttons get left in the UI if one is not careful. In the former case, it has the additional problem of needing an undo of the edit.
Comment posted by Teri Hughes on Sunday, April 13, 2014 1:15 AM
I downloaded this version and the create does not work. It adds the fields and does not update the database.

The cancel button never worked in either version and the create copies the info from the previous unsaved fields.

Interesting concept but only if it works.

Going back to the way I already learned.