This is a migrated thread and some comments may be shown as answers.

Excel Export 400 Error on .NET Core Razor Page

4 Answers 442 Views
Grid
This is a migrated thread and some comments may be shown as answers.
John
Top achievements
Rank 1
John asked on 15 May 2019, 01:23 PM

I'm getting a 400 Error when I click on the toolbar button to export the grid contents to Excel.  The call to the ProxyURL method never happens.

Chrome Dev Tools shows the following.

POST https://localhost:44329/Admin/Companies?handler=Read 400

I suspect this is because the Anti-Forgery token is not getting passed to the read that the export function uses although it looks like the same read call that the grid uses successfully.  Any ideas on how I can resolve this?

@(Html.Kendo().Grid<InstallerCompanyPageModel>()
    .AutoBind(false)
    .Name("CompaniesGrid")
    .Columns(columns =>
    {
        columns.Command(command => { command.Edit()
            .HtmlAttributes(new { title = "Edit", @class = "k-button k-button-icontext" })
            .UpdateText("Save"); }).Width(100);
        columns.Bound(a => a.CompanyName).Width(200)
            .Filterable(filterable => filterable.Extra(false)
                .Operators(operators => operators
                    .ForString(str => str.Clear()
                        .StartsWith("Starts with")
                        .Contains("Contains")
                    ))
            );
        columns.Bound(a => a.WebSite).Width(250)
            .HtmlAttributes(new { @class = "wordWrapGridColumn" })
            .Filterable(filterable => filterable.Extra(false)
            .Operators(operators => operators
                .ForString(str => str.Clear()
                    .StartsWith("Starts with")
                    .Contains("Contains")
                ))
            );
        columns.Bound(a => a.Active).Width(60)
            .ClientTemplate("#= Active ? 'Yes' : 'No' #").HtmlAttributes(new { style = "text-align:center" });
    })
    .ToolBar(toolbar => toolbar.Custom().Text("<i class='fas fa-file'></i> Add New")
        .HtmlAttributes(new { id = "companyAdd", title = "Add a new company.", @class = "k-button k-button-icontext k-grid-add" }))
    .ToolBar(toolbar => toolbar.Custom().Text("<i class='fas fa-sync'></i> Refresh Grid")
        .HtmlAttributes(new { id = "companyRefresh", title = "Refresh the data in the grid." }))
    .ToolBar(toolbar => toolbar.Custom().Text("<i class='fas fa-minus-square'></i> Clear Filters")
        .HtmlAttributes(new { id = "companyReset", title = "Clear the grid column filters." }))
    .ToolBar(toolbar => toolbar.Custom().Text("<i class='fas fa-file-excel'></i> Export To Excel")
        .HtmlAttributes(new { id = "export", title = "Export to Excel", @class = "k-button k-button-icontext k-grid-excel" }))
    .Editable(editable => editable.Mode(GridEditMode.InLine))
    .Pageable(pageable => pageable
        .Input(true)
        .Numeric(false)
        .PageSizes(true)
        .Messages(messages => messages.Display("Showing items from {0:0,0} to {1:0,0}. Total items: {2:0,0}"))
        .Messages(messages => messages.Empty("No companies found."))
        .PageSizes(new[] { 5, 10, 25, 50 })
    )
    .Resizable(resizeable => resizeable.Columns(true))
    .Sortable()
    .Scrollable(s => s.Height("auto"))
    .Filterable()
    .Excel(excel => excel
        .AllPages(true)
        .Filterable(true)
        .FileName("Companies.xlsx")
        .ProxyURL(Url.Action("Excel_Export_Save", "Grid"))
    )
    .Events(events => events.ExcelExport("excelExport1"))
    .DataSource(dataSource => dataSource
        .Ajax()
        .Read(r => r.Url("/Admin/Companies?handler=Read"))
        .Update(u => u.Url("/Admin/Companies?handler=Update"))
        .Create(c => c.Url("/Admin/Companies?handler=Create"))
        .Model(m =>
        {
            m.Id(id => id.InstallerCompanyId);
            m.Field(i => i.CompanyName);
            m.Field(i => i.WebSite);
            m.Field(i => i.Active).DefaultValue(true);
        })
        .PageSize(10)
        .Sort(sortable => sortable.Add("CompanyName"))
        .Events(events => events.Error("gridErrorHandler"))
    )
)

 

My JavaScript

$(function () {
    // razor pages have built in anti-forgery token so wire up the kendo grid to pass it when doing crud
    var grid = $("#CompaniesGrid").data("kendoGrid");
    grid.dataSource.transport.options.read.beforeSend = function (req) {
        req.setRequestHeader('RequestVerificationToken', $('input:hidden[name="__RequestVerificationToken"]').val());
    };
    grid.dataSource.transport.options.update.beforeSend = function (req) {
        req.setRequestHeader('RequestVerificationToken', $('input:hidden[name="__RequestVerificationToken"]').val());
    };
    grid.dataSource.transport.options.create.beforeSend = function (req) {
        req.setRequestHeader('RequestVerificationToken', $('input:hidden[name="__RequestVerificationToken"]').val());
    };
});

 

My controller method

[HttpPost]
public ActionResult Excel_Export_Save(string contentType, string base64, string fileName)
{
    var fileContents = Convert.FromBase64String(base64);
    return File(fileContents, contentType, fileName);
}

 

4 Answers, 1 is accepted

Sort by
0
Tsvetina
Telerik team
answered on 20 May 2019, 06:13 AM
Hello John,

From the error code, I also think the problem is a missing anti-forgery token. You can inspect the request in the browser developer tools (F12 key) Network tab to see if the token is sent with the request. If it is missing for some reason, you can also try the approach shown in this example:
Grid Razor Pages CRUD Operations

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}
 
@using GridRazorPagesApp.Data
@using Kendo.Mvc.UI
 
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
@Html.AntiForgeryToken()


.DataSource(ds => ds.Ajax()
       .Read(r => r.Url("/Index?handler=Read").Data("forgeryToken"))
       .Update(u => u.Url("/Index?handler=Update").Data("forgeryToken"))
       .Create(c => c.Url("/Index?handler=Create").Data("forgeryToken"))
       .Destroy(d => d.Url("/Index?handler=Destroy").Data("forgeryToken"))
       .Model(m => m.Id(id => id.OrderID))
    .PageSize(10)
)

<script>
    function forgeryToken() {
        return kendo.antiForgeryTokens();
    }
</script>


Regards,
Tsvetina
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
John
Top achievements
Rank 1
answered on 20 May 2019, 01:55 PM

That worked.  The JS code I have show above works on the grid read but doesn't seem to work on the Excel export so I'll have to adjust my approach.

This does lead to an another issue.  For some of my grid reads, I need to use the .Data function to pass the ready additional parameters which I was I used the JS code show above and not your approach.  How can I pass the Antiforgery token and additional parameters to the grid read?

In my grid

.Read(r => r.Url("/Admin/Questions?handler=ReadQuestion").Data("additionalData"))

 

Javascript:

function additionalData() {

// how would I return the kendo.antiForgeryTokens() and the parameter?

    return { inspectionSectionId: selectedInspectionSectionId }
}

 

 

 

 

0
Accepted
Tsvetina
Telerik team
answered on 22 May 2019, 11:50 AM
Hello John,

In this case, you can first get the object returned by kendo.antiForgeryTokens() and add your custom arguments as additional options:
<script>
    function additionalData() {
        var data = kendo.antiForgeryTokens();
        data.inspectionSectionId = selectedInspectionSectionId;
        return data;
    }
</script>


Regards,
Tsvetina
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
John
Top achievements
Rank 1
answered on 22 May 2019, 12:21 PM
Perfect.  Thank you.
Tags
Grid
Asked by
John
Top achievements
Rank 1
Answers by
Tsvetina
Telerik team
John
Top achievements
Rank 1
Share this question
or