2

I am using the Kendo UI for ASP.NET Core MVC suite with a Razor Pages web application so I am trying to use the handler technique for the grid's server operations.

@(Html.Kendo().Grid<CustomerViewModel>()
        .Name("CustomersGrid")
        .Columns(columns =>
        {
            columns.Bound(x => x.CustomerId).Title("Student ID");
            columns.Bound(x => x.CustomerName).Title("Name");
        })
        .Pageable()
        .Sortable()
        .DataSource(dataSource => dataSource
            .Ajax()
            .PageSize(20)
            .ServerOperation(true)
            .Read(read => read.Url("/Customers?handler=Read")))
         )

I looked in the network tab and it is making the correct POST to http://localhost:5000/Customers?handler=Read however I am not ending up at my breakpoint and I get a status code 400.

In the razor page's code behind the Action method is named OnPostReadAsync

Any idea why this is not working? In addition to .Url also tried using read.Action and read.Route in the .Read property of the DataSource.

Here is the class with the action method:

public class IndexModel : PageModel
{
    private readonly ICustomerRepository _customerRepository;
    private readonly IMapper _mapper;

    public IndexModel(ICustomerRepository customerRepository, IMapper mapper)
    {
        _customerRepository = customerRepository;
        _mapper = mapper;
    }

    public IList<CustomerViewModel> Customers { get; set; }

    public async Task<IActionResult> OnPostReadAsync([DataSourceRequest] DataSourceRequest request)
    {
        // THIS IS WHERE I WANT IT TO GO FOR READ

        var customersFromDb = await _customerRepository.FilterAsync();
        return new JsonResult(_mapper.Map<IList<Customer>, IList<CustomerViewModel>>(customersFromDb).ToDataSourceResult(request));
    }
}
Jonathan
  • 4,916
  • 2
  • 20
  • 37
Blake Rivell
  • 13,105
  • 31
  • 115
  • 231
  • can we see the class for the Action method? – jazb Dec 12 '18 at 03:46
  • @JohnB I added it. – Blake Rivell Dec 12 '18 at 03:57
  • Shouldn't you be calling a method in a controller instead of a method in your model? That would be a `Post` method in your `Customers` controller – Jonathan Dec 12 '18 at 04:34
  • @Jonathan I am using Razor Pages and it is complicating this entire thing. You are supposed to use the handler query string param. – Blake Rivell Dec 12 '18 at 04:39
  • Ah, I see. I'm not familiar with Razor Pages – Jonathan Dec 12 '18 at 14:55
  • Again, I'm not familiar with `Razor Pages`, but just looked through some of the documentation. Isn't your url `/Customers?handler=read` going to look for an `OnPostReadAsync` action in a *`Customers`* model? But you've got it in the *`Index`* model? Also, are you sure you're `POST`ing that request and not sending a `GET`? – Jonathan Dec 12 '18 at 16:40

2 Answers2

5

You may be doing this already, but I didn't see it in your code. Remember the Razor Pages require the anti-forgery token. You can inject it into your markup like this:

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf

And I added this function to set it on the grid's dataSource requests:

$(function () {
    const requestVerificationToken = '@Xsrf.GetAndStoreTokens(HttpContext).RequestToken';
    const beforeSend = req => req.setRequestHeader('RequestVerificationToken', requestVerificationToken);
    const grid = $("#grid").getKendoGrid();
    grid.dataSource.transport.options.create.beforeSend = beforeSend;
    grid.dataSource.transport.options.update.beforeSend = beforeSend;
    grid.dataSource.transport.options.destroy.beforeSend = beforeSend;
});

Without that token all the PageModel custom handlers will return 400 errors.

Reference: Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks in ASP.NET Core

crgolden
  • 4,332
  • 1
  • 22
  • 40
2

To me, using a POST to get data is wrong. If you want to use a GET, then append .Type(HttpVerbs.Get) to your Read method:

.DataSource(data =>
{
    data.Ajax()
      .Events(events => events.Error("grid_error"))
      .Read(read => read.Url("./Groups?handler=Read").Type(HttpVerbs.Get));
      //.Sort(sort => sort.Add("Name").Ascending())
      //.PageSize(20);
})

And keep your usual Ajax method but the convention expects OnGet plus the value specified by handler=...

public IActionResult OnGetRead([DataSourceRequest] DataSourceRequest request)
{
    var result = db.Queryable<IdentityRole>();
    return new JsonResult(result.ToDataSourceResult(request));
}

The TagHelper style is similar:

<datasource type="DataSourceTagHelperType.Ajax" custom-type="AndyB.Identity.IdentityRole">
  <transport>
    <read url="./Groups?handler=Read" type="Get" />
  </transport>
  <sorts>
    <sort field="Name" direction="ascending" />
  </sorts>
</datasource>

Don't use for inserts, updates or delete, continue to use the POST

AndyB
  • 101
  • 4