Web API – Tips

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]

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]

namespace ServiceUsers.Model
{
    public enum UserType
    {
        Admin,
        Contributor,
        Read
    }
}


[IUserCore]

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]

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]

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]

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 can verify it works fine it:






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]

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]

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]

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;
        }
    }
}


[UsersController]

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 can verify it works fine it:




TIP#2
It is a best practice used 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 create an UserDTO class and then we will install the library AutoMapper (AutoMapper.Extensions.Microsoft.DependencyInjection):


[UserDTO]

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]

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]

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]

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]

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: