In 2012, Microsoft released the first version of the ASP.NET Web API framework, which was designed to create RESTful web services.
This was Windows only, until spring 2016 when .NET Core 1.0 was released (previously known as ASP.NET vNext).
.NET Core can be downloaded from the official website.
For Ubuntu users, there are unofficial installation instructions and a one-click installer.
After .NET Core has been installed, we have a few options to create a new Web API project:
- Use Visual Studio
- Use the Yeoman generator
- or alternatively we can use the following commands to create a new API project:
- dotnet new -t web
- dotnet restore
- dotnet build
- dotnet run
Assuming these commands executed successfully, we can visit http://localhost:5000 to view the page which was created.
Note: Avoid using special/uncoded characters when naming the project folder or in the folder path. There is an issue in Visual Studio Code where the Integrated Terminal failes to start with non-ASCII characters. You can follow up on the issue here
Similar to ASP.NET MVC, a Web API uses a Controller to specify methods which map to URLs. In previous versions of .NET, there was a different base class for Web API controllers called ApiController, but in .NET Core there is just a single base controller for all controllers, i.e. both MVC and Web API controllers.
A controller called ValuesController will by default map to the URL "api/values" (however, see later about [routing](4. Web API Routing.md)). Then, if it has a function with a name which starts with "Get" (example: Get(), GetValues() etc.), then it is assumed that the given function should handle a GET request for "api/values". Similarly, if there are other functions which start with Post, Put or Delete, they will handle the corresponding HTTP verbs to the given URL.
Inside a function in an API controller, we can then use whatever tool we like to read and/or write data, such as LINQ, Entity Framework or some other ORM tool (see later).
We can also specify explicitly that a function will handle GET/POST/PUT/DELETE requests by decorating them with the appropriate attribute:
public class MyController : Controller
{
[HttpGet]
public List<CourseGrade> Grades()
{
...
}
[HttpPost]
public CourseGrade AddGrade(/* Probably some parameters... */)
{
}
[HttpPut]
public void UpdateGrade(/ *Some parameters... */)
{
}
}
and this should be preferred, as it is then completely explicit what verbs will be mapped to what methods.
In ASP.NET MVC, it is possible to create a method which returns JSON data explicitly. However, REST is not tied to any one type of formatting, and a Web Service is by no means required to return JSON data. Fortunately, the Web API framework takes care of this for us. Assume we've got a POCO class:
public class CourseGrade
{
public int CourseID { get; set; }
public float Grade { get; set; }
}
and a corresponding Web API method which returns a list of grades:
public class MyController : Controller
{
public List<CourseGrade> GetGrades()
{
// TODO: load the grades for the currently logged in user, and return them!
}
}
As you will notice, the method is defined to return just a simple list of the CourseGrade class. There is nowhere any mention of either JSON nor XML. What happens behind the scenes is that the Web API framework will examine the "Accept" HTTP header, which will contain the MIME type which the client requests. This will usually contain either "application/xml" or "application/json". The framework will then use a corresponding serializer, based on this.
Note that the Web API framework has built-in support for these two formats, but we can easily add support for other formats as well: CSV, iCal, plain text etc.
Having an API method which returns an object (or a list of objects) is usually the preferred method, since the framework will automatically ensure that the response will use HTTP 200 as a status code, and will automatically serialize the given return value to the response body.
However, in some cases, the default behaviour is not the desired one. For instance, when a POST method is being executed (which should create an instance of a resource), the API method should not return HTTP 200 (Ok), but 201 (Created) instead.
This can be accomplished by specifying the return value as IActionResult, and use the Created() method provided by the Controller base class:
public class CoursesController : Controller
{
[HttpPost]
public IActionResult CreateCourse(/* parameters... */)
{
// TODO: create a course!
return Created( /* Data which will be passed to the response... */ );
}
}
There are several other methods available, such as BadRequest() which will set the response status code to 400, NotFound() which returns a 404, etc.
When using HTTP 201 (Created), the response body will still contain the serialized content of some class. We can state this explicitly by adding another attribute to the function:
public class CoursesController : Controller
{
[HttpPost]
[ResponseType(typeof(Course))]
public IActionResult CreateCourse(/* parameters... */)
{
var course = ... /* Some code which creates the course object and stores it */
var location = ... /* See below */
return Created(location, course);
}
}
Finally, when a method returns 201 (Created), it is customary to add a "Location" header to the response, which indicates how this newly created entity can be accessed later on. Assuming we've got two methods, i.e. one which allows us to access a single course, and another one which allows us to create a course, we can use the following code to ensure the response from the create method has this correctly set:
public class CoursesController : Controller
{
[HttpGet]
[Route("/courses/{id:int}", Name="GetCourse")]
public Course GetCourseByID(int id)
{
// Some code which loads the given course by ID and returns it
}
[HttpPost]
[ResponseType(typeof(Course))]
public IActionResult CreateCourse(/* parameters... */)
{
var course = ... /* Some code which creates the course object and stores it */
var location = Url.Link("GetCourse", new { id = course.ID });
return Created(location, course);
}
}
An ASP.NET Web API will by default allow incoming requests from code originating from the same server as the API. It is however highly likely that other code will need to call our API, for instance if we were to create an App which would connect to our API.
In that case, we should use CORS (Cross-Origin Resource Sharing), which allows code from other domains to access our API. This is implemented by adding the "Access-Control-Allow-Origin" HTTP header to a response, with either a list of domains allowed to call the service, or a wildcard star in case anyone can use it. Clients will then check this by issuing a HTTP OPTIONS request before an actual request is issued, and if it allows them to make the request, they will.
In .NET Core, there is a built in support for CORS, as explained here: https://docs.asp.net/en/latest/security/cors.html.