.NET Core API — Dive into C#. Build a back end web server for the... | by Richard Peterson | Towards Data Science

If you have been following along from the Series outline, and have a working database, an Angular Tour of Heroes App, and a generic .NET API, we have now come to the meat of the Series, writing the C# code for our .NET API.
To follow along, you can download a complete version of the .NET API + Docker MSSQL DB + Angular app from my GitHub repo.
The process we are going to follow for this article is:
Connection String and Startup.csModelScaffold ControllerController RoutingTesting from PostmanCORSModify the Angular app
Connection String and Startup.cs
Model
Scaffold Controller
Controller Routing
Testing from Postman
CORS
Modify the Angular app
If you have not read the Introduction to this series, I highly recommend it. If you are coming from the Introduction and already have a generic API, great! If you are starting from scratch, simply run from the command line:
dotnet new webapi -n Heroes.API
This will create a sample .NET Core API with fake weather forecast data. I have .NET Core 3.0.2 installed on my machine, so all dependencies will be for 3.0.2.
From the sample API, delete WeatherForecast.cs and WeatherForecastController.cs.
We will need several packages for our .NET Core app to work. You can install these using the NuGet package manager, or from the command line:
dotnet add package Microsoft.EntityFrameworkCore --version 3.0.2dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 3.0.2dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design --version 3.0.0dotnet add package Microsoft.EntityFrameworkCore.Design --version 3.0.2
Create a new folder in the directory called Model, and in the Model Folder, create a new C# class called Hero.cs.
The Model will define the properties of the data in the database and the application, so we simply add values for id, and name.
namespace Heroes.API.Model{    public class HeroValue    {        public int id { get; set; }        public string name { get; set; }    }}
Now, we need to add a database context to manage the connection between our .NET Core data model and the database. We will name our context class HeroesContext and it will inherit from DbContext.HeroesContext will contain a DbSet HeroValues, which corresponds to the name of our table in SQL.
Create a folder called Data and add a new C# file called HeroesContext.cs. Add the below code to your HeroesContext.cs.
using Microsoft.EntityFrameworkCore;using Heroes.API.Model;namespace Heroes.API.Data{    public class HeroesContext : DbContext    {        public HeroesContext (DbContextOptions<HeroesContext> options)            : base(options)        {        }        public DbSet<HeroValue> HeroValue { get; set; }    }}
We also need to add a reference to the DbSet in our Startup.cs file, so that .NET knows to use the HeroesContext as the context for interacting with the database. We will also add the connection string to our database to our appsettings.json file.
Add this to your Startup.cs file under public void ConfigureServices. In our case, we are using the Docker MSSQL database for testing and development.
services.AddDbContext<HeroesContext>(options =>options.UseSqlServer(Configuration.GetConnectionString("DockerDB")));
Add the “DockerDB” connection string to appsettings.json.
"ConnectionStrings": {    "DockerDB": "Server=localhost,1433;Database=heroes;User ID=SA;Password=Password1!"  }
To create the HeroesController, we will use the aspnet-codegenerator tool to scaffold the controller for us.
First, we must install the tool from the command line. Then, from the root directory of your project, run the aspnet-codegenerator.
# Install the tooldotnet tool install --global dotnet-aspnet-codegenerator --version 3.0.0# Run the aspnet-codegeneratordotnet aspnet-codegenerator controller -name HeroesController -async -api -m HeroValue -dc HeroesContext -outDir Controllers
When this is complete, you will see a new file has been created under your Controllers folder called HeroesController. Open this up to see the bare bones controller that has been created for you. This tool is extremely useful for getting the basic structure of your controller into place. Now let’s edit the Controller to match what’s coming from our Angular app.
There is an unintended side effect of using the codegenerator. It adds a value to Startup.cs for the connection string in ConfigureServices:services.AddDbContext of the name of the DbContext, which makes sense, but is not what we want to use for our connection string in this dev environment. Replace the connection string value of “HeroesContext” so that your services.AddDbContext looks like this again:
services.AddDbContext<HeroesContext>(options =>options.UseSqlServer(Configuration.GetConnectionString("DockerDB")));
While we’re at it, in the Configure method of Startup.cs let’s comment out app.UseHttpsRedirection();. It’s not needed for our app.
In our controller, we will have 5 essential methods for interacting with the database and returning values to the Angular app. These 5 methods are called by the route that is provided from Angular to the .NET app. What the .NET app is looking for are HTTP requests with a provided route. These routes are:
GET: api/Heroes — this method will return all the heroes in the databaseGET: api/Heroes/5 — this method will return a specific hero from the database. In this case, it will return the hero with an Id of 5.PUT: api/Heroes/5 — this method will update a specific hero’s information.POST: api/Heroes — this method will post a new hero to the database.DELETE: api/Heroes/5— this method will delete a specific hero from the database.
GET: api/Heroes — this method will return all the heroes in the database
GET: api/Heroes/5 — this method will return a specific hero from the database. In this case, it will return the hero with an Id of 5.
PUT: api/Heroes/5 — this method will update a specific hero’s information.
POST: api/Heroes — this method will post a new hero to the database.
DELETE: api/Heroes/5— this method will delete a specific hero from the database.
Let’s start from the top.
The Angular app will use this route to GET an IEnumerable List object of all the heroes from .NET. This method corresponds to the getHeroes() method in Angular’ hero.service.ts.
Replace the first GET method with the below code, which was greatly informed by the Microsoft docs for adding a search function to a controller. Remember, the Angular app uses a GET: api/Heroes request for several different components. It needs to be able to return a list for the heroes dashboard, the heroes list page, and also search for heroes in the search box on the dashboard.
We accomplish this with an asynchronous ActionResult<IEnumerable> that is fed information from the query string in the GET request. That query can either be no information, which returns all the heroes, or a search string, which returns only the heroes with a name that contains the string.
// GET: api/Heroes[HttpGet]public async Task<ActionResult<IEnumerable<HeroValue>>> GetHeroValue([FromQuery] string name){    // Define a LINQ query    var heroquery = from h in _context.HeroValue select h;        // If the string is not empty, find items 'h' whose name contains the query string    if (!String.IsNullOrEmpty(name))    {        heroquery = heroquery.Where(            h => h.name.Contains(name));    }// Return an asynchronous list of heroes that satisfy query    return await heroquery.OrderBy(num => num.id).ToListAsync();}
Let’s test our GET controller method using Postman. Postman offers a user-friendly way to test your API. First, fire up your Docker database. Run your .NET Core API by running dotnet watch run from the root directory of your API. When the API has launched and is listening on port 5000, in Postman, we will send 2 requests to test for our desired results:
A GET request to http://localhost:5000/api/Heroes. Observe the JSON array returned with a status of 200 OK. This will satisfy the GET request for our dashboard and heroes list.A GETrequest to http://localhost:5000/api/Heroes/?name=wonder. Observe the returned value of "id": 1, "name": "Wonder Woman\r"
A GET request to http://localhost:5000/api/Heroes. Observe the JSON array returned with a status of 200 OK. This will satisfy the GET request for our dashboard and heroes list.
A GETrequest to http://localhost:5000/api/Heroes/?name=wonder. Observe the returned value of "id": 1, "name": "Wonder Woman\r"
Congrats! You have a working API! Let’s build it out to find heroes by Id.
This is where our aspnet-codegenerator really shines. We do not need to change a single thing about this controller method. It is ready to go, code already generated. It simply finds the hero with the matching id passed to it in the route. Your method should be:
[HttpGet("{id}")]public async Task<ActionResult<HeroValue>> GetHeroValue(int id){    var heroValue = await _context.HeroValue.FindAsync(id);    if (heroValue == null)    {        return NotFound();    }    return heroValue;}
Test in Postman:
Run a GET request to http://localhost:5000/api/Heroes/2 and observe a JSON result of “id”: 5, “name”: “Bat Man\r”.
This is also a method that aspnet-codegenerator scaffolds for us perfectly. Simply run a Postman test to try it out:
Run a PUT request to http://localhost:5000/api/Heroes/5. In the request, under Body, change TEXT to JSON and fill out the request body like the below:
{    "id": 5,    "name": "Dr. Strange"}
If successful, you will receive a request status of 204 No Content.
You need to be able to POST new heroes to the database. Also, your API needs to create the Id for the new hero, Angular is only providing the name. Your API uses the current context of the DbSet _context.HeroValueto access values in the database. Use .Max to find the highest Id in the database, enumerate by 1, and assign that new Id to herovalue.id. Then save the new to the database with .Add and .SaveChangesAsync, and return a CreatedAtAction result that produces a status 201 Created response.
[HttpPost]public async Task<ActionResult<HeroValue>> PostHeroValue(HeroValue heroValue){        if (heroValue == null)    {        return BadRequest();    }    // Generate Hero Id    //https://github.com/lohithgn/blazor-tour-of-heroes/blob/master/src/BlazorTourOfHeroes/BlazorTourOfHeroes.Server/Controllers/HeroesController.cs    // Grab the current context of the DbSet HeroValue    // Find the max id    // increase by 1    heroValue.id = _context.HeroValue.Max(h => h.id) + 1;    _context.HeroValue.Add(heroValue);    await _context.SaveChangesAsync();    return CreatedAtAction("GetHeroValue", new { id = heroValue.id }, heroValue);}
Test in Postman:
Send a POST request to http://localhost:5000/api/Heroes/ with a JSON body of:
{ "name": "Poison Ivy"}
Observe the Status: 201 Created. Success!
Finally, you need to be able to delete heroes. This is done when angular sends an HTTP DELETE request to the API with the hero Id to be deleted. Thankfully, our DELETE method was perfectly generated by the aspnet-codegenerator.
[HttpDelete("{id}")]public async Task<ActionResult<HeroValue>> DeleteHeroValue(int id){    var heroValue = await _context.HeroValue.FindAsync(id);    if (heroValue == null)    {        return NotFound();    }    _context.HeroValue.Remove(heroValue);    await _context.SaveChangesAsync();    return heroValue;}
The aspnet-codegenerator really helped us out here. The only methods we really had to think about were the search function in GET: api\Heroes and the Id generator for our POST: api\Heroes. Now that we have a functioning Controller that can be tested from Postman, we will add code to allow Cross-Origin Resource Sharing, better known as CORS.
The Mozilla docs gives us a great definition of CORS.
Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin.
We have an angular app running on one port, and our .NET API running on another port, and .NET does not like another app accessing it’s resources without permission. We need to enable it to receive requests from a specific location, the localhost port of the angular app.
Above your ConfigureServices class, create a readonly string:
readonly string MyAllowSpecificOrigins = “_myAllowSpecificOrigins”;
In your ConfigureServices class, add the AddCors service. Your ConfigureServices class should look like this:
public void ConfigureServices(IServiceCollection services){    services.AddCors(options =>    {        options.AddPolicy(MyAllowSpecificOrigins,        builder =>        {            builder.WithOrigins("http://localhost:4200")                                .AllowAnyHeader()                                .AllowAnyMethod();        });    });    services.AddControllers();    services.AddDbContext<HeroesContext>(options =>            options.UseSqlServer(Configuration.GetConnectionString("DockerDB")));}
We are creating a builder that only allows requests from http://localhost:4200, the port at which our angular app is running. Additionally, from that port, we are allowing any header, and any method to access resources available to our .NET API.
The last thing we need to do for CORS is to add the service we just created to our Configure method in Startup.cs. Add this to your Configure method:
app.UseCors(MyAllowSpecificOrigins);
Your API is finished! Now we just have to point the angular app to your API endpoint.
We are very close to being done! Only a few changes need to be made to the angular app for it to be able to request data from our API.
We need to disable the in-memory web api that you build in the Tour Of Heroes tutorial. Comment out these imports in app.module.ts
//import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';//import { InMemoryDataService }  from './in-memory-data.service';
Then, comment out the imports from under @ngModule() imports
// HttpClientInMemoryWebApiModule.forRoot(InMemoryDataService, { dataEncapsulation: false })
Our public HeroService class contains the code needed for our angular app to interact with the .NET API. We should start by changing the heroesURL to match the endpoint at which our API is listening:
private heroesUrl = 'http://localhost:5000/api/Heroes';
The only other thing I had to change to the HeroService class was to add a const URL for updateHero. The URL that was being passed was not correct, resulting in 405 Method not allowed errors. Add the URL to your updateHero class so that your class looks like this:
updateHero (hero: Hero): Observable<any> {    // Create the route - getting 405 Method not allowed errors    const url = `${this.heroesUrl}/${hero.id}`;    return this.http.put(url, hero, this.httpOptions).pipe(        tap(_ => this.log(`updated hero id=${hero.id}`)),        catchError(this.handleError<any>(`updateHero`))    );}
Now run ng serve --open from the root of your Angular app. Success!
That’s it, you’ve built a .NET API!
You can download a complete version of the .NET API + Docker MSSQL DB + Angular app from my GitHub repo.
Thank you for reading!