Web API – Tips

By | 17/03/2021

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:



Leave a Reply

Your email address will not be published. Required fields are marked *