Matt G
Matt G

Reputation: 632

MVC Web API Routing to Wrong Action

I have a web API controller, when I call the default Get action this works, when I call another specific action (GetReservationsForCustomer) this also works, but one action gives an error (GetReservationsByDate), it seems to route to the default Get action. Here is the code:

    // GET: api/Reservations
    public IQueryable<Reservation> GetReservations()
    {
        return db.Reservations;
    }

    [ResponseType(typeof(ReservationDTO))]
    public IHttpActionResult GetReservationsForCustomer(int CustomerId)
    {
        IEnumerable<Reservation> reservations = db.Reservations.Where(r => r.CustomerId == CustomerId).ToList();
        List<ReservationDTO> reservationList = new List<ReservationDTO>();

        foreach(Reservation reservation in reservations)
        {
            reservationList.Add(new ReservationDTO
            {
                id = reservation.id,
                ReservationStart = reservation.ReservationStart,
                Covers = reservation.Covers
            });
        }

        return Ok(reservationList);
    }

    [ResponseType(typeof(ListReservationDTO))]
    public IHttpActionResult GetReservationsByDate(DateTime StartDate, DateTime EndDate)
    {
        IEnumerable<Reservation> reservations = new List<Reservation>();

        if (EndDate != null)
        {
            reservations = db.Reservations.Where(r => r.ReservationStart.Date >= StartDate.Date && r.ReservationStart.Date >= EndDate.Date).ToList();
        }
        else
        {
            reservations = db.Reservations.Where(r => r.ReservationStart.Date == StartDate.Date).ToList();
        }

        List<ReservationDTO> reservationList = new List<ReservationDTO>();
        foreach (Reservation res in reservations)
        {
            reservationList.Add(new ReservationDTO
            {
                id = res.id,
                ReservationStart = res.ReservationStart,
                Covers = res.Covers,
                CustomerEmail = res.Customer.EmailAddress,
                CustomerName = res.Customer.Name,
                CustomerPhone = res.Customer.PhoneNumber
            });
        }

        return Ok(reservationList);
    }

Here is my API call:

http://localhost:55601/api/Reservations/GetReservationsByDate/?StartDate=2018-03-04:T12:30:00

And here is the response:

{
    "Message": "The request is invalid.",
    "MessageDetail": "The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Http.IHttpActionResult GetReservation(Int32)' in 'GreenLionBookings.API.ReservationsController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}

Please note the specifics of the action are not relevant at this stage, I've butchered it a fair bit trying to get this to work! I've tried specifying a start date and end date and neither seems to work. It always seems to get routed to the default Get action.

Here is my RouteConfig:

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

And here is my WebApiConfig:

    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        // Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }

So both basically just default.

Why is this not routing to the correct action, just for this one and not the others? I have another controller (Customers) which seems to work correctly for all actions too. I've read this and this, and also this and this which I actually thought was pretty relevant and quite helpful, but didn't solve my problem.

What am I doing wrong here?

Upvotes: 0

Views: 164

Answers (1)

Marcus H&#246;glund
Marcus H&#246;glund

Reputation: 16801

First of all, you have a typeo in the date.

This 2018-03-04:T12:30:00 should be 2018-03-04T12:30:00.

Then, to solve the routing problem, you could leave out the action name of the url and let the framework match the request against the parameters name.

Try it like this

api/Reservations?StartDate=2018-03-04T12:30:00&EndDate=2018-03-05T12:30:00 

Then, if you want to be able to send nullable values to EndDate which is a value type of DateTime; make the DateTime nullable

[ResponseType(typeof(ListReservationDTO))]
public IHttpActionResult GetReservationsByDate(DateTime StartDate, DateTime? EndDate)

Notice the DateTime? which is a shorthand for Nullable<DateTime>

Upvotes: 1

Related Questions