You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DataTables Ajax请求无法命中Controller Action的问题排查及解决

问题:DataTables Ajax请求无法命中Controller Action,始终显示Processing

问题描述

我正面临一个棘手的问题:相同的代码在项目其他位置能正常运行,但当前场景下,DataTables的Ajax请求始终无法命中Controller中的Action。页面加载后,DataTables只显示processing弹窗,完全加载不出数据。我在Controller的Action上设置了断点,发现请求根本没到达这个方法。

我试过把url改成@Url.Action("GetList", "Home"),怀疑是路由的问题,但检查路由配置后没发现异常。直接访问这个Url会返回404页面,而项目里其他同类请求哪怕报错,至少能命中Controller Action。

更新:我把页面和Controller Action的名称从Parts改成了PartSearch,但问题还是存在,这个Action属于Home Controller。改名是因为项目里已经有PartsController了,那个Controller是后端用的,需要登录凭证,没法用在当前场景。

相关代码

JavaScript代码

$(document).ready(function () { 
    var oTableMenuPermission = ""; 
    $("#PartsListTable").dataTable({ 
        "ajax": { 
            "url": "/Home/GetList", 
            "type": "POST", 
            "datatype": "json", 
        }, 
        "responsive": true, 
        "bRetrieve": true, 
        "bProcessing": true, 
        "deferRender": true, 
        "dom": 'lBfrtip', 
        "serverSide": "true", 
        "order": [0, "PartNumber"], 
        "language": { "processing": "processing...Please wait" }, 
        "buttons": [ 
            { extend: 'copyHtml5', exportOptions: { columns: ':visible' } } , 
            { extend: 'excelHtml5', exportOptions: { columns: ':visible' } } , 
            { extend: 'csvHtml5', exportOptions: { columns: ':visible' } } , 
            { extend: 'pdfHtml5', exportOptions: { columns: ':visible' } } , 
            { extend: 'print', exportOptions: { columns: ':visible' } } , 
            'colvis' 
        ], 
        columnDefs: [{ orderable: false, targets: [2, 3, 4, 5, 6] }], 
        "aoColumns": [ 
            { "data": "PartNumber", "name": "PartNumber" }, 
            { 
                "data": "PartImage", 
                "aTargets": [0], 
                "render": function (data) { 
                    if (data === null) return '<img src=Content/Images/Parts/NoImage.png style="width:50px;" />'; 
                    return '<img src=Content/Images/Parts/' + data + ' class="zoom" style="width:50px;" />'; 
                } 
            }, 
            { "data": "Description", "name": "Description" }, 
            { 
                "data": "HasUpgrade", 
                "aTargets": [0], 
                "render": function (data) { 
                    if (data === true) { 
                        return '<b style="color:green;">Yes</b>'; 
                    } else { 
                        return '<b style="color:red;">No</b>'; 
                    } 
                } 
            }, 
            { 
                "data": "IsUpgrade", 
                "aTargets": [0], 
                "render": function (data) { 
                    if (data === true) { 
                        return '<b style="color:green;">Yes</b>'; 
                    } else { 
                        return '<b style="color:red;">No</b>'; 
                    } 
                } 
            }, 
            { 
                "mRender": function (oObj, type, full) { 
                    var button = '<div>' 
                    button += '<div class="btn-group">'; 
                    button += '<a class="btn btn-warning btn-sm" href="' + ControlerNameParts + '/Details/' + full.Material + '" data-ajax-update="#SkEdit" data-ajax-success="openModalDialog(\'SkEdit\', \'Edit\')" data-ajax-mode="replace" data-ajax-method="GET" data-ajax-failure="clearModalDialog(\'SkEdit\');alert(\'Ajax call failed\')" data-ajax-begin="prepareModalDialog(\'SkEdit\')" data-ajax="true">Details</a>&nbsp;'; 
                    button += '</div></div>'; 
                    return button; 
                } 
            }, 
        ], 
    }); 
});

Controller Action代码

[HttpPost]
public ActionResult GetList()
{
    // Server Side Parameters
    int start = Convert.ToInt32(Request["start"]);
    int length = Convert.ToInt32(Request["length"]);
    string searchValue = Request["search[value]"];
    string sortColumnName = Request["column[" + Request["order[0][column]"] + "][name]"];
    string sortDirection = Request["order[0][dir]"];
    List<PartSearchView> prodList = new List<PartSearchView>();
    using (GeneralEntities db = new GeneralEntities())
    {
        prodList = db.PartSearchView.ToList();
        int totalRows = prodList.Count;
        if (!string.IsNullOrEmpty(searchValue))
        {
            prodList = prodList.Where(x => x.PartNumber != null && x.PartNumber.ToLower().Contains(searchValue.ToLower())).ToList();
        }
        int totalRowsAfterFilteing = prodList.Count;
        // Sorting
        prodList = prodList.OrderBy(sortColumnName + " " + sortDirection).ToList<PartSearchView>();
        prodList = prodList.Skip(start).Take(length).ToList();
        return Json(new { data = prodList, draw = Request["draw"], recordsTotal = totalRows, recordsFiltered = totalRowsAfterFilteing }, JsonRequestBehavior.AllowGet);
    }
}

路由配置代码

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    // routes.MapRoute(
    // name: "Roles",
    // url: "Roles/{action}/{id}",
    // defaults: new { controller = "UserAndRole", action = "Index", id = UrlParameter.Optional }
    //);
    routes.MapRoute(
    name: "About",
    url: "About",
    defaults: new { controller = "Home", action = "About" }
    );
    routes.MapRoute(
    name: "Services",
    url: "Services",
    defaults: new { controller = "Home", action = "Services" }
    );
    routes.MapRoute(
    name: "Contact",
    url: "Contact",
    defaults: new { controller = "Home", action = "Contact" }
    );
    routes.MapRoute(
    name: "Quote",
    url: "Quote",
    defaults: new { controller = "Home", action = "Quote" }
    );
    routes.MapRoute(
    name: "MyEquipment",
    url: "MyEquipment/{id}",
    defaults: new { controller = "MyEquipment", action = "Index" }
    );
    // routes.MapRoute(
    // name: "PartsSearch",
    // url: "Parts",
    // defaults: new { controller = "Home", action = "Parts" }
    //);
    routes.MapRoute(
    name: "MyEquipmentTlj",
    url: "MyEquipment/Parts/{id}",
    defaults: new { controller = "MyEquipment", action = "Parts" }
    );
    // routes.MapRoute(
    // name: "Dashboard",
    // url: "Dashboard/{id}",
    // defaults: new { controller = "Dashboard", action = "Index", page = UrlParameter.Optional }
    //);
    routes.MapRoute(
    name: "EquipCat",
    url: "Equipment/Category/{slug}/{Catid}",
    defaults: new { controller = "Equipment", action = "Index", page = UrlParameter.Optional }
    );
    routes.MapRoute(
    name: "EquipTag",
    url: "Equipment/Tag/{slug}/{Tagid}",
    defaults: new { controller = "Equipment", action = "Index", page = UrlParameter.Optional }
    );
    routes.MapRoute(
    name: "EquipPost",
    url: "Equipment/Post/{id}/{slug}",
    defaults: new { controller = "Equipment", action = "Post" }
    );
    routes.MapRoute(
    name: "AddUsers",
    url: "Users/Create/{CompanyId}/{AddressId}",
    defaults: new { controller = "Users", action = "Create" }
    );
    routes.MapRoute(
    name: "BestenAddUsers",
    url: "UsersManager/Create/{CompanyId}/{AddressId}",
    defaults: new { controller = "UsersManager", action = "Create" }
    );
    routes.MapRoute(
    name: "AddMachines",
    url: "Machine/Add/{CompanyId}/{AddressId}",
    defaults: new { controller = "Machine", action = "Add" }
    );
    routes.MapRoute(
    name: "EventManagerAdd",
    url: "CalendarManager/Add/{companyId}/{addressId}",
    defaults: new { controller = "CalendarManager", action = "Add" }
    );
    routes.MapRoute(
    name: "ListAddresses",
    url: "Addresses/{id}",
    defaults: new { controller = "Addresses", action = "Index" }
    );
    routes.MapRoute(
    name: "ListOrders",
    url: "Orders/{id}",
    defaults: new { controller = "Orders", action = "Index" }
    );
    routes.MapRoute(
    name: "UserCreate",
    url: "Users/create/{CompanyId}/{AddressId}",
    defaults: new { controller = "Users", action = "Create"}
    );
    routes.MapRoute(
    name: "NotesAdd",
    url: "Notes/Add/{CompanyId}/{AddressId}",
    defaults: new { controller = "Notes", action = "Add" }
    );
    routes.MapRoute(
    name: "SerialAdd",
    url: "SerialNumbers/AddSerial/{SerialNumber}/{CompanyId}/{AddressId}",
    defaults: new { controller = "SerialNumbers", action = "AddSerial" }
    );
    routes.MapRoute(
    name: "SerialSearch",
    url: "SerialSearch",
    defaults: new { controller = "Home", action = "SerialSearch" }
    );
    routes.MapRoute(
    name: "ExpensesAdd",
    url: "ExpenseReports/Create/{CompanyId}/{AddressId}",
    defaults: new { controller = "ExpenseReports", action = "Create" }
    );
    routes.MapRoute(
    name: "InvoiceList",
    url: "Invoice/{cmpid}",
    defaults: new { controller = "Invoice", action = "Index" }
    );
    routes.MapRoute(
    name: "BInvoiceList",
    url: "InvoiceManager/{cmpid}",
    defaults: new { controller = "InvoiceManager", action = "Index", cmpid = UrlParameter.Optional }
    );
    routes.MapRoute(
    name: "CInvoiceList",
    url: "InvoiceManager/List/{addid}",
    defaults: new { controller = "InvoiceManager", action = "List" }
    );
    routes.MapRoute(
    name: "QuoteList",
    url: "Quotes/{cmpid}",
    defaults: new { controller = "Quotes", action = "Index" }
    );
    routes.MapRoute(
    name: "PurchaseOrderAdd",
    url: "PurchaseOrder/Add/{id}/{RFQID}",
    defaults: new { controller = "PurchaseOrder", action = "Add" }
    );
    routes.MapRoute(
    name: "Subjobs",
    url: "SubJobs/{id}",
    defaults: new { controller = "SubJobs", action = "Index" }
    );
    routes.MapRoute(
    name: "BQuoteList",
    url: "QuotePdfManager/{cmpid}",
    defaults: new { controller = "QuotePdfManager", action = "Index", cmpid = UrlParameter.Optional }
    );
    routes.MapRoute(
    name: "CQuoteList",
    url: "QuotePdfManager/List/{addid}",
    defaults: new { controller = "QuotePdfManager", action = "List" }
    );
    routes.MapRoute(
    name: "ConfirmEmail",
    url: "Account/SendEmailConfirm/{UserName}",
    defaults: new { controller = "Account", action = "SendEmailConfirm" }
    );
    routes.MapRoute(
    name: "ShippingAdd",
    url: "ShippingOrBOLs/Add/{companyId}/{addressId}/{orderId}",
    defaults: new { controller = "ShippingOrBOLs", action = "Add" }
    );
    routes.MapRoute(
    name: "EventAdd",
    url: "Calendar/{companyId}/{addressId}",
    defaults: new { controller = "Calendar", action = "Index" }
    );
    routes.MapRoute(
    name: "MessagesAdd",
    url: "Messages/Add/{user}",
    defaults: new { controller = "Messages", action = "Add" }
    );
    routes.MapRoute(
    name: "PastDue",
    url: "InvoiceManager/PastDue/{id}/{pastdue}",
    defaults: new { controller = "InvoiceManager", action = "PastDue" }
    );
    routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

问题根源与解决方案

最后我终于找到问题所在了:问题出在columnDefs: [{ orderable: false, targets: [2, 3, 4, 5, 6] }]这一行。

当前表格一共6列,列索引是从0开始计数的,有效索引范围是0到5,但这里的targets数组里包含了索引6,超出了有效范围。

  • 在非服务端模式下,这种错误只会导致加载时格式异常(比如按钮、搜索框、记录数等不显示),但数据还能加载出来;
  • 而在服务端模式下,DataTables在生成请求之前就会因为这个索引错误直接失败,导致请求根本发不到Controller的Action里。

解决方法很简单,把targets数组里的6去掉就行,修改后的代码如下:

columnDefs: [{ orderable: false, targets: [2, 3, 4, 5] }]

之前我一直误以为是路由的问题,走了不少弯路,最后在DataTables相关社区找到了对应的解决方案。


内容的提问来源于stack exchange,提问作者Scott Purtan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 13:42:42