-
Notifications
You must be signed in to change notification settings - Fork 0
Creating a RESTful API
Previous: Setting Up xUnit
In this section, we'll build a RESTful endpoint for our project. When the new project was created, an auto-generated ValuesController
class was created. This class has an attribute (similar to annotations in Java) of [Route]
with a parameter of api/[controller]
. This routes requests for /api/values
through this class. In .NET Core 2.2, controllers are also annotated with [ApiController]
, allowing them access to more functionality.
There are also methods marked with [HttpGet]
, [HttpPost]
, [HttpPut]
, and [HttpDelete]
indicating the basic CRUD operations for the API. Try running the app from the IDE and opening http://localhost:5000/api/values
to verify it returns ["value1", "value2"]
.
Note: Visual Studio may prompt you to trust a self-signed certificate for running the app.
Let's first rename this class to NotesController
as we are making a notes app. For the sake of simplicity, we're going to remove all the auto-generated endpoints other than Get()
for now (feel free to remove the comments). We're going to need to have a Note
model to return, so let's make that class now.
In the NotesApp
project, create a Models
folder and add a Note
class, as follows:
public class Note
{
public long Id { get; set; }
public string Body { get; set; }
}
We've got two fields, an Id
for a unique id, and Body
for the contents.
Let's go ahead and start making our first meaningful test.
In the NotesApp.Tests
project, add a Controllers
directory to mirror the NotesApp
project. In the Controllers
directory, add our test class NotesControllerTests
.
Let's add a test to get a list of notes. We'll, for the time being, have our API return one note by default.
[Fact]
public void Get_ShouldReturnNoteList()
{
var expected = new List<Note>
{
new Note { Id = 1, Body = "Note 1" }
};
// ...
}
At this point, you'll probably encounter some errors from the IDE, complaining about missing references. Fact
and List
should be easily resolvable, but Note
is a little bit trickier to get working.
Rider:
To resolve this, follow the suggestion provided on the ALT+ENTER menu, and add a reference to the main project.
Visual Studio:
To resolve this, right-click on the Dependencies section and select Add Reference. From there, select the NotesApp project and hit OK.
CLI:
To resolve this, run the following:
dotnet add "NotesApp.Tests/NotesApp.Tests.csproj" reference "NotesApp/NotesApp.csproj"
There's a bit of a quirk that occurs when our NotesApp.Tests
project references our NotesApp
project. In order for the test project to understand which version of Microsoft.AspNetCore.App
the main project is dependent on, you will need to manually set the version in NotesApp.csproj
. For the purposes of this guide, we will be using version 2.2.2
.
Now that we have some of the dependencies sorted out, let's continue writing the test.
[Fact]
public void Get_ShouldReturnNoteList()
{
// ...
var controller = new NotesController();
// Make the request
var response = controller.Get();
// Ensure that we get back a result
Assert.NotNull(response);
Assert.NotNull(response.Result);
// Ensure that we received an Ok
Assert.IsType<OkObjectResult>(response.Result);
var value = ((OkObjectResult) response.Result).Value;
// Ensure the the value of the ActionResult is an enumerable of type Note
Assert.NotNull(value);
Assert.IsAssignableFrom<IEnumerable<Note>>(value);
var actual = ((IEnumerable<Note>) value).ToList();
// Ensure that we have the correct values in the list
Assert.NotNull(actual);
Assert.NotEmpty(actual);
Assert.Equal(actual.Count, expected.Count);
Assert.Equal(actual[0].Id, expected[0].Id);
Assert.Equal(actual[0].Body, expected[0].Body);
}
Let's take a look at the test. We've got a [Fact]
attribute, to denote this method as a test, similar to @Test
in jUnit.
We make a call to the controller's endpoint directly (as this is a unit test rather than an integration test). The response is validated against what we expect back, including length and Id
/Body
for the first item in the list. In .NET Core 2.2, the type ActionResult<T>
was introduced to allow for more meaningful return types from methods. A description of them can be found here.
Now, let's get that test to pass. In the NotesController
, update the Get()
method as follows:
[HttpGet]
public ActionResult<IEnumerable<Note>> Get()
{
return Ok(new[] { new Note { Id = 1, Body = "Note 1" } });
}
Ok
automatically gets transformed into an ActionResult
.
Run the test again and it should pass.
If you run the main app (either from the IDE or via dotnet run "NotesApp/NotesApp.csproj"
) and open http://localhost:5000/api/notes
in your browser, you should see
[{"id": 1, "body": "Note 1"}]
Congratulations! You now have a working API.
Git Tag: creating-a-restful-api
Up Next: Introducing Fluent Assertions
References
Comparing xUnit.net to other frameworks
Build web APIs with ASP.NET Core
Controller action return types with ASP.NET Core