1

我有一個KendoUI DataSource鏈接到WebApi 2 OData控制器,並且在更新操作時遇到問題。我可以創建和刪除就好了。Web API + OData - PATCH請求400錯誤

當我打這個電話來進行任何更新後的數據源服務器同步,我得到一個400錯誤:

{ 
    "odata.error":{ 
    "code":"","message":{ 
     "lang":"en-US","value":"The request is invalid." 
    },"innererror":{ 
     "message":"patch : Invalid JSON. A token was not recognized in the JSON content.\r\n","type":"","stacktrace":"" 
    } 
    } 
} 

調試在Visual Studio中顯示該補丁的功能被傳遞的標識,但不公司對象。螢火蟲表明PATCH請求如下所示:

models=%7B%22Id%22%3A1026%2C%22Title%22%3A%22Test+Company+test%22%7D 

我有一種預感,也有一些是靠不住的這個服務器不理解。

的模型很簡單,我離開的任何VS對我產生的控制器:

型號:

public class Company { 
    public Company() { } 

    public Company(Company company) { 
     this.Id = company.Id; 
     this.Title = company.Title; 
     this.Projects = company.Projects; 
    } 

    public int Id { get; set; } 
    public string Title { get; set; } 

    public virtual ICollection<Project> Projects { get; set; } 
} 

控制器:

public class CompanyController : ODataController 
{ 
    private ApplicationDbContext db = new ApplicationDbContext(); 

    // GET odata/Company 
    [Queryable] 
    public IQueryable<Company> GetCompany() 
    { 
     return db.Companies; 
    } 

    // GET odata/Company(5) 
    [Queryable] 
    public SingleResult<Company> GetCompany([FromODataUri] int key) 
    { 
     return SingleResult.Create(db.Companies.Where(company => company.Id == key)); 
    } 

    // PUT odata/Company(5) 
    public async Task<IHttpActionResult> Put([FromODataUri] int key, Company company) 
    { 
     if (!ModelState.IsValid) 
     { 
      return BadRequest(ModelState); 
     } 

     if (key != company.Id) 
     { 
      return BadRequest(); 
     } 

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

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

     return Updated(company); 
    } 

    // POST odata/Company 
    public async Task<IHttpActionResult> Post(Company company) 
    { 
     if (!ModelState.IsValid) 
     { 
      return BadRequest(ModelState); 
     } 

     db.Companies.Add(company); 
     await db.SaveChangesAsync(); 

     return Created(company); 
    } 

    // PATCH odata/Company(5) 
    [AcceptVerbs("PATCH", "MERGE")] 
    public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Company> patch) 
    { 
     if (!ModelState.IsValid) 
     { 
      return BadRequest(ModelState); 
     } 

     Company company = await db.Companies.FindAsync(key); 
     if (company == null) 
     { 
      return NotFound(); 
     } 

     patch.Patch(company); 

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

     return Updated(company); 
    } 

    // DELETE odata/Company(5) 
    public async Task<IHttpActionResult> Delete([FromODataUri] int key) 
    { 
     Company company = await db.Companies.FindAsync(key); 
     if (company == null) 
     { 
      return NotFound(); 
     } 

     db.Companies.Remove(company); 
     await db.SaveChangesAsync(); 

     return StatusCode(HttpStatusCode.NoContent); 
    } 

    // GET odata/Company(5)/Projects 
    [Queryable] 
    public IQueryable<Project> GetProjects([FromODataUri] int key) 
    { 
     return db.Companies.Where(m => m.Id == key).SelectMany(m => m.Projects); 
    } 

    protected override void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      db.Dispose(); 
     } 
     base.Dispose(disposing); 
    } 

    private bool CompanyExists(int key) 
    { 
     return db.Companies.Count(e => e.Id == key) > 0; 
    } 
} 

最後,KendoUI ,HTML/Javascript是這樣的:

<h2>Company List</h2> 

<div id="company-data"> 
    <div class="col-md-3 col-sm-5 col-xs-5"> 
     <div id="company-list" style="padding: 0px; height: 500px; overflow: auto" data-role="listview" data-template="list-template" data-bind="source: companies, events: {change: OnSelect}" data-selectable="true"></div> 
     <div> 
      <button class="btn btn-success btn-sm" id="btn-add-company"><span class="glyphicon glyphicon-plus"></span> Add</button> 
      <button class="btn btn-danger btn-sm" id="btn-delete-company" data-bind="visible: hasSelection, click: deleteSelection"><span class="glyphicon glyphicon-remove"></span> Delete</button> 
      <button class="btn btn-default btn-sm" id="btn-clear-company" data-bind="visible: hasSelection, click: clearSelection"><span class="glyphicon glyphicon-ban-circle"></span> Clear</button> 
      <button class="btn btn-primary btn-sm btn-block" id="btn-save" data-bind="visible: hasChanges, click: saveChanges"><span class="glyphicon glyphicon-cloud-upload"></span> Save All</button> 
     </div> 
    </div> 
    <div class="col-md-9 col-sm-7 col-xs-7" data-bind="visible: hasSelection"> 
     <label for="company-title">Title:</label><br /> 
     <input id="company-title" data-bind="value: selectedItem.Title" ><br /> 
    </div> 
</div> 

<script type="text/x-kendo-template" id="list-template"> 
    <div class="company" style="cursor: pointer"> 
     <span data-bind="text: Title"></span> 
    </div> 
</script> 

<script> 
    $(function() { 
     var firstSync = true; 
     var companyVM = new kendo.observable({ 
      // Data Source. 
      companies: new kendo.data.DataSource({ 
       type: 'odata', 
       transport: { 
        create: { 
         url: '/odata/Company', 
         dataType: 'json', 
         type: 'POST' 
        }, 
        read: { 
         url: '/odata/Company', 
         dataType: 'json' 
        }, 
        update: { 
         url: function (data) { 
          return '/odata/Company(' + data.Id + ')'; 
         }, 
         dataType: 'json', 
         type: 'PATCH' 
        }, 
        destroy: { 
         url: function (data) { 
          return '/odata/Company(' + data.Id + ')'; 
         }, 
         dataType: 'json', 
         type: 'DELETE' 
        }, 
        parameterMap: function (options, operation) { 
         if (operation !== "read" && options) { 
          console.log(operation + '*: ' + kendo.stringify(options)); 
          return { 
           models: kendo.stringify(options) 
          }; 
         } 
         console.log(operation + ': ' + kendo.stringify(options)); 
         return options; 
        } 
       }, 
       schema: { 
        data: function (data) { 
         return data['value']; 
        }, 
        total: function (data) { 
         return data['odata.count']; 
        }, 
        model: { 
         id: 'Id', 
         fields: { 
          Title: { type: 'string' } 
         } 
        } 
       }, 
       change: function() { 
        // We don't want to fire the first time the data loads because that counts as changed. 
        if (!firstSync) 
         companyVM.set('hasChanges', true); 
        else 
         firstSync = false; 
       } 
      }), 

      // Properties. 
      selectedItem: null, 
      hasSelection: function() { 
       return this.get('selectedItem') != null; 
      }, 
      hasChanges: false, 

      // Functions. 
      clearSelection: function() { 
       this.set('selectedItem', null); 
       $('#company-list').getKendoListView().clearSelection(); 
      }, 
      saveChanges: function() { 
       this.companies.sync(); 
       this.set('hasChanges', false); 
      }, 
      deleteSelection: function() { 
       if (confirm('Warning, deletion is permanent! Are you sure you wish to delete this item?')) { 
        this.companies.remove(this.selectedItem); 
        this.set('hasChanges', true); 
        this.clearSelection(); 
       } 
      }, 

      // Events. 
      OnSelect: function (e) { 
       var list = $(e.sender.element).getKendoListView(); 
       var row = list.select(); 
       var item = list.dataSource.getByUid(row.data('uid')); 

       this.set('selectedItem', item); 
      } 
     }); 

     kendo.bind($('#company-data'), companyVM); 
    }); 
</script> 

回答

2

在Kendo支持論壇上回答的問題here

的解決方案是在parameterMap的功能改變爲:

parameterMap: function (data, operation) { 
    return JSON.stringify(data); 
}