Search code examples
asp.net-mvcrestasp.net-web-apiasp.net-web-api-routing

RESTful API controller/route issue


With VS 2013 I created a simple MVC/Web Api app. It has Photo model:

public class Photo
{
    public int Id{ get; set; }
    public string Name{ get; set; }
    public DateTime DateTimeOriginal { get; set; }
}

and a PhotoController:

Photo[] photos = new Photo[] 
{ 
    new Photo { Id= 1, Name= "Mary B.",  DateTimeOriginal = Convert.ToDateTime("May 18, 2015 2:01:22 PM")}, 
    new Photo { Id = 2, Name= "Martin M.",  DateTimeOriginal = Convert.ToDateTime("May 18, 2015 2:13:09 PM" )}, 
    new Photo { Id = 3, Name= "Sparky",  DateTimeOriginal = Convert.ToDateTime("May 18, 2015 2:31:22 PM")} 
};


public IEnumerable<Photo> GetAllPhotos()
{
    return photos;
}

public IHttpActionResult GetPhoto(int id)
{
    var photo = photos.FirstOrDefault((p) => p.Id == id);
    if (photo == null)
    {
        return NotFound();
    }
    return Ok(photo);
}

public IHttpActionResult GetPhoto(string Name)
{
    var photo = photos.FirstOrDefault((p) => p.Name== Name);
    if (photo == null)
    {
        return NotFound();
    }
}

//I added this to see if this helps with the routing. It didn't
public IHttpActionResult GetName(string Name)
{
    var photo = photos.FirstOrDefault((p) => p.Name== Name);
    if (photo == null)
    {
        return NotFound();
    }
    return Ok(photo);

}

This compiles and I can issue a web request like this:

http://localhost:59628/api/photos

And I'll get back an array of photo elements.

I can issue a web request like this:

http://localhost:59628/api/photos/2

And I'll get back element where ID =2

But

http://localhost:59628/api/photos/"Mary B" 

or

Name="Mary B" 

or

/GET/Name/Mary B 

or

/api/photos/name/Mary B 

it returns an error.

SO my first question is why? How can I query the service for photos by name?

Second question: I see the information in the route config but even when I add this to the PhotosController:

[Route("photos/{photo}/Name")]
[HttpGet]
public IHttpActionResult GetName(string Name)
{
    var photo = photos.FirstOrDefault((p) => p.Name== Name);
    if (photo == null)
    {
        return NotFound();
    }
    return Ok(photo);
}

I get back a 404. WHY?


Solution

  • Routes are configured so that the api knows how to handle requests for resources.

    404 Not Found is returned when the api cannot find the resource that was requested based on configuration.

    If planning to use attribute routing, make sure to configure it in the WebApiConfig.cs file like

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

    For attribute routing the PhotosController would then need to be updated

    [RoutePrefix("api/photos")]
    public class PhotosController : ApiController {
    
        Photo[] photos = new Photo[] 
        { 
            new Photo { Id= 1, Name= "Mary B",  DateTimeOriginal = Convert.ToDateTime("May 18, 2015 2:01:22 PM")}, 
            new Photo { Id = 2, Name= "Martin M",  DateTimeOriginal = Convert.ToDateTime("May 18, 2015 2:13:09 PM" )}, 
            new Photo { Id = 3, Name= "Sparky",  DateTimeOriginal = Convert.ToDateTime("May 18, 2015 2:31:22 PM")} 
        };
    
        //GET api/photos
        [HttpGet]
        [Route("")]
        public IEnumerable<Photo> GetAllPhotos() {
            return photos;
        }
    
        //GET api/photos/5
        [HttpGet]
        [Route("{id:int}")]
        public IHttpActionResult GetPhoto(int id) {
            var photo = photos.FirstOrDefault((p) => p.Id == id);
            if (photo == null) {
                return NotFound();
            }
            return Ok(photo);
        }
    
        //GET api/photos/name/Mary B
        [HttpGet]
        [Route("name/{name}")]
        public IHttpActionResult GetPhotoByName(string name) {
            var photo = photos.FirstOrDefault((p) => p.Name == name);
            if (photo == null) {
                return NotFound();
            }
            return Ok(photo);
        }
    }