Search code examples
c#asp.net-core.net-coreasp.net-web-api-routingasp.net-core-5.0

.NET Core 3.1/5 Api Querystring parameters do not work


I have noticed something strange with my .Net Core 3.1 and .Net Core 5 Apis. When there are 2 different GET methods to retrieve all records and a single record by id, route based parameters work but querystring parameters do not.

Here is some sample code

using Core5TestApi.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Core5TestApi.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class DataController : ControllerBase
    {
        private readonly ILogger<DataController> _logger;
        List<Data> allData = new List<Data>{
                new Data { Id = 1, Name = "Name 1" },
                new Data { Id = 2, Name = "Name 2" }
            };

        public DataController(ILogger<DataController> logger)
        {
            _logger = logger;
        }

        [HttpGet]
        public IEnumerable<Data> GetAll ()
        {
            return allData;
        }

        [HttpGet("{id}")]
        [Route("{id}")]
        public Data Get(int id)
        {
            return allData.First(i => i.Id == id);
        }
    }
}

The following are the urls and the results

Call: http://localhost:51672/api/data Result:[{"id":1,"name":"Name 1"},{"id":2,"name":"Name 2"}]

Call: http://localhost:51672/api/data/1 Result: {"id":1,"name":"Name 1"}

Call: http://localhost:51672/api/data?id=1 Result: [{"id":1,"name":"Name 1"},{"id":2,"name":"Name 2"}]

The last result ignores the querystring parameter and performs the GetAll.

I feel like I am missing something very basic though I believe I have seen this work before

I have also attempted the [FromQuery] attribute before the parameter in the Get method. This actually breaks the route based parameter path


Solution

  • When you call http://localhost:51672/api/data?id=1 routing ignores ?id=1 since there is no any route for this.

    You have to use http://localhost:51672/api/data/1 if you want the record with id=1.

    And remove from the last action [Route("{id}")], it should be:

              [HttpGet("{id}")]
                public Data Get(int id)
            {
                return allData.First(i => i.Id == id);
            }
    

    but if you still want to use http://localhost:51672/api/data?id=1 your action should be:

                 [HttpGet]
                public Data Get([FromQuery] int id)
            {
                return allData.First(i => i.Id == id);
            }
    

    Or you can use one action for both:

           //http://localhost:51672/api/data/getAll
            [HttpGet("GetAll")]
            public IEnumerable<Data> GetAll()
            {
                  return allData.ToArray();
            }
    
            [HttpGet("{id0?}")]
            [HttpGet]
            public Data Get(int id0, [FromQuery] int id)
            {
                var itemId =  id0 == 0 ? id : id0;
                if(itemId==0) ...return error
                return allData.FirstOrDefault(i => i.Id == itemId);
            }