What are Controllers?
Controllers are the first point of contact for an ASP.NET Web API Core 5 application. They process incoming requests and depending upon how the application is structured, it may pass the request to service layer as in our case to perform operations on Model data and return a response as JSON or SOAP but, we are going to work with JSON response in this post. Controllers are stored inside the Controllers folder, they are basically C# classes whose public methods are called Action Methods. These Action Methods handle the HTTP request and prepares the response to be sent to the client.
In the image below we have a Model which represents our data and controllers, which are responsible to process incoming requests and views can be any of the UI's ASP.Net MVC Web App, Angular, React or a mobile application which can consume our Web API.
Controllers are classes which are derived from ControllerBase class in ASP.Net Core 5 and onwards, one of the important points to remember while working with Controller class, is that it must have a “Controller” suffix. For example, if you want to add a controller with the name Company, then the name of the controller should be CompanyController. Similarly, if you want to add a controller for Address, then the name should be AddressController. Controller class must inherit from the ControllerBase class.
The ControllerBase class provides many properties and methods that are useful for handling HTTP requests. For more information click the link. To find more about Controllers click me
Here are some examples of methods that ControllerBase provides.
Method | Return |
---|---|
BadRequest | Returns 400 status code. |
NotFound | Returns 404 status code. |
PhysicalFile | Returns a file. |
NoContent | Returns no content. |
Enough of the theory let us create a controller.
To Add a controller right click the controller folder in solution explorer and select Add->Controller
and you will end up at Add New Scaffold Item Make sure you select API option, default one is MVC, I am going to go with API Controller - Empty option as that is the best way to learn. But I encourage you to explore other templates when you add more Controllers.
Next we are going to name our controller, so our first controller is going to be company as in image below.
We will end up with the basic template of Web API controller
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebApiSeriesCore5.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CompanyController : ControllerBase
{
}
}
How to Use Get Put Post Delete
To start with, I will like to get a list of companies and to do that we can use [HttpGet] in our controller as you can see in the example code below.
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(List<CompanyDto>))]
public async Task<IActionResult> GetAll()
{
return OK();
}
In the above code I have added two things first one is, ProducesResponseType An IActionResult method in a controller can return multiple response types and paths, using [ProducesResponseType] attribute is a good practice. It helps to produce more descriptive response for web API help pages generated by tools like Swagger. It also narrows down known response types and HTTP status codes to be returned by the action. To find more about ProducesResponseType click me
async which is out of scope of this project but I encourage you to go explore and experiment with async code and learn how to use it and how to implement it.
If you have read my previous article How to Web API .Net Core Basics to Advanced Part 4 Service Layer in which we implemented service layer. So lets inject our service object in our controller to interact with our data.
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebApiSeriesCore5.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CompanyController : ControllerBase
{
private readonly ICompanyService _companyService;
public CompanyController(ICompanyService companyService)
{
this._companyService = companyService;
}
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(List<CompanyDto>))]
public async Task<IActionResult> GetAll()
{
var companies = await _companyService.GetCompaniesAsync();
return Ok(companies);
}
}
}
Even though I have provided complete controller in the example below, including if you notice GetByGUID(Guid CompanyGUID) the method is commented and if you add, remove or edit any part of comments on top of methods it will reflect on swagger UI. I would suggest try and add HttpPost, HttPut, HttpPatch and HttpDelete yourself and have fun with them. Try parameter binding with primitive types and using complex types because that is the best way to learn when you are enjoying it.
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WebApiSeriesCore5.Dtos.Company;
using WebApiSeriesCore5.Service.Contract;
namespace WebApiSeriesCore5.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CompanyController : ControllerBase
{
private readonly ICompanyService _companyService;
public CompanyController(ICompanyService companyService)
{
this._companyService = companyService;
}
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(List<CompanyDto>))]
public async Task<IActionResult> GetAll()
{
var companies = await _companyService.GetCompaniesAsync();
return Ok(companies);
}
[HttpGet("{CompanyID:int}", Name = "GetByCompanyID")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CompanyDto))]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesDefaultResponseType]
public async Task<ActionResult<CompanyDto>> GetCompanyID(int CompanyID)
{
if (CompanyID <=0)
{
return BadRequest(CompanyID);
}
var CompanyFound = await _companyService.GetByIdAsync(CompanyID);
if (CompanyFound.Data == null)
{
return NotFound();
}
return Ok(CompanyFound);
}
/// <summary>
/// Get Company by GUID.
/// </summary>
/// <param name="CompanyGUID"></param>
/// <returns></returns>
//GET/companies/123
[HttpGet("{CompanyGUID:Guid}", Name = "GetCompanyByGUID")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CompanyDto))]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)] //Not found
[ProducesDefaultResponseType]
public async Task<ActionResult<CompanyDto>> GetByGUID(Guid CompanyGUID)
{
if (CompanyGUID == Guid.Empty)
{
return BadRequest(CompanyGUID);
}
var company = await _companyService.GetByGUIDAsync(CompanyGUID);
if (company.Data == null)
{
return NotFound();
}
return Ok(company);
}
/// <summary>
/// Create a new company Record.
/// </summary>
/// <param name="createCompanyDto"></param>
/// <returns></returns>
//POST /Companies
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CompanyDto))]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)] //Not found
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<CompanyDto>> CreateCompany([FromBody] CreateCompanyDto createCompanyDto)
{
if (createCompanyDto == null)
{
return BadRequest(ModelState);
}
if (!ModelState.IsValid) { return BadRequest(ModelState); }
var _newCompany = await _companyService.AddCompanyAsync(createCompanyDto);
if (_newCompany.Success == false && _newCompany.Message == "Exist")
{
return Ok(_newCompany);
}
if (_newCompany.Success == false && _newCompany.Message == "RepoError")
{
ModelState.AddModelError("", $"Some thing went wrong in respository layer when adding company {createCompanyDto}");
return StatusCode(500, ModelState);
}
if (_newCompany.Success == false && _newCompany.Message == "Error")
{
ModelState.AddModelError("", $"Some thing went wrong in service layer when adding company {createCompanyDto}");
return StatusCode(500, ModelState);
}
//Return new company created
return CreatedAtRoute("GetCompanyByGUID", new { CompanyGUID = _newCompany.Data.GUID }, _newCompany);
}
/// <summary>
/// Update existing company record.
/// </summary>
/// <param name="CompanyGUID"></param>
/// <param name="updateCompanyDto"></param>
/// <returns></returns>
//PUT/Companies/id
[HttpPatch("{CompanyGUID:Guid}", Name = "UpdateCompany")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)] //Not found
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> UpdateCompany(Guid CompanyGUID, [FromBody] UpdateCompanyDto updateCompanyDto)
{
if (updateCompanyDto == null || updateCompanyDto.GUID != CompanyGUID)
{
return BadRequest(ModelState);
}
var _updateCompany = await _companyService.UpdateCompanyAsync(updateCompanyDto);
if (_updateCompany.Success == false && _updateCompany.Message == "NotFound")
{
return Ok(_updateCompany);
}
if (_updateCompany.Success == false && _updateCompany.Message == "RepoError")
{
ModelState.AddModelError("", $"Some thing went wrong in respository layer when updating company {updateCompanyDto}");
return StatusCode(500, ModelState);
}
if (_updateCompany.Success == false && _updateCompany.Message == "Error")
{
ModelState.AddModelError("", $"Some thing went wrong in service layer when updating company {updateCompanyDto}");
return StatusCode(500, ModelState);
}
return Ok(_updateCompany);
}
/// <summary>
/// Mark a record as deleted .
/// </summary>
/// <param name="CompanyGUID"></param>
/// <returns></returns>
//DELETE /companies/{id}
[HttpDelete("{CompanyGUID:Guid}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)] //Not found
[ProducesResponseType(StatusCodes.Status409Conflict)] //Can not be removed
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> DeleteCompany(Guid CompanyGUID)
{
var _deleteCompany = await _companyService.SoftDeleteCompanyAsync(CompanyGUID);
if (_deleteCompany.Success == false && _deleteCompany.Data == "NotFound")
{
ModelState.AddModelError("", "Company Not found");
return StatusCode(404, ModelState);
}
if (_deleteCompany.Success == false && _deleteCompany.Data == "RepoError")
{
ModelState.AddModelError("", $"Some thing went wrong in Repository when deleting company");
return StatusCode(500, ModelState);
}
if (_deleteCompany.Success == false && _deleteCompany.Data == "Error")
{
ModelState.AddModelError("", $"Some thing went wrong in service layer when deleting company");
return StatusCode(500, ModelState);
}
return NoContent();
}
}
}
Before we run our API lets make sure we have registered our service and repository objects for DI in startup.cs
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks();
services.AddDbContext<DataContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
//Add Automapper
services.AddAutoMapper(typeof(Startup));
//inject Data Access Layer - Repository
services.AddScoped<ICompanyRepository, CompanyRepository>();
//inject Service layer
services.AddScoped<ICompanyService, CompanyService>();
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApiSeriesCore5", Version = "v1" });
});
}
Now lets run our API
Response
Because we don't have any records in our table we get an empty json response.
Get Let us get that record we just added.
>>Part 6 Web API Core Controllers
<<<Part 4 How to Web API Core set up Service Layer
<<<Part 3 How to Web API Core set up Data Access Layer
<<<Part 2 How to Web API Core set up Database Context
<<<Part 1 How to set up Web API core project
Top comments (2)
Excellent Serie of Posts,Hats off!, I am curious about what you'll be bringing with the next post that you mention :) "Adding second controller and basics of "Entity RelationShips" Coming Soon".
Thank you Dariem I am glad your liked the post. Upcoming posts will be API versioning, implementing Authentication with API key or JWT and how to test API endpoints using postman and finally consume API in an MVC Application. That will end basics series and after that will start Advanced API series.