I am dealing with DataTables.AspNet nuget package to implement server-side pagination, searching and filtering.
I am trying to post my table name from ajax datatables to WebApi controller in order to get the specific C# DataTable to be returned as data source. I pack table name as a key-value pair of IDictionary<string, object> AdditionalParameters ("table" is a key). When the request is processed by Data method of WebApi controller, the IDataTablesRequest argument is always coming as null so I can't get my table name! Moreover, when I assign the table name explicitly and get DataTable, DataTablesResponse.Create() returns null.
So how can I force IDataTablesRequest to get data passed from datatables.js?
Client Side
function loadTable(type, editable) {
var url = "/api/Request/" + type;
$.ajax({
url: "/api/Request/Columns/" + type,
type: "GET",
statusCode: {
200: function (response) {
dtcolumns = response;
var columns = [];
if (editable) { columns.push({ data: null, className: 'select-checkbox', defaultContent: '' }); }
for (var i in dtcolumns) {
columns.push(
{
name: dtcolumns[i].Name,
title: dtcolumns[i].Caption,
data: dtcolumns[i].Name,
visible: !(dtcolumns[i].Caption === ""),
sortable: true,
searchable: true
});
}
var request_data = new Object();
var AdditionalParameters = {};
AdditionalParameters["table"] = type; // Table Name!
request_data.AdditionalParameters = AdditionalParameters;
var table = $('#just-table').DataTable({
deferRender: true,
bPaginate: true,
autoFill: {
horizontal: false
},
serverSide: true,
processing: true,
columns: columns,
ajax: {
url: "/api/Request/Data",
type: "POST",
//contentType: "application/json",
//dataType: "json",
//data: JSON.stringify(request_data),
data: request_data, //PASSING TABLE NAME TO WEBAPI CONTROLLER
statusCode: {
500: function (response) {
alert(response.responseJSON.Message)
}
}
},
rowId: 'ID',
scroller: {
loadingIndicator: true
},
targets: 0,
dom: 'BS<"toolbar">frtips'
});
}
}
})
}
Server Side
[HttpPost]
[Route("api/Request/Data")]
public JsonResult<IDataTablesResponse> Data(IDataTablesRequest request /*ALWAYS NULL!!!*/)
{
DataTable data = DBContext.GetInstance()[request.AdditionalParameters["table"].ToString()];
var filteredData = data.AsEnumerable().Where(row => row.ItemArray.Contains(request.Search.Value));
var dataPage = filteredData.Skip(request.Start).Take(request.Length).CopyToDataTable();
//ALWAYS NULL!!!
var response = DataTables.AspNet.WebApi2.DataTablesResponse.Create(request, data.AsDataView().Count, filteredData.Count(), dataPage);
return new DataTablesJsonResult(response, Request);
}
Your ajax query is GET yet the method is decorated with a POST attribute
Late to the reply, but here's my two cents worth as I had the same question.
An MVC controller cannot construct an interface as it doesn't know or understand which implementation of that interface to use.
Create a class that implements the interface and use that as the method parameter identifier.
see also: Passing an interface to an ASP.NET MVC Controller Action method
Related
I'm using ajax to send an int64 and a string from a month selector to my server-side, but my variable is always null or 0.
JS:
function deleteConta(id) {
var data = $("#monthSelector").val();
var params = {
id: id,
dataAtual: data
};
$.ajax({
type: "POST",
url: '/Contas/ContaView?handler=Delete',
contentType: "application/json; charset=utf-8",
data: JSON.stringify(params),
headers:
{
"RequestVerificationToken": $('input:hidden[name="__RequestVerificationToken"]').val()
},
success: function (partialReturn) {
$("#partial").html(partialReturn);
}
});
}
C#:
public PartialViewResult OnPostDelete([FromBody] long id, string dataAtual)
{
contaDTO.Remove(id, contaDTO.Database, contaDTO.ContaCollection);
dataCorrente = DateTime.ParseExact(dataAtual, "yyyy-MM", null).AddMonths(1);
contas = BuscarContasUsuarioMes(User.Identity.Name, dataCorrente);
return Partial("_PartialContas", contas);
}
I already checked with debugger and my variables are ok and filled with value expected (One test was like {id: 50, dataAtual: '2023-01'}
Checked a lot of forums, but Couldn't figure out how to make this thing work.
By declaring the number parameter with [FromBody] you tell ASP.NET Core to use the input formatter to bind the provided JSON (or XML) to a model. So your test should work, if you provide a simple model class.
Have you tried to remove it and sending value to the action?
—- UPDATE ——
Try this
function deleteConta(id) {
var data = $("#monthSelector").val();
$.ajax({
type: "POST",
url: '/Contas/ContaView?handler=Delete',
data: { id: id, dataAtual: data },
headers:
{
"RequestVerificationToken": $('input:hidden[name="__RequestVerificationToken"]').val()
},
success: function (partialReturn) {
$("#partial").html(partialReturn);
}
});
}
I have been trying to post two parameteres...
This is the ajax code
function Kaydet() {
var params = {};
var Kiralayan = $("#RentForm").serialize();
params.kisi = Kiralayan ;
params.aracid = P.AracID;
console.log(params);
$.ajax({
type: "POST",
url: '#Url.Action("Save","AracKirala")',
data: params,
dataType: "text",
success: function (response) {
if (response != "OK") {
alert("Kayıt yapılamadı.");
}
else {
document.getElementById("RentForm").reset();
alert("Kayıt başarıyla gerçekleştirildi.");
$("#myModal").modal('hide');
Ara();
}
}
});
Method
public ActionResult Save(Kiralayan kisi = null, int aracid = 0)
{
the problem is ajax posts "aracid" corretly but "kisi" turns null when the method is trigged...
I tried not to post "aracid" with "kisi" so ajax posted well for one parameter "kisi", but doesnt work together...
If you serializing the form, then you can add additional values to it with the .param() function
var data = $("#RentForm").serialize() + '&' + $.param({ 'aracid': AracID }, true);
$.ajax({
type: "POST",
url: '#Url.Action("Save","AracKirala")',
data: data,
....
MVC will map the object for you, so you might as well skip the extract nesting of the form within the object.
Notes:
If aracid is also a property in the model, it will map to both the property and the extra parameter.
Using push on the serialise() collection is more maintainable than the alternative of concatenating strings before the serialize() call.
e.g.
var Kiralayan = $("#RentForm").serialize();
// Add the extra non-form parameter
Kiralayan.push({name: 'aracid', value: P.AracID});
Full example:
function Kaydet() {
var Kiralayan = $("#RentForm").serialize();
// Add the extra non-form parameter
Kiralayan.push({name: 'aracid', value: P.AracID});
console.log(params);
$.ajax({
type: "POST",
url: '#Url.Action("Save","AracKirala")',
data: Kiralayan,
dataType: "text",
success: function (response) {
if (response != "OK") {
alert("Kayıt yapılamadı.");
}
else {
document.getElementById("RentForm").reset();
alert("Kayıt başarıyla gerçekleştirildi.");
$("#myModal").modal('hide');
Ara();
}
}
});
I don´t know why my parameter "ParametroFiltro Filtro" is getting null, the other parameters "page" and "pageSize" is getting OK.
public class ParametroFiltro
{
public string Codigo { get; set; }
public string Descricao { get; set; }
}
My ApiController Get method:
public PagedDataModel<ParametroDTO> Get(ParametroFiltro Filtro, int page, int pageSize)
My ajax call:
var fullUrl = "/api/" + self.Api;
$.ajax({
url: fullUrl,
type: 'GET',
dataType: 'json',
data: { Filtro: { Codigo: '_1', Descricao: 'TESTE' }, page: 1, pageSize: 10 },
success: function (result) {
alert(result.Data.length);
self.Parametros(result.Data);
}
});
You are trying to send a complex object with GET method. The reason this is failing is that GET method can't have a body and all the values are being encoded into the URL. You can make this work by using [FromUri], but first you need to change your client side code:
$.ajax({
url: fullUrl,
type: 'GET',
dataType: 'json',
data: { Codigo: '_1', Descricao: 'TESTE', page: 1, pageSize: 10 },
success: function (result) {
alert(result.Data.length);
self.Parametros(result.Data);
}
});
This way [FromUri] will be able to pick up your complex object properties directly from the URL if you change your action method like this:
public PagedDataModel<ParametroDTO> Get([FromUri]ParametroFiltro Filtro, int page, int pageSize)
Your previous approach would rather work with POST method which can have a body (but you would still need to use JSON.stringify() to format body as JSON).
Provide the contentType property when you make the ajax call. Use JSON.stringify method to build the JSON data to post. change the type to POST and MVC Model binding will bind the posted data to your class object.
var filter = { "Filtro": { "Codigo": "_1", "Descricao": "TESTE" },
"page": "1", "pageSize": "10" };
$.ajax({
url: fullUrl,
type: 'POST',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(filter),
success: function (result) {
alert(result.Data.length);
self.Parametros(result.Data);
}
});
It's also possible to access POST variables via a Newtonsoft.Json.Linq JObject.
For example, this POST:
$.ajax({
type: 'POST',
url: 'URL',
data: { 'Note': note, 'Story': story },
dataType: 'text',
success: function (data) { }
});
Can be accessed in an APIController like so:
public void Update([FromBody]JObject data)
{
var Note = (String)data["Note"];
var Story = (String)data["Story"];
}
If you append json data to query string, and parse it later in web api side. you can parse complex object too. It's useful rather than post json object, espeicaly in some special httpget requirement case.
//javascript file
var data = { UserID: "10", UserName: "Long", AppInstanceID: "100", ProcessGUID: "BF1CC2EB-D9BD-45FD-BF87-939DD8FF9071" };
var request = JSON.stringify(data);
request = encodeURIComponent(request);
doAjaxGet("/ProductWebApi/api/Workflow/StartProcess?data=", request, function (result) {
window.console.log(result);
});
//webapi file:
[HttpGet]
public ResponseResult StartProcess()
{
dynamic queryJson = ParseHttpGetJson(Request.RequestUri.Query);
int appInstanceID = int.Parse(queryJson.AppInstanceID.Value);
Guid processGUID = Guid.Parse(queryJson.ProcessGUID.Value);
int userID = int.Parse(queryJson.UserID.Value);
string userName = queryJson.UserName.Value;
}
//utility function:
public static dynamic ParseHttpGetJson(string query)
{
if (!string.IsNullOrEmpty(query))
{
try
{
var json = query.Substring(7, query.Length - 7); //seperate ?data= characters
json = System.Web.HttpUtility.UrlDecode(json);
dynamic queryJson = JsonConvert.DeserializeObject<dynamic>(json);
return queryJson;
}
catch (System.Exception e)
{
throw new ApplicationException("can't deserialize object as wrong string content!", e);
}
}
else
{
return null;
}
}
In .NET Core, the HttpClient sets the transfer-encoding: chunked header by default. This can cause the .NET Web API controller parameters to be null.
To get around this, you'll need to set the ContentLength header explicitly:
var json = JsonConvert.SerializeObject(myObject);
var content = new StringContent(json, Encoding.UTF8, "application/json");
content.Headers.ContentLength = json.Length;
var response = await client.PostAsync("http://my-api.com", content);
SO answer if you already know the transfer-encoding header is the issue: How to disable Chunked Transfer Encoding in ASP.Net C# using HttpClient
Related bug which won't be fixed, which gives some insight into the problem: https://github.com/dotnet/runtime/issues/30283
I have been trying to post two parameteres...
This is the ajax code
function Kaydet() {
var params = {};
var Kiralayan = $("#RentForm").serialize();
params.kisi = Kiralayan ;
params.aracid = P.AracID;
console.log(params);
$.ajax({
type: "POST",
url: '#Url.Action("Save","AracKirala")',
data: params,
dataType: "text",
success: function (response) {
if (response != "OK") {
alert("Kayıt yapılamadı.");
}
else {
document.getElementById("RentForm").reset();
alert("Kayıt başarıyla gerçekleştirildi.");
$("#myModal").modal('hide');
Ara();
}
}
});
Method
public ActionResult Save(Kiralayan kisi = null, int aracid = 0)
{
the problem is ajax posts "aracid" corretly but "kisi" turns null when the method is trigged...
I tried not to post "aracid" with "kisi" so ajax posted well for one parameter "kisi", but doesnt work together...
If you serializing the form, then you can add additional values to it with the .param() function
var data = $("#RentForm").serialize() + '&' + $.param({ 'aracid': AracID }, true);
$.ajax({
type: "POST",
url: '#Url.Action("Save","AracKirala")',
data: data,
....
MVC will map the object for you, so you might as well skip the extract nesting of the form within the object.
Notes:
If aracid is also a property in the model, it will map to both the property and the extra parameter.
Using push on the serialise() collection is more maintainable than the alternative of concatenating strings before the serialize() call.
e.g.
var Kiralayan = $("#RentForm").serialize();
// Add the extra non-form parameter
Kiralayan.push({name: 'aracid', value: P.AracID});
Full example:
function Kaydet() {
var Kiralayan = $("#RentForm").serialize();
// Add the extra non-form parameter
Kiralayan.push({name: 'aracid', value: P.AracID});
console.log(params);
$.ajax({
type: "POST",
url: '#Url.Action("Save","AracKirala")',
data: Kiralayan,
dataType: "text",
success: function (response) {
if (response != "OK") {
alert("Kayıt yapılamadı.");
}
else {
document.getElementById("RentForm").reset();
alert("Kayıt başarıyla gerçekleştirildi.");
$("#myModal").modal('hide');
Ara();
}
}
});
I am having trouble passing my array via a $.post.
The Javascript
var privIDs = [1,2,4,5];
$.post("/Home/GrantPrivilegesToUser", { privilegeIDs: privIDs }, function (data) {
alert("Data Loaded: " + data.success);
});
The Action
public ActionResult GrantPrivilegesToUser(int[] privilegeIDs)
{
return Json(new {success=true});
}
The action sees privilegeIDs as null. Any ideas?
You need to set jQuery.ajaxSettings.traditional = true; for you jQuery ajax setting. In jQuery 1.4 they changed the way items are serialized in a form post.
please see:
http://forum.jquery.com/topic/nested-param-serialization
And:
How can I post an array of string to ASP.NET MVC Controller without a form?
I use JSON to pass data as a string using the JSON2 library: http://www.json.org/js.html
var privIDs = [1,2,3,4,5];
var data = JSON.stringify({privilegeIDs : privIDs});
$.POST("/Home/GrantPrivilegesToUser", data, function (data) {
alert("Data Loaded: " + data.success);
});
And the action would use the WebMethod type:
[WebMethod]
public object Entry_GetFormOptions(string privilegeIDs)
{
return new {success=true};
}
There are both built-in and 3rd party functions for parsing the received JSON to access the privilege IDs.
Let me know if this does or does not help.
This is what I ended up doing. Might be helpful to someone...
var privIDs = [1,2,4,5];
$.ajax({
type: "POST",
url: "/Home/UpdatePrivileges",
data: { privilegeIDs: privIDs },
traditional: true,
success: function (json) {
alert(json.success);
}
});
If you use .ajax instead of .post you can include traditional: true