WebAPI with POST instead of GET

WebAPI with POST instead of GET

transporter_iitransporter_ii Posts: 24Questions: 7Answers: 0

Spent the last couple of days trying my first server side DataTable w/ WebAPI 2.0. I installed DataTables.AspNet.WebApi2. I worked the example at GitHub into my existing project. I got the example code working.

First big issue was it was passing too much data in the URL for GET. I followed the docs here and changed to POST: https://datatables.net/examples/server_side/post.html

As long as I limit the table columns, it works with GET. When I change to POST, it hits the endpoint, but in "IDataTablesRequest request," request is always null. It's not with GET.

I dropped down to one column and hit the endpoint with GET and POST. The data it passes is:

Using POST (this is from the body and doesn't work):
draw=1&columns%5B0%5D%5Bdata%5D=orderNo&columns%5B0%5D%5Bname%5D=OrderNo&columns%5B0%5D%5Bsearchable%5D=false&columns%5B0%5D%5Borderable%5D=false&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&order%5B0%5D%5Bcolumn%5D=0&order%5B0%5D%5Bdir%5D=asc&order%5B0%5D%5Bname%5D=OrderNo&start=0&length=10&search%5Bvalue%5D=&search%5Bregex%5D=false

Using GET, from the URL (works):
draw=1&columns%5B0%5D%5Bdata%5D=orderNo&columns%5B0%5D%5Bname%5D=OrderNo&columns%5B0%5D%5Bsearchable%5D=false&columns%5B0%5D%5Borderable%5D=false&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&order%5B0%5D%5Bcolumn%5D=0&order%5B0%5D%5Bdir%5D=asc&order%5B0%5D%5Bname%5D=OrderNo&start=0&length=10&search%5Bvalue%5D=&search%5Bregex%5D=false&_=1742406764085

The only thing I see different between the two is that GET added: &_=1742406764085

Is there anything else I should do when swapping from GET to POST? The link for the docs I posted makes it sound like all you have to do is change it to POST and it should work.

Thanks,

Answers

  • allanallan Posts: 64,210Questions: 1Answers: 10,597 Site admin

    The _ is an anti cache parameter for GET requests.

    Your server side script needs to be told to use the body parameters from the POST request if you change it to POST. What is your server side script?

    Allan

  • transporter_iitransporter_ii Posts: 24Questions: 7Answers: 0
    edited March 24

    That's easy. It's C#. I just change the decoration for my endpoint from
    [HttpGet] to [HttpPost], which I did do.

    The block of code I posted in my question that comes from the body, that was actually copied from an actual POST, so it's hitting the endpoint and getting far enough for me to capture the body. However, at my breakpoint, the request object (IDataTablesRequest request) is always null.

    Usually when that happens, I mistyped a DTO object property or something similar. In this case, however, I would expect that if the IDataTablesRequest object works with GET, it would also work reading the same object from the body of a POST.

    That's why I ended up asking if there was anything else on the client side I needed to do, other than following the instructions on the link in my question.

    When I first moved the DataTables.AspNet.WebApi2 files into my project, I didn't register them in the Global.asax.cs file. This also causes the IDataTablesRequest object to always be null. When I got that worked out, GET started working immediately.

    However, after too many columns, GET starts erroring out on me.

    Got some time to work on it this afternoon, so I'm going to give it another go.

    Thanks,

  • transporter_iitransporter_ii Posts: 24Questions: 7Answers: 0

    In further testing, I came up with this test. Without touching a thing on the client side, I made a test class with a property of "Draw" in it. When DataTables posts to the server, I can set a breakpoint, I get no errors, and I see that Draw = 1;

    public class TestTest
    {
        public string Draw { get; set; }
    }
    
    
    [Route("api/datatables/test")]
    [HttpPost]
    public async Task<string> Post(TestTest obj)
    {
    Breakpoint, obj.Draw = 1;
    }
    

    If I change back to this:

     [Route("api/datatables/test")]
    [HttpPost]
    public async Task<JsonResult<IDataTablesResponse>> Post(IDataTablesRequest 
    request)
    {
    At my breakpoint, request = null.
    
    }
    

    In both cases of the above, the client side is this:

    serverSide: true,
    ajax: {
    url: '/api/datatables/test/',
     type: 'POST'
     },
    

    Again, in cases similar to this, it's usually I typed a DTO object property wrong, or something similar. However, in this case, that's out of my control. I would assume what the client is sending is correct, since it works with GET.

    Thanks,

  • kthorngrenkthorngren Posts: 21,837Questions: 26Answers: 5,048
    edited March 24

    AFAIK IDataTablesRequest is not something that is apart of Datatables. It looks like IDataTablesRequest is part of the ALMMa/datatables.aspnet package you linked to. Have you researched or asked in their issues section about the issue with it being null? I'm not familair with the package but I suspect you will need to do something within their package to deal with the null IDataTablesRequest.

    EDIT: It does look like the developer is not maintaining the package anymore. See this issue.

    Kevin

  • transporter_iitransporter_ii Posts: 24Questions: 7Answers: 0

    Very interesting. I converted the IDataTablesRequest interfaces to class files, the best I could, and I am able to POST it to the server and read the values.

    public class DataTablesRequest
    {
        public int Draw { get; set; }
        public int Start { get; set;  }
        public int Length { get; set; }
        public Search Search { get; set; }
        public IEnumerable<Column> Columns { get; set; }
        public Dictionary<string, object> AdditionalParameters { get; set; }
    }
    
    public class Column
    {
        public string Name { get; set; }
        public string Field { get; set; }
        public bool IsSearchable { get; set; }
        public Search Search { get; set; }
        public bool IsSortable { get; set; }
        public Sort Sort { get; set; }
        public bool SetSort { get; set; }
    }
    
    public class Sort
    {
        public int Order { get; set; }
        public SortDirection Direction { get; set; }
    }
    
    public class Search
    {
        public string Value { get; set; }
        public bool IsRegex { get; set; }
    }
    
    
    public class HomeController : ApiController
    {
        [AcceptVerbs("GET", "POST", "PUT", "Delete")]
    
        [Route("api/datatables/test")]
        [HttpPost]
        public async Task<JsonResult<IDataTablesResponse>> Post(DataTablesRequest request)
        {
    
        }
    

    Very weird that if I converted it to GET, the same controller works.

  • kthorngrenkthorngren Posts: 21,837Questions: 26Answers: 5,048

    A ran across this SO thread. Not sure it applies to you but it show your not the only one running into this issue.

    Kevin

  • transporter_iitransporter_ii Posts: 24Questions: 7Answers: 0

    Six hours later, I finally actually captured an actual error:

    Newtonsoft.Json.JsonSerializationException: Could not create an instance of type DataTables.AspNet.Core.IDataTablesRequest. Type is an interface or abstract class and cannot be instantiated. Path 'draw', line 1, position 8.
    at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, String id, Boolean& createdFromNonDefaultCreator)
    at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
    at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
    at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)}

    And if anyone needs to know how I got that, I had to put the [FromBody] in before it would show any error:

            [Route("api/datatables/test")]        
            [HttpPost]
            public async Task<JsonResult<IDataTablesResponse>> Post([FromBody] IDataTablesRequest request)
        {
            if (request == null)
            {
                var test = InternalServerError();
                var test22 = BadRequest(ModelState);               
            }
        }
    

    Set a breakpoint and view the ModelState. It only shows the errors when [FromBody] is added!

    So, looking at the error, pretty much the answer is to register it properly in Global.asax.

    I've already done that, because GET wasn't working until I did that.

  • allanallan Posts: 64,210Questions: 1Answers: 10,597 Site admin

    Interesting - thank you for posting back your findings. I haven't used the third party IDataTablesResponse class before, so I can't help much in that regard I'm afriad. DtRequest in my own library is similar, although it expects to be used in the method with Request.Form (or similar), rather than as a parameter to the method.

    Allan

  • RichardD2RichardD2 Posts: 10Questions: 2Answers: 1

    [FromBody] seems to think the body is JSON. But the body you've shown looks like a standard application/x-www-form-urlencoded body.

    Do you have any global jQuery AJAX configuration that might be sending the wrong content-type header with the request?

    Have a look at the headers of the request in your browser's dev tools network panel to see what content type is being sent.

  • transporter_iitransporter_ii Posts: 24Questions: 7Answers: 0

    Have a look at the headers of the request in your browser's dev tools network panel to see what content type is being sent.

    Content-type: application/json; charset=utf-8.

    So, with the [FromBody] attribute, it just mysteriously started working for me. Just to make sure I'm not crazy, I rebuilt my endpoint from the GitHub example I started with. I have made no other changes in my project, either. I changed zero on the client side. With [FromBody], the "request" object is no longer null. If I remove [FromBody], then it is null again.

    I'll admit, I'm not a high level developer. I mainly do A LOT of database crud, and I keep it as simple as I can. But I've been at it long enough that I can say, something usually doesn't fix itself, especially after I've been working on it for two straight days.

    Even if I get the table working the way I want, this all makes me nervous about trying to move this into production.

    [FromBody] seems to think the body is JSON. But the body you've shown looks like a standard application/x-www-form-urlencoded body.

    Looking through SO, I still don't quite understand the [FromBody] attribute. It seems redundant to [HttpPost], which means it's a POST and is going to use the request body.

    Thanks,

Sign In or Register to comment.