Skip to content

Latest commit

 

History

History
129 lines (100 loc) · 3.86 KB

5. Web API Routing.md

File metadata and controls

129 lines (100 loc) · 3.86 KB

Routing

ASP.NET MVC used to define routing in a separate file, i.e. routing is defined in a single file. A lot of MVC routing is convention based, i.e. it is assumed that URLs correspond to ControllerName/ActionName.

This is still possible in Web API. However, in the latest version of Web API, Attribute Routing has been added. This means we can define routing "inplace", i.e. we can specify what routing rules should apply for individual controllers and/or controller actions. Example:

[RoutePrefix("api/v1/my")]
public class MyController : ApiController
{
    [Route("grades")]
    public List<Grade> Grades()
    {
        ....
    }
    
    [Route("schedule")]
    public List<Event> Schedule()
    {
        ....
    }
}

In this case, there are two resources:

  • /api/v1/my/grades
  • /api/v1/my/schedule

The RoutePrefix attribute takes care of defining a common prefix for the routes, which are then defined explicitly for each action which should be exposed as public APIs.

Note that in this case, the names of the functions match the name in the route, but this is not required.

Routes can define parameters:

    [Route("{courseInstanceID}/grades")]
    public List<Grade> Grades(int courseInstanceID)
    {
        ....
    }

and we can specify that the parameters are of a given type:

    [Route("{courseInstanceID:int}/grades")]
    public List<Grade> Grades(int courseInstanceID)
    {
        ....
    }

Parameters

As seen above, routes can accept parameters, which are enclosed in curly braces in the route definition. The function must then accept a parameter (or parameters) with the same name as used in the route.

The function can accept other parameters, which are then assumed to be passed in as query string values:

    // We can call this method using the URL /api/v1/12345/grades?semester=20133
    [Route("{courseInstanceID:int}/grades")]
    public List<Grade> Grades(int courseInstanceID, string semester)
    {
        ....
    }

Note that the query string parameter is mandatory if declared in this way. It is however possible to declare optional query string parameters by using the C# syntax for default values:

    // We can call this method using the URL /api/v1/12345/grades?semester=20133
    // OR omit the query string parameter: /api/v1/12345/grades
    [Route("{courseInstanceID:int}/grades")]
    public List<Grade> Grades(int courseInstanceID, string semester = null)
    {
        ....
    }

Parameters from the HTTP body

When a request comes in that contains data in the HTTP body (such as POST, PUT and PATCH), we must be able to map those values to parameters as well. There are two cases which we must consider:

  • Primitive values must be marked with the [FromBody] attribute
  • Complex types will map directly to model classes

In the case when a primitive value is passed in as a parameter via the HTTP body, things can get a little tricky on the client, as is explained here. It is therefore a common technique to always pass in complex types, even if they only contain a single property. Example:

    POST /api/v1/courses/12345/students
    some http headers...
    
    {
        "SSN": "1234567890"
    }
    
    // Our ViewModel class:
    public class AddStudentViewModel
    {
        public string SSN { get; set; }
    }
    
    
    // The Controller method:
    [HttpPost]
    [Route("{courseInstanceID:int}/students")]
    public IHttpActionResult AddStudentToCourse(int courseInstanceID, AddStudentViewModel model)
    {
        // model.SSN now contains the value passed in
    }

That way, we can easily add parameters to the request later, without having to add more parameters to the controller function.