I have web APIs written in net7 that use the custom DataSourceRequest model binder and a kendo grid that uses them.
My API has multiple parameters in the URL segments: example "/api/users/{userId}/todos/{todoId}". So depending on the row being edited, I want the URL to which the PUT or DELETE verbs are performed to be different.
Example code: https://dojo.telerik.com/ogaHEHuq/4
How can I achieve this when using DataSourceRequest on the server?
3 Answers, 1 is accepted
Hello Shashwat,
I am not sure about the endpoint implementation on your side. However, I would suggest checking the following article regarding Use Web API with Server-Side Operations:
- https://docs.telerik.com/kendo-ui/knowledge-base/web-api-server-operations
As you will see, the data, total, and errors fields are also set in the dataSource schema:
schema: {
data: "Data",
total: "Total",
errors: "Errors",
...}
I noticed that the response returned in the provided Dojo does not include information about the data and total.
Could you please review the implementation on the server on your end and try to return the ToDataSourceResult as demonstrated in the Knowledge Base article linked above:
return orders.ToDataSourceResult(request);
I hope this helps.
Regards,
Neli
Progress Telerik
Hi Neli,
Grid is showing data for me. Issue is specifically with the Update/Delete APIs.
Suppose there's an API with multiple parameters in the URL segment, eg: "users/{userId}/todos/{todoId}" where the parts in the braces should change depending on the row being updated.
How would I configure my datasource for this?
Regards
Hi Shashwat,
When an update/delete request with parameters is sent, the respective endpoint should be implemented to accept such parameters. For example, if you are sending integer IDs In your scenario it could be:
public IActionResult Put(int users, int todos, [FromForm] ProductViewModel product)
What could be helpful is if you take a look at the server implementation of the Telerik UI for ASP.NET Core Web API binding demo:
- https://demos.telerik.com/aspnet-core/grid/webapi
In the link above you can click on the 'View source' tab and examine the 'WebAPIControlle.cs' implementation.
Regards,
Neli
Hi Neli
I don't think I was able to convey my problem properly, so attaching a repo with sample code: https://github.com/shashwatsingh/kendo-grid-webapi
'working' branch can only read data, but cannot update or delete it.
'main' branch fails because I added type='webapi'.
Please help make the 'main' branch work.
Hi Shashwat,
I will need some additional time to review the provided application. I will write you back as soon as I have something to share.
Thank you very much for your patience.
Regards,
Neli
Progress Telerik
Hi Shashwat,
Thank you very much for your patience.
I have reviewed the sample project in the provided GitHub and I managed to send additional parameters useing the following implementation:
transport: {
read: {
url: '/api/users/todos/read',
data: {
userId: 2
}
},
update: {
url: '/api/users/todos/update',
type: "POST",
data: {
userId: 568,
todoId: 30
}
},
destroy: {
url: '/api/users/todos/destroy',
type: "POST",
data: {
userId: 8,
todoId: 50
}
}
On the server I created a class containing the additional fields:
public class Test
{
public long userId { get; set; }
public long todoId { get; set; }
}
Then, I used the class to receive the parameters in the Update and Destroy endpoints:
[HttpPost]
[Route("/api/users/todos/update")]
public IActionResult Post([FromForm] Test test)
{....}
and the Destroy endpoint is as follows:
[HttpPost]
[Route("/api/users/todos/destroy")]
public IActionResult Destroy([FromForm] Test test)
{ .... }
Please try the suggested changes and let me know about the result.
Regards,
Neli
hi Neli,
Our API requires URL segments to be parameterized (eg: /api/users/{userId}/todos) with specific HTTP verbs as they follow Microsoft API guidelines for getting nested resources (https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#931-nested-collections-and-properties).
Your example sends data as a form collection using POST verb for all cases, so that isn't feasible for us to do.
I can parameterize the URL segment if I remove "type: 'webapi'" (see -- https://github.com/shashwatsingh/kendo-grid-webapi/commit/0ef3d56a6d57cca438702b7ec9e806476ea82dbb).
If you suggest a way to set parameterMap similar to how webapi transport does, so that [DataSourceResult] model binder can be used in the API, it should take care of my issue.
Hope this helps illustrate my problem.
---
I may not be the right person to suggest this, but here's what might help us move forward.
When I say "type: 'webapi'" the datasource uses the transport defined in kendo.data.aspnetmvc.js -- kendo.data.transports.webapi. This transport isn't as flexible as the JS datasource API: it doesn't allow the transport.read.url or transport.read to be a function. Had it allowed for that, we'd be set.
Current implementation:
- kendo.data.aspnetmvc.js defines kendo.data.transports.webapi. This transport basically assumes that the options.update is either a string or an object. It doesn't handle the scenario where it could be a function. If it did, we'd be fine.
- Internally, this transport uses parameterMap function and Serializer object defined in kendo.data.aspnetmvc.js to take the grid's state and convert it into proper query strings. However this object is internal, and cannot be used by us the users of the library.
Suggested changes:
If the kendo MVC API allowed URL to come from a function -- or just follow what the JS API allows, we'd be good to go.
In absence of that, if it exposed the Serializer that can translater the grouping, filtering, sorting and paging state to be converted into a querystring that can be ready by the DataSourceRequest modelbinder on the serverside, we could work with that too.
Thank you for continuing to be engaged with our problem for this long. Appreciate it.
Regards,
Shashwat
Hello Shashwat,
In order to send multiple parameters to the server in the described way eg: "/api/users/update/{userId:long}/todo/{todoId:long}" you will need to remove the FromBody from the action. Even if you remove the type:webapi' and use functions you will need to either use the FromBody or send multiple separate parameters.
For example, you can remove the 'type:webapi'. You can configure the transport functions as follows:
dataSource: {
....
//type: 'webapi',
....
transport: {
read: function (options) {
$.ajax({
url: "/api/users/2/todos",
dataType: "json",
success: function (result) {
options.success(result);
},
error: function (result) {
options.error(result);
}
});
},
update: function (options) {
let value1 = 4;
let value2 = 8;
$.ajax({
url: '/api/users/update/' + value1 + '/todo/' + value2,
type: "GET",
contentType: "application/json",
dataType: "json",
......
});
}
},
And in the update action, you will receive the parameters as follows:
[HttpGet]
[Route("/api/users/update/{userId:long}/todo/{todoId:long}")]
public IActionResult Update(long userId, long todoId,
//[FromBody] Todo vm,
[DataSourceRequest] DataSourceRequest request)
{....}
I hope this helps.
Regards,
Neli
Progress Telerik
hi Neli
When I remove "type: webapi", the sorting, aggregate, filtering, etc. actions done on the grid don't get model bound by DataSourceRequest model binder in the API.
You can see difference in the http request with and without type webapi in the sample I submitted.
How do I make the model binding work?
Regards
Hi Shashwat,
I am afraid that I could only suggest an approach where FromForm is used.
As in the provided sample application there is a comment that
// this doesn't get called when dataSource.type isn't 'webapi'
transport: {
// read: function (options) { ....}
could you please try to change the syntax as follows:
transport: {
read: {
url: '/api/users/2/todos'
},
update: {
url: '/api/users/update/' + value1 + '/todo/' + value2
},
And change the HttpGet to HttpPut for the update action. Below is an example:
[HttpPut]
[Route("/api/users/update/{userId:long}/todo/{todoId:long}")]
public IActionResult Update(long userId, long todoId, [FromForm] Todo vm)
{
.....
}
[HttpGet]
[Route("/api/users/{userId:long}/todos")]
public IActionResult Get(long userId, [DataSourceRequest] DataSourceRequest request)
{
.................
return Ok(results.ToDataSourceResult(request));
}
With the changes above the additional parameters are successfully sent to the remote read and update endpoint. Also the the data is correctly filtered, sorted, etc, using the DataSourceResult.
Regards,
Neli
hello Neli
value1 and value2 change as per the row being edited, they are not static (as also seen in the github sample). That's the issue where I'm stuck on.
Could you please see my comment from 21st, and suggest what I could do to use the parameterMap?
Please consider the following as my feature requests:
- I don't want duplicate for converting kendo grid state into kendo model binder compatible query string, so please make the Serializer and parameterMap public.
- the webapi transport that it doesn't handle transport.read.url or transport.read being a function but the regular js transport does. Please make the transport handle function as a valid value.
I think these issues are general purpose, and will have to be handled if the kendo grid needs to talk to a RESTful API (which apart from simple demos, will fetch sub resource collections).
Would it be considered if I submitted a patch for this?
Regards
Hi Shashwat,
Regarding the following comment: If you suggest a way to set parameterMap similar to how webapi transport does, so that [DataSourceResult] model binder can be used in the API, it should take care of my issue. - what is the exact structure you need to achieve. If you need to add additional parameters containing the filter and sorting please note that the parameterMap function is designed to transform the request payload, not to add new parameters to it. The correct way of adding parameters is using data option.
Regarding the requested features and suggestions for improvements I would suggest to log the issues in our official Feedback Portal:
- https://feedback.telerik.com/kendo-jquery-ui
This way the other users could vote for the reseptiive items.
Regards,
Neli