问题描述
我有一个KendoUI数据源链接到WebApi 2 OData控制器,并且在更新操作上遇到问题.我可以创建和删除就可以了.
I have a KendoUI DataSource linked up to a WebApi 2 OData controller and am having problems with update operations. I can create and delete just fine.
进行任何更新后,我打电话将数据源同步到服务器时,出现400错误:
When I make the call to sync the datasource to the server after making any updates I get a 400 error:
{
"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中进行调试显示,修补程序函数正在传递Id,而不是Company对象. Firebug显示PATCH请求如下所示:
Debugging in Visual Studio shows that the patch function is being passed the Id but not the Company object. Firebug shows that the PATCH request looks like this:
models=%7B%22Id%22%3A1026%2C%22Title%22%3A%22Test+Company+test%22%7D
我有一种预感,服务器对此不了解.
I have a hunch there is something wonky about this that the server doesn't understand.
模型很简单,我留下了控制器,无论它为我生成了什么VS:
The model is simple and I left the controller as whatever VS generated for me:
型号:
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是这样的:
Finally, the KendoUI, HTML/Javascript is this:
<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>
推荐答案
在Kendo支持论坛上回答的问题此处.
Question answered on Kendo support forums here.
解决方案是将parameterMap函数更改为:
The solution was to change the parameterMap function to:
parameterMap: function (data, operation) {
return JSON.stringify(data);
}
这篇关于Web API + OData-PATCH请求400错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!