1

This is the default webapi method with [FromBody] attribute added:

// PUT api/Pedidos/5
    public async Task<IHttpActionResult> PutPedido(Guid id,[FromBody]Job pedido)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if (id != pedido.Id)
        {
            return BadRequest();
        }

        db.Entry(pedido).State = EntityState.Modified;

        try
        {
            await db.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!PedidoExists(id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return StatusCode(HttpStatusCode.NoContent);
    }

this is the jquery request:

self.update = function () {                
            $.ajax({
                type: "PUT",
                url: baseUri + '@Model.Id',
                data: ko.toJSON(updatableData),                    
                dataType: "json",
                contentType: "application/json"
            })
                .done(function (data) {

                    alert('Magic');
                })
                .error(function (jqXHR, textStatus, errorThrown) {
                    alert(errorThrown);
                    alert("fail");
                });
        }

This is the data:

var updatableData = {
            Id : '@Model.Id',
            Name : '@Model.Name',
            AbreviaNome: self.AbreviaNome(),
            AbreviaFantasia: self.AbreviaFantasia,
            AbreviaLogradouro: self.AbreviaLogradouro,
            AbreviaComplemento: self.AbreviaComplemento,
            AbreviaBairro: self.AbreviaBairro,
            AbreviaCidade: self.AbreviaCidade,
            AbreviaExtra: self.AbreviaExtra,
        };

this is partial c# model, I have removed about 7 fields where 2 are dates and the rest are strings:

public class Job
{       
    public Guid Id { get; set; } 
    [Required]   
    public string Name {get;set;}    
    public int TotalItems { get; set; }        
    public bool AbreviaNome { get; set; }
    public bool AbreviaFantasia { get; set; }
    public bool AbreviaLogradouro { get; set; }
    public bool AbreviaComplemento { get; set; }
    public bool AbreviaBairro { get; set; }
    public bool AbreviaCidade { get; set; }
    public bool AbreviaExtra { get; set; }
    public virtual ICollection<Item> Items { get; set; }        
}

I using ASP.NET with knockout.js thats why you see 'self' on the data. the request comes through, and I get to the PUT method just fine. But pedido instance has all false values and no Id at all (only on the first parameter), and all values were supposed to come as true, with the exception of the last one, on the attempt I had, see the Request Body content:

{"Id":"c47f0ad2-0783-e311-8289-88532ee08d00", "Name":"Test 1","AbreviaNome":true,"AbreviaFantasia":true,"AbreviaLogradouro":true,"AbreviaComplemento":true,"AbreviaBairro":true,"AbreviaCidade":true,"AbreviaExtra":false}

What might be happening here? Do I have to have all fields on the "updatableData" object? Is there another way of achieving what I need?

EDIT: I have added a field to model that was missing but its important "Name" is a required field and there is an error somewhere that it thinks that the Name value is not on the request, when it actually is, after the correction.

EDIT: Found an error in the ModelBinding, related to the Required Name:

{"$id":"1","Message":"The request is invalid.","ModelState":{"$id":"2","pedido.Name":["The Name field is required."]}}

I have removed the [Required] attribute from the Name property, then it stopped working worse, in the ModelBinding I was getting only one parameter, which was the Id. When I put required back it goes back to working but with the above error.

Oakcool
  • 1,470
  • 1
  • 15
  • 33
  • Do you really need the [FromBody] attribute. It is only needed for scalar parameters. What happens if you remove it? – Håkan Fahlstedt Jan 24 '14 at 05:48
  • 3
    Your code should work, however I see something strange: in your `$.ajax` the `contenttype` should be `contentType` the `Type` should start with a capital `T`. – nemesv Jan 24 '14 at 05:54
  • I have removed the attribute and also corrected the contentType spelling (Nice Catch), but the behavior still the same... – Oakcool Jan 24 '14 at 06:01
  • Check Chrome Net console and see what error your receive, then post that here. – radu florescu Jan 24 '14 at 06:40
  • 1
    Try [Postman](https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm?hl=en). It's a chrome extension that lets you "manually" send requests. You can use it to make sure there is nothing wrong with what's going in the wire. The contentype is wrong for sure though, it's _contentType_ – Rui Jan 24 '14 at 10:08
  • Also datatype was wrong, correct was dataType. http://stackoverflow.com/questions/2722750/ajax-datatype – radu florescu Jan 24 '14 at 11:02
  • Rui, I have tried Postman, and it works when I pass the bigger JSON object, I copied from the Request Body on the browser. So there is something wrong with what the browser is sending over the wire, when I run the app code. – Oakcool Jan 24 '14 at 13:52

1 Answers1

2

Try to use JSON.stringify instead of ko.ToJSON:

self.update = function () {                
            $.ajax({
                type: "PUT",
                url: baseUri + '@Model.Id',
                data: JSON.stringify(updatableData),                    
                datatype: "json",
                contenttype: "application/json"
            })
                .done(function (data) {
                alert('Magic');
            })
            .error(function (jqXHR, textStatus, errorThrown) {
                alert(errorThrown);
                alert("fail");
            });
    }

Also, you appear to be passing one object over but expecting 2 parameters, so you shouldn't need the Guid id on the method signature as it's in the Job object already:

public async Task<IHttpActionResult> PutPedido([FromBody]Job pedido)
Tanner
  • 22,205
  • 9
  • 65
  • 83
  • His approach is valid, the guid will be loaded implicit FromURI and the complex Object explicit [FromBody]. But I suspect that he has an error when posting in browser console. – radu florescu Jan 24 '14 at 09:57
  • Tanner, I did try the stringify approach and it generated a different JSON then before {"Id":"96ed3a0f-fb84-e311-8289-88532ee08d00","Name":"Test 2","AbreviaNome":true}, its missing all the other things, and it still did not work. – Oakcool Jan 24 '14 at 13:33
  • About the 2 parameters: I believe this is the correct way since a proper REST implementation would return a resource URL (http:\\test.com\api\pedido\some-big-key), and when you make the call to apply changes to that resource, you use the that whole URL, and passes in the body the changes, just like Floradu88 describes. – Oakcool Jan 24 '14 at 13:43