KnockoutJS postJSON method escapes my string why?

Go To


I am new to KockoutJS so I may be missing something simple.

I have a textarea in a form:

<form method="POST" data-bind="submit: save" id="theForm">
    <textarea name="Text" rows="10" cols="100" data-bind="value: userText" ></textarea>
    <input type="submit" value="Save" />

And I have the following Knockout code:

<script type="text/javascript">
    var MyViewModel = {
        userText: ko.observable('@Model.UserText'),
        save: function () {
              ko.utils.postJson('@Url.Action("modify")',{userText : this.userText});

Where @Model.UserText is a literal string. and my ASP.NET MVC controller method modify is defined as follows:

public ActionResult Modify(string userText)

My problem is that The postJSON method is POSTing an escaped string to my controller method and I don't understand why. I can see in fiddler that the string is coming in as userText=%22some+text%22

if I use normal jQuery ajax instead of using postJson like this:

url: '@Url.Action("modify")',
contentType: 'application/json',
type: 'POST',
data: ko.toJSON({userText: ko.toJS(this.userText)}),
success: function(result) {
    ko.mapping.updateFromJS(this, result);

then the JSON object {'userText': 'some text'} is passed to the controller method and the MVC model binder parses this correctly and give me an unescaped string.

Can I make postJson pass the same JSON object that is being passed by the jQuery ajax method?

2012-04-04 00:35
by Brad Cunningham


Full information is located at this article

What you need, is to use [FromJson] attribute on your action.

public ActionResult Modify([FromJson]string userText)

implementation for atttribute:

using System.Web.Mvc;
using System.Web.Script.Serialization;

namespace koListEditor
    public class FromJsonAttribute : CustomModelBinderAttribute
        private readonly static JavaScriptSerializer serializer = new JavaScriptSerializer();

        public override IModelBinder GetBinder()
            return new JsonModelBinder();

        private class JsonModelBinder : IModelBinder
            public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
                var stringified = controllerContext.HttpContext.Request[bindingContext.ModelName];
                if (string.IsNullOrEmpty(stringified))
                    return null;
                return serializer.Deserialize(stringified, bindingContext.ModelType);

Just a few comments:

ko.utils.postJson(urlOrForm, data, options) will do following for you:

1) it will create internal form with set of inputs

2) it will iterate your data parameter, and will invoke ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));

on each property, and will save result to that input. This internally uses JSON.stringify, so in your case it will call

JSON.stringify('"some text"') 

that's why you see your string 'escaped' (actually it just converted in to JSON)

3) in case you passed options with params, it will also append them for request.


ko.utils.postJson('localhost',{ userText : '"some text"' });

posts: userText "\"some text\""

ko.utils.postJson('localhost',{}, {params: { userText : '"some text"' } });

posts: userText "some text"

so in your case if you don't want to decorate with [FromJson], you can just add to options.params instead of data. Hovewer, in case you will need to pass more complex json object (not a simple string but some ViewModel), you will still need to use this [FromJson] attribute.

2012-04-04 01:51
by Artem
Thank you. using the {params: {}} syntax worked. I didn't want to decorate my MVC controller method with [fromJSON]. I actually think the normal jQuery ajax approach is clearer to read so I think I will end up going that route - Brad Cunningham 2012-04-04 16:05
I also always use pure jquery ajax approach. From my point of view, the only reason to use ko.utils.postJson is when you want to perform real form post, e.g. to use things like RedirectToAction in your controller, or just returning full (non-partial) view. But these use cases are pretty rare.. - Artem 2012-04-04 16:30
If you use [ValidateAntiForgeryToken] in your controller post action, you can use this trick to send the token: params: { RequestVerificationToken: $('input[name=RequestVerificationToken]').val() - jgarza 2012-09-17 17:37