8

I would like to be able to handle a complex type as an argument to my HttpGet method.

The method pickNFirstElements works when the api method does not take any parameters, but not when i try to pass the object.

I have an idea that i need to inform my ajax query that the data is a single object, but i don't know how that is done, as i thought that was the point of BindProperties tag in the TodoItem class.

[HttpGet]
        [Route("search")]
        public async Task<ActionResult<TodoItem>> GetMatchingTodoItem([FromQuery]TodoItem todo)
        {
            // var name = todo.Name;
            // var completed = todo.IsComplete;
            return await _context.TodoItems.FirstAsync();
        }
function pickNFirstElements() {
    const item = {
        Name: "dope",
        IsComplete: false,
        Id: 2
    }
    $.ajax({
        type: "GET",
        url: uri+"/search",
        data: { name: item.Name, isComplete: item.IsComplete, Id: Item.Id },
        cache: false,
        success: function (return1) {
            alert(return1.name);
        }
    })
};
namespace TodoApi.Models
{
    [Microsoft.AspNetCore.Mvc.BindProperties(SupportsGet =true)]
    public class TodoItem
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public bool IsComplete { get; set; }
    }
}
Kunal Mukherjee
  • 5,775
  • 3
  • 25
  • 53
mabeto5p
  • 307
  • 3
  • 8

3 Answers3

3

Your code is actually working (almost) fine. You only have a typo in this line:

data: { name: item.Name, isComplete: item.IsComplete, Id: Item.Id },

should be lowercase 'item' instead of 'Item':

data: { name: item.Name, isComplete: item.IsComplete, Id: item.Id },

Check your console in the browser, you'll see that it cannot find the object 'Item'.

Jesse de Wit
  • 3,867
  • 1
  • 20
  • 41
1

HTTP Get are meant to be stateless and immutable. You cannot pass something in the request body using HTTP Get.

So you can either send query / route parameters.

I suggest to refactor your code to this:

Javascript:

function pickNFirstElements() {
    const item = {
        Name: "dope",
        IsComplete: false,
        Id: 2
    };
    const queryParams = new URLSearchParams(item).toString();

    $.ajax({
        type: "GET",
        url: `${uri}/search?${queryParams}`,
        cache: false,
        success: function (return1) {
            alert(return1.name);
        }
    })
};

C#:

[HttpGet("search")]
public async Task<ActionResult<TodoItem>> GetMatchingTodoItem(string name, bool isComplete, int Id)
{
    return await _context.TodoItems.FirstAsync();
}
Kunal Mukherjee
  • 5,775
  • 3
  • 25
  • 53
  • 1
    I see that this solution can work, but what if I'm working with larger classes that might have other classes as fields. Such a class would require many parameters which i would believe is bad practice? I made my initial code work with a Post instead, as it accepts complex types, but the purpose of a post should never be to retrieve elements, right? – mabeto5p Jun 28 '19 at 12:11
  • 2
    `You cannot pass something in the request body using HTTP Get.` Well, technically you can - https://stackoverflow.com/questions/978061/http-get-with-request-body . But it just isn't _useful_ to do so. :) – mjwills Jun 28 '19 at 12:20
  • 1
    @mabeto5p this solution can quickly become unwieldy but you need to take a step back and re-think what you are trying to implement, HTTP POSTs are okay if want to send a complex object and expect the response to be idempotent. – Kunal Mukherjee Jun 28 '19 at 12:28
  • 1
    Passing arguments in the 'data' field in an ajax call of type "GET" actually adds them to the querystring, instead of the body. So that's not the problem here. – Jesse de Wit Jun 28 '19 at 12:43
0

.Net Core gives you BindAttribute..

[HttpGet("route")]
public async Task<ActionResult<TodoItem>> SomeMethod([Bind(nameof(TodoItem))] TodoItem todoItem)
{
    // Your code..
}
v.t.
  • 41
  • 2