In this post, we will see two tips that could help us in order to create Web API.
First of all, we create a simple Web API for managing a list of Users:
[USER.CS]
using System;
namespace ServiceUsers.Model
{
public class User
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public UserType Type { get; set; }
public DateTime CreatedAT { get; set; }
}
}
[USERTYPE.CS]
namespace ServiceUsers.Model
{
public enum UserType
{
Admin,
Contributor,
Read
}
}
[IUSERCORE.CS]
using ServiceUsers.Model;
using System.Collections.Generic;
namespace ServiceUsers.Core
{
public interface IUserCore
{
List<User> GetAllUsers();
User GetUserById(int id);
List<User> AddNewUser(User objUser);
List<User> DeleteUser(int id);
}
}
[USERCORE.CS]
using ServiceUsers.Model;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ServiceUsers.Core
{
public class UserCore : IUserCore
{
private List<User> lstUsers = new List<User> {
new User{ Id = 1, Email="email1@email.com", Password="password1", Type= UserType.Admin, CreatedAT=DateTime.Now.AddDays(-10)},
new User{ Id = 2, Email="email2@email.com", Password="password2", Type= UserType.Contributor, CreatedAT=DateTime.Now.AddDays(-7)},
new User{ Id = 3, Email="email3@email.com", Password="password3", Type= UserType.Contributor, CreatedAT=DateTime.Now.AddDays(-5)},
new User{ Id = 4, Email="email4@email.com", Password="password4", Type= UserType.Read, CreatedAT=DateTime.Now.AddDays(-5)},
new User{ Id = 5, Email="email5@email.com", Password="password5", Type= UserType.Read, CreatedAT=DateTime.Now.AddDays(-2)},
new User{ Id = 6, Email="email6@email.com", Password="password6", Type= UserType.Read, CreatedAT=DateTime.Now.AddDays(-1)},
};
public List<User> AddNewUser(User objUser)
{
lstUsers.Add(objUser);
return lstUsers;
}
public List<User> GetAllUsers()
{
return lstUsers;
}
public User GetUserById(int id)
{
return lstUsers.FirstOrDefault(x => x.Id == id);
}
public List<User> DeleteUser(int id)
{
var objUser = lstUsers.First(x => x.Id == id);
lstUsers.Remove(objUser);
return lstUsers;
}
}
}
[USERCONTROLLER.CS]
using Microsoft.AspNetCore.Mvc;
using ServiceUsers.Core;
using ServiceUsers.Model;
using System.Collections.Generic;
namespace ServiceUsers.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
IUserCore _userCore;
public UsersController(IUserCore userCore)
{
_userCore = userCore;
}
[HttpGet]
public ActionResult<List<User>> Get()
{
return Ok(_userCore.GetAllUsers());
}
[HttpGet("{id}")]
public ActionResult<User> Get(int id)
{
return Ok(_userCore.GetUserById(id));
}
[HttpPost]
public ActionResult<List<User>> Post(User objUser)
{
return Ok(_userCore.AddNewUser(objUser));
}
[HttpDelete("{id}")]
public ActionResult<User> Delete(int id)
{
return Ok(_userCore.DeleteUser(id));
}
}
}
[STARTUP.CS]
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ServiceUsers.Core;
namespace ServiceUsers
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped<IUserCore, UserCore>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
We have done and now with Postman, we will verify it works fine:
TIP #1
We can see that in our Web API we have many methods with different outputs.
In order to manage our service better, we could define a class called CoreResponse, used as output for every methods:
[CORERESPONSE.CS]
namespace ServiceUsers.Model
{
public class CoreResponse<T>
{
public T Data { get; set; }
public bool Success { get; set; } = true;
public string Info { get; set; } = null;
}
}
[IUSERCORE.CS]
using ServiceUsers.Model;
using System.Collections.Generic;
namespace ServiceUsers.Core
{
public interface IUserCore
{
CoreResponse<List<User>> GetAllUsers();
CoreResponse<User> GetUserById(int id);
CoreResponse<List<User>> AddNewUser(User objUser);
CoreResponse<List<User>> DeleteUser(int id);
}
}
[USERCORE.CS]
using ServiceUsers.Model;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ServiceUsers.Core
{
public class UserCore : IUserCore
{
private List<User> lstUsers = new List<User> {
new User{ Id = 1, Email="email1@email.com", Password="password1", Type= UserType.Admin, CreatedAT=DateTime.Now.AddDays(-10)},
new User{ Id = 2, Email="email2@email.com", Password="password2", Type= UserType.Contributor, CreatedAT=DateTime.Now.AddDays(-7)},
new User{ Id = 3, Email="email3@email.com", Password="password3", Type= UserType.Contributor, CreatedAT=DateTime.Now.AddDays(-5)},
new User{ Id = 4, Email="email4@email.com", Password="password4", Type= UserType.Read, CreatedAT=DateTime.Now.AddDays(-5)},
new User{ Id = 5, Email="email5@email.com", Password="password5", Type= UserType.Read, CreatedAT=DateTime.Now.AddDays(-2)},
new User{ Id = 6, Email="email6@email.com", Password="password6", Type= UserType.Read, CreatedAT=DateTime.Now.AddDays(-1)},
};
public CoreResponse<List<User>> AddNewUser(User objUser)
{
CoreResponse<List<User>> result = new CoreResponse<List<User>>();
lstUsers.Add(objUser);
result.Data = lstUsers;
return result;
}
public CoreResponse<List<User>> GetAllUsers()
{
CoreResponse<List<User>> result = new CoreResponse<List<User>>();
result.Data = lstUsers;
return result;
}
public CoreResponse<User> GetUserById(int id)
{
CoreResponse<User> result = new CoreResponse<User>();
try
{
var objUser = lstUsers.First(x => x.Id == id);
result.Data = objUser;
}
catch (Exception ex)
{
result.Success = false;
result.Info = ex.Message;
}
return result;
}
public CoreResponse<List<User>> DeleteUser(int id)
{
CoreResponse<List<User>> result = new CoreResponse<List<User>>();
try
{
var objUser = lstUsers.First(x => x.Id == id);
lstUsers.Remove(objUser);
result.Data = lstUsers;
}
catch (Exception ex)
{
result.Success = false;
result.Info = ex.Message;
}
return result;
}
}
}
[USERCONTROLLER.CS]
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using ServiceUsers.Core;
using ServiceUsers.Model;
namespace ServiceUsers.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
IUserCore _userCore;
public UsersController(IUserCore userCore)
{
_userCore = userCore;
}
[HttpGet]
public IActionResult Get()
{
var result = _userCore.GetAllUsers();
if(result.Success)
{
return Ok(result);
}
else
{
return StatusCode(StatusCodes.Status500InternalServerError, result);
}
}
[HttpGet("{id}")]
public IActionResult Get(int id)
{
var result = _userCore.GetUserById(id);
if (result.Success)
{
return Ok(result);
}
else
{
return StatusCode(StatusCodes.Status500InternalServerError, result);
}
}
[HttpPost]
public IActionResult Post(User objUser)
{
var result = _userCore.AddNewUser(objUser);
if (result.Success)
{
return Ok(result);
}
else
{
return StatusCode(StatusCodes.Status500InternalServerError, result);
}
}
[HttpDelete("{id}")]
public IActionResult Delete(int id)
{
var result = _userCore.DeleteUser(id);
if (result.Success)
{
return Ok(result);
}
else
{
return StatusCode(StatusCodes.Status500InternalServerError, result);
}
}
}
}
We have done and now with Postman, we will verify it works fine:
TIP#2
It is a best practice to use a DTO (Data Transfer Object) as output of a service, in order to hide particular properties that clients are not supposed to view, reduce payload size and for remove circular references.
For this reason, we will install the library AutoMapper (AutoMapper.Extensions.Microsoft.DependencyInjection) and then, we will create an UserDTO class:
[USERDTO.CS]
namespace ServiceUsers.Model
{
public class UserDTO
{
public int Id { get; set; }
public string Email { get; set; }
public UserType Type { get; set; }
}
}
Now, we will modify the code in order to use the new Class:
[IUSERCORE.CS]
using ServiceUsers.Model;
using System.Collections.Generic;
namespace ServiceUsers.Core
{
public interface IUserCore
{
CoreResponse<List<UserDTO>> GetAllUsers();
CoreResponse<UserDTO> GetUserById(int id);
CoreResponse<List<UserDTO>> AddNewUser(User objUser);
CoreResponse<List<UserDTO>> DeleteUser(int id);
}
}
[USERCORE.CS]
using AutoMapper;
using ServiceUsers.Model;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ServiceUsers.Core
{
public class UserCore : IUserCore
{
private List<User> lstUsers = new List<User> {
new User{ Id = 1, Email="email1@email.com", Password="password1", Type= UserType.Admin, CreatedAT=DateTime.Now.AddDays(-10)},
new User{ Id = 2, Email="email2@email.com", Password="password2", Type= UserType.Contributor, CreatedAT=DateTime.Now.AddDays(-7)},
new User{ Id = 3, Email="email3@email.com", Password="password3", Type= UserType.Contributor, CreatedAT=DateTime.Now.AddDays(-5)},
new User{ Id = 4, Email="email4@email.com", Password="password4", Type= UserType.Read, CreatedAT=DateTime.Now.AddDays(-5)},
new User{ Id = 5, Email="email5@email.com", Password="password5", Type= UserType.Read, CreatedAT=DateTime.Now.AddDays(-2)},
new User{ Id = 6, Email="email6@email.com", Password="password6", Type= UserType.Read, CreatedAT=DateTime.Now.AddDays(-1)},
};
private IMapper _iMapper;
public UserCore(IMapper iMapper)
{
_iMapper = iMapper;
}
public CoreResponse<List<UserDTO>> AddNewUser(User objUser)
{
CoreResponse<List<UserDTO>> result = new CoreResponse<List<UserDTO>>();
lstUsers.Add(objUser);
result.Data = _iMapper.Map<List<UserDTO>>(lstUsers);
return result;
}
public CoreResponse<List<UserDTO>> GetAllUsers()
{
CoreResponse<List<UserDTO>> result = new CoreResponse<List<UserDTO>>();
result.Data = _iMapper.Map<List<UserDTO>>(lstUsers);
return result;
}
public CoreResponse<UserDTO> GetUserById(int id)
{
CoreResponse<UserDTO> result = new CoreResponse<UserDTO>();
try
{
var objUser = lstUsers.First(x => x.Id == id);
result.Data = _iMapper.Map<UserDTO>(objUser);
}
catch (Exception ex)
{
result.Success = false;
result.Info = ex.Message;
}
return result;
}
public CoreResponse<List<UserDTO>> DeleteUser(int id)
{
CoreResponse<List<UserDTO>> result = new CoreResponse<List<UserDTO>>();
try
{
var objUser = lstUsers.First(x => x.Id == id);
lstUsers.Remove(objUser);
result.Data = _iMapper.Map<List<UserDTO>>(lstUsers);
}
catch (Exception ex)
{
result.Success = false;
result.Info = ex.Message;
}
return result;
}
}
}
Then, we have to change the Startup.cs for using AutoMapper:
[STARTUP.CS]
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ServiceUsers.Core;
namespace ServiceUsers
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped<IUserCore, UserCore>();
services.AddAutoMapper(typeof(Startup));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Finally, we have to create a file for defining the Map from User to UserDTO:
[AUTOMAPPERPROFILE.CS]
using AutoMapper;
using ServiceUsers.Model;
namespace ServiceUsers
{
public class AutoMapperProfile: Profile
{
public AutoMapperProfile()
{
CreateMap<User, UserDTO>();
}
}
}
We have done and now with Postman, we can verify it works fine it: