ASP.NET8 使用 DataTables.net – 第6部分 – 在 AJAX 中返回附加参数
在 Asp.Net 8 MVC 应用程序中使用 jQuery DataTables.net 组件的实用指南。
1 ASP.NET8 使用 jQuery DataTables.net
我正在评估 jQuery DataTables.net 组件 [1] 在 ASP.NET8 项目中的使用,并创建了几个原型(概念验证)应用程序,这些应用程序将在这些文章中介绍。
1.1 本系列文章
本系列文章包括:
- ASP.NET8 使用 DataTables.net – 第1部分 – 基础
- ASP.NET8 使用 DataTables.net – 第2部分 – 操作按钮
- ASP.NET8 使用 DataTables.net – 第3部分 – 状态保存
- ASP.NET8 使用 DataTables.net – 第4部分 – 多语言
- ASP.NET8 使用 DataTables.net – 第5部分 – 在 AJAX 中传递附加参数
- ASP.NET8 使用 DataTables.net – 第6部分 – 在 AJAX 中返回附加参数
- ASP.NET8 使用 DataTables.net – 第7部分 – 常规按钮
- ASP.NET8 使用 DataTables.net – 第8部分 – 选择行
- ASP.NET8 使用 DataTables.net – 第9部分 – 高级筛选器
2 最终结果
本文的目标是创建一个概念验证应用程序,演示 DataTables.net 组件在 AJAX 中返回附加参数。让我们展示本文的结果。
重点是,DataTables.net 组件允许您传递一些附加参数,以便在 AJAX 调用中返回到客户端。我正在表单上显示这些数据。 这些数据是通过 AJAX 调用从后端传递到前端的。
3 客户端 DataTables.net 组件
在这里,我将只展示使用 DataTables 组件的 ASP.NET 视图的样子。
<!-- Employees.cshtml -->
<partial name="_LoadingDatatablesJsAndCss" />
@{
<div class="text-center">
<h3 class="display-4">Employees table</h3>
</div>
<!-- Here is our table HTML element defined. JavaScript library Datatables
will do all the magic to turn it into interactive component -->
<table id="EmployeesTable01" class="table table-striped table-bordered ">
</table>
<div class="text-center">
<h6>Passed parameters</h6>
rParam1: <span id="rParam1"></span> <br />
rParam2: <span id="rParam2"></span>
</div>
}
<script>
// Datatables script initialization=========================================
// we used defer attribute on jQuery so it might not be available at this point
// so we go for vanilla JS event
document.addEventListener("DOMContentLoaded", InitializeDatatable);
function InitializeDatatable() {
$("#EmployeesTable01").dataTable({
//processing-Feature control the processing indicator.
processing: true,
//paging-Enable or disable table pagination.
paging: true,
//info-Feature control table information display field
info: true,
//ordering-Feature control ordering (sorting) abilities in DataTables.
ordering: true,
//searching-Feature control search (filtering) abilities
searching: true,
//search.return-Enable / disable DataTables' search on return.
search: {
return: true
},
//autoWidth-Feature control DataTables' smart column width handling.
autoWidth: true,
//lengthMenu-Change the options in the page length select list.
lengthMenu: [10, 15, 25, 50, 100],
//pageLength-Change the initial page length (number of rows per page)
pageLength: 10,
//order-Initial order (sort) to apply to the table.
order: [[1, 'asc']],
//serverSide-Feature control DataTables' server-side processing mode.
serverSide: true,
//Load data for the table's content from an Ajax source.
ajax: {
"url": "@Url.Action("EmployeesDT", "Home")",
"type": "POST",
"datatype": "json",
"dataSrc": extractReturnParameters
},
//Set column specific initialization properties
columns: [
//name-Set a descriptive name for a column
//data-Set the data source for the column from the rows data object / array
//title-Set the column title
//orderable-Enable or disable ordering on this column
//searchable-Enable or disable search on the data in this column
//type-Set the column type - used for filtering and sorting string processing
//visible-Enable or disable the display of this column.
//width-Column width assignment.
//render-Render (process) the data for use in the table.
//className-Class to assign to each cell in the column.
{ //0
name: 'id',
data: 'id',
title: "Employee Id",
orderable: true,
searchable: false,
type: 'num',
visible: false
},
{
//1
name: 'givenName',
data: "givenName",
title: "Given Name",
orderable: true,
searchable: true,
type: 'string',
visible: true
},
{
//2
name: 'familyName',
data: "familyName",
title: "Family Name",
orderable: true,
searchable: true,
type: 'string',
visible: true
},
{
//3
name: 'town',
data: "town",
title: "Town",
orderable: true,
searchable: true,
type: 'string',
visible: true
},
{
//4
name: 'country',
data: "country",
title: "Country",
orderable: true,
searchable: true,
type: 'string',
visible: true
},
{
//5
name: 'email',
data: "email",
title: "Email",
orderable: true,
searchable: true,
type: 'string',
visible: true
},
{
//6
name: 'phoneNo',
data: "phoneNo",
title: "Phone Number",
orderable: false,
searchable: true,
type: 'string',
visible: true
}
]
});
function extractReturnParameters(json) {
let rParam1 = json.rParam1;
let rParam2 = json.rParam2;
//async work
setTimeout(() => doWorkWithParamters(rParam1, rParam2), 0);
return json.data;
}
function doWorkWithParamters(rParam1, rParam2) {
let span1 = document.getElementById('rParam1');
span1.innerHTML = rParam1;
let span2 = document.getElementById('rParam2');
span2.innerHTML = rParam2;
}
}
</script>
请注意,在 ajax 属性中,我们使用 dataSrc 属性传递对 JavaScript 函数 extractReturnParameters 的引用,该函数将提取返回的参数。
有关这些属性的更多信息,请参阅 [1] 中的手册。这里的应用程序只是 ASP.NET 环境的一个概念验证。
4 示例 AJAX 调用
为了更好地理解发生了什么,我使用 Chrome DevTools 捕获了在示例 AJAX 调用中返回的数据。
这是 AJAX 调用响应
{
"draw": 1,
"recordsTotal": 1000,
"recordsFiltered": 1000,
"data": [
{
"id": 459,
"givenName": "Abbe",
"familyName": "Crouse",
"town": "San Jerónimo",
"country": "Peru",
"email": "acrousecq@rakuten.co.jp",
"phoneNo": "590-568-7650"
},
{
"id": 854,
"givenName": "Abbe",
"familyName": "Geraldo",
"town": "Kornyn",
"country": "Ukraine",
"email": "ageraldonp@adobe.com",
"phoneNo": "346-510-7193"
},
{
"id": 726,
"givenName": "Abigael",
"familyName": "Davidai",
"town": "Rukaj",
"country": "Albania",
"email": "adavidaik5@blog.com",
"phoneNo": "750-275-0919"
},
{
"id": 256,
"givenName": "Adaline",
"familyName": "Waren",
"town": "Pruszcz Gdański",
"country": "Poland",
"email": "awaren73@netscape.com",
"phoneNo": "887-568-2194"
},
{
"id": 375,
"givenName": "Adelaide",
"familyName": "O'Dowgaine",
"town": "Fajsławice",
"country": "Poland",
"email": "aodowgaineae@networkadvertising.org",
"phoneNo": "304-825-5438"
},
{
"id": 145,
"givenName": "Adelheid",
"familyName": "Fer",
"town": "Tužno",
"country": "Croatia",
"email": "afer40@bravesites.com",
"phoneNo": "983-283-7629"
},
{
"id": 340,
"givenName": "Adrian",
"familyName": "Thunnercliffe",
"town": "Avelinha",
"country": "Portugal",
"email": "athunnercliffe9f@hibu.com",
"phoneNo": "819-620-4630"
},
{
"id": 585,
"givenName": "Ag",
"familyName": "Winchcombe",
"town": "Tinghir",
"country": "Morocco",
"email": "awinchcombeg8@istockphoto.com",
"phoneNo": "573-856-3366"
},
{
"id": 389,
"givenName": "Aggy",
"familyName": "Dacre",
"town": "Rosignol",
"country": "Guyana",
"email": "adacreas@china.com.cn",
"phoneNo": "941-421-7755"
},
{
"id": 464,
"givenName": "Aharon",
"familyName": "Wathall",
"town": "Mimbaan Timur",
"country": "Indonesia",
"email": "awathallcv@bbc.co.uk",
"phoneNo": "108-340-8110"
}
],
"rParam1": "My parameter",
"rParam2": 12345
}
我当然只提取了相关的数据部分。 每一个优秀的 Web 程序员都应该能够从上面的数据中理解发生了什么。
5 DataTables.AspNet.Core 库中的错误
不幸的是,C#/.NET 库 DataTables.AspNet.Core 中存在一个与在 AJAX 调用中返回参数相关的错误。 有一个针对该错误的解决方法,我在此示例中使用了它。 这是用于解决该错误的 Helper 类。
using DataTables.AspNet.AspNetCore;
using Newtonsoft.Json;
using System.Collections;
//DataTablesResponseHelper.cs
namespace Example06
{
/*
* This code has been taken from https://github.com/ALMMa/datatables.aspnet/issues/72
* It resolves the bug in library DataTables.AspNet.AspNetCore
* when returning parameters from server to client
* proper usage is as follows
*
* // Create some response additional parameters.
* var returnParameters = new Dictionary<string, object>()
* {
* { "rParam1", "My parameter" },
* { "rParam2", 12345 }
* };
*
* var response = DataTablesResponse.Create(
* request, totalRecordsCount, filteredRecordsCount, dataPage, returnParameters);
* var response2 = new DataTablesResponseHelper(response);
*
* return new DataTablesJsonResult(response2, true);
*/
public class DataTablesResponseHelper : DataTables.AspNet.AspNetCore.DataTablesResponse
{
protected DataTablesResponseHelper(int draw, string errorMessage) :
base(draw, errorMessage)
{
}
protected DataTablesResponseHelper(int draw, string errorMessage,
IDictionary<string, object> additionalParameters) :
base(draw, errorMessage, additionalParameters)
{
}
protected DataTablesResponseHelper(int draw, int totalRecords, int totalRecordsFiltered, object data) :
base(draw, totalRecords, totalRecordsFiltered, data)
{
}
protected DataTablesResponseHelper(int draw, int totalRecords, int totalRecordsFiltered,
object data, IDictionary<string, object> additionalParameters) :
base(draw, totalRecords, totalRecordsFiltered, data, additionalParameters)
{
}
public DataTablesResponseHelper(DataTablesResponse response) :
this(response.Draw, response.TotalRecords, response.TotalRecordsFiltered,
response.Data, response.AdditionalParameters)
{
}
private bool IsSuccessResponse()
{
return this.Data != null && string.IsNullOrWhiteSpace(this.Error);
}
public override string ToString()
{
using (StringWriter stringWriter = new StringWriter())
{
using (JsonTextWriter jsonTextWriter = new JsonTextWriter((TextWriter)stringWriter))
{
if (this.IsSuccessResponse())
{
jsonTextWriter.WriteStartObject();
jsonTextWriter.WritePropertyName(Configuration.Options.ResponseNameConvention.Draw, true);
jsonTextWriter.WriteValue(this.Draw);
jsonTextWriter.WritePropertyName(Configuration.Options.ResponseNameConvention.TotalRecords, true);
jsonTextWriter.WriteValue(this.TotalRecords);
jsonTextWriter.WritePropertyName(Configuration.Options.ResponseNameConvention.TotalRecordsFiltered, true);
jsonTextWriter.WriteValue(this.TotalRecordsFiltered);
jsonTextWriter.WritePropertyName(Configuration.Options.ResponseNameConvention.Data, true);
jsonTextWriter.WriteRawValue(this.SerializeData(this.Data));
if (this.AdditionalParameters != null)
{
foreach (KeyValuePair<string, object> additionalParameter in (IEnumerable<KeyValuePair<string, object>>)this.AdditionalParameters)
{
jsonTextWriter.WritePropertyName(additionalParameter.Key, true);
if (additionalParameter.Value is IEnumerable)
{
jsonTextWriter.WriteRawValue(SerializeData(additionalParameter.Value));
}
else
{
jsonTextWriter.WriteValue(additionalParameter.Value);
}
}
}
jsonTextWriter.WriteEndObject();
}
else
{
jsonTextWriter.WriteStartObject();
jsonTextWriter.WritePropertyName(Configuration.Options.ResponseNameConvention.Draw, true);
jsonTextWriter.WriteValue(this.Draw);
jsonTextWriter.WritePropertyName(Configuration.Options.ResponseNameConvention.Error, true);
jsonTextWriter.WriteValue(this.Error);
if (this.AdditionalParameters != null)
{
foreach (KeyValuePair<string, object> additionalParameter in
(IEnumerable<KeyValuePair<string, object>>) this.AdditionalParameters)
{
jsonTextWriter.WritePropertyName(additionalParameter.Key, true);
if (additionalParameter.Value is IEnumerable)
{
jsonTextWriter.WriteRawValue(SerializeData(additionalParameter.Value));
}
else
{
jsonTextWriter.WriteValue(additionalParameter.Value);
}
}
}
jsonTextWriter.WriteEndObject();
}
jsonTextWriter.Flush();
return stringWriter.ToString();
}
}
}
}
}
6 ASP.NET 后端处理
现在我们来到了 C#/.NET 部分,编写我们的 ASP.NET 代码。 这是我想出的解决方案。 我不会假装这很简单,它假设对 DataTables.AspNet.Core 和 System.Linq.Dynamic.Core 库有很好的理解。
这是代码
//HomeController.cs
//this is target of AJAX call and provides data for
//the table, based on selected input parameters
public IActionResult EmployeesDT(DataTables.AspNet.Core.IDataTablesRequest request)
{
// There is dependency in this method on names of fields
// and implied mapping. I see it almost impossible to avoid.
// At least, in this method, we avoided dependency on the order
// of table fields, in case order needs to be changed
//Here are our mapped table columns:
//Column0 id -> Employee.Id
//Column1 givenName -> Employee.FirstName
//Column2 familyName -> Employee.LastName
//Column3 town -> Employee.City
//Column4 country -> Employee.Country
//Column5 email -> Employee.Email
//Column6 phoneNo -> Employee.Phone
try
{
IQueryable<Employee> employees = MockDatabase.MockDatabase.Instance.EmployeesTable.AsQueryable();
int totalRecordsCount = employees.Count();
var iQueryableOfAnonymous = employees.Select(p => new
{
id = p.Id,
givenName = p.FirstName,
familyName = p.LastName,
town = p.City,
country = p.Country,
email = p.Email,
phoneNo = p.Phone,
});
iQueryableOfAnonymous = FilterRowsPerRequestParameters(iQueryableOfAnonymous, request);
int filteredRecordsCount = iQueryableOfAnonymous.Count();
iQueryableOfAnonymous = SortRowsPerRequestParamters(iQueryableOfAnonymous, request);
iQueryableOfAnonymous = iQueryableOfAnonymous.Skip(request.Start).Take(request.Length);
var dataPage = iQueryableOfAnonymous.ToList();
// Create some response additional parameters.
var returnParameters = new Dictionary<string, object>()
{
{ "rParam1", "My parameter" },
{ "rParam2", 12345 }
};
var response = DataTablesResponse.Create(request, totalRecordsCount, filteredRecordsCount, dataPage, returnParameters);
var response2 = new DataTablesResponseHelper(response);
return new DataTablesJsonResult(response2, true);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
var response = DataTablesResponse.Create(request, "Error processing AJAX call on server side");
return new DataTablesJsonResult(response, false);
}
}
//Program.cs
namespace Example06
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
// DataTables.AspNet registration
DataTablesAspNetRegistration(builder);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
}
//DataTables.AspNet registration
public static void DataTablesAspNetRegistration(WebApplicationBuilder? builder)
{
if (builder == null) { throw new Exception("builder == null"); };
var options = new DataTables.AspNet.AspNetCore.Options()
.EnableRequestAdditionalParameters()
.EnableResponseAdditionalParameters();
// DataTables.AspNet registration
builder.Services.RegisterDataTables(options);
}
}
}
7 结论
可以下载完整的示例代码项目。
8 参考
[1] https://datatables.net.cn/
[21] ASP.NET8 使用 DataTables.net – 第1部分 – 基础
https://codeproject.org.cn/Articles/5385033/ASP-NET-8-Using-DataTables-net-Part1-Foundation
[22] ASP.NET8 使用 DataTables.net – 第2部分 – 操作按钮
https://codeproject.org.cn/Articles/5385098/ASP-NET8-using-DataTables-net-Part2-Action-buttons
[23] ASP.NET8 使用 DataTables.net – 第3部分 – 状态保存
https://codeproject.org.cn/Articles/5385308/ASP-NET8-using-DataTables-net-Part3-State-saving
[24] ASP.NET8 使用 DataTables.net – 第4部分 – 多语言
https://codeproject.org.cn/Articles/5385407/ASP-NET8-using-DataTables-net-Part4-Multilingual
[25] ASP.NET8 使用 DataTables.net – 第5部分 – 在 AJAX 中传递附加参数
https://codeproject.org.cn/Articles/5385575/ASP-NET8-using-DataTables-net-Part5-Passing-additi
[26] ASP.NET8 使用 DataTables.net – 第6部分 – 在 AJAX 中返回附加参数
https://codeproject.org.cn/Articles/5385692/ASP-NET8-using-DataTables-net-Part6-Returning-addi
[27] ASP.NET8 使用 DataTables.net – 第7部分 – 常规按钮
https://codeproject.org.cn/Articles/5385828/ASP-NET8-using-DataTables-net-Part7-Buttons-regula
[28] ASP.NET8 使用 DataTables.net – 第8部分 – 选择行
https://codeproject.org.cn/Articles/5386103/ASP-NET8-using-DataTables-net-Part8-Select-rows
[29] ASP.NET8 使用 DataTables.net – 第9部分 – 高级筛选器
https://codeproject.org.cn/Articles/5386263/ASP-NET8-using-DataTables-net-Part9-Advanced-Filte