Wednesday, July 26, 2023

ASP.NET Core Web API Ecommerce Project and API testing in Postman

Project
  • Open the Visual Studio. 
  • Create web project using ASP.NET Core Web API template.
  • Project name: EShoppingApi
  • Solution name: EShoppingApi
  • .NET Core version: 5.0
  • Delete WeatherForecastController.cs file from Controllers folder.
  • Also delete WeatherForecas.cs file from root folder.
  • Run the application. We get URL like https://localhost:44330/weatherforecast and some 404 error message because weather controller is deleted.
  • The port number will vary system to system. From where this URL is decided? Open the launchsettings file which is inside Properties folder. It contains launchUrl property in Profiles.
Look at the Startup class. It contains AddControllers service which is suitable for API. The MapControllers method helps to map an endpoint to a controller. Attribute Routing is usually followed.
Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace EShoppingApi
{
    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();
        }

        // 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();
            });
        }
    }
}
  • In API application, we need domain classes. We create a separate class library project for this.
  • Create class library project named as EShopping.Domain. Delete the Class1.cs file.
  • Add a new folder in this project and rename it as Models. Add a class file called Product in it.
Product.cs

namespace EShopping.Domain.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public double Price { get; set; }
    }
}

Save all. We reference the class library project in Api project. Add ProductsController in Controllers folder. Note that it should be API controller not MVC controller. We get the following.


using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace EShoppingApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
    }
}

Before going ahead, create a seeder so that product data is available when we use ProductsController. Seeder class provides dummy data of Product model class. For this, we create Services and Interfaces folder in EShoppingApi project. In the Interfaces folder, create ISeeder.cs interface. The purpose of creating ISeeder interface is get dependency inversion so that Seeder will be available in any controller class via dependency injection.

ISeeder.cs


using EShopping.Domain.Models;
using System.Collections.Generic;

namespace EShoppingApi.Interfaces
{
    public interface ISeeder
    {
        List GetAllProducts();
        bool AddProduct(Product product);
        bool RemoveProduct(int pid); // product id
        bool UpdateProduct(int id, Product product);
    }
}
 

Seeder.cs Implement these methods of ISeeder in Seeder class created in Services folder.


using EShopping.Domain.Models;
using EShoppingApi.Interfaces;
using System.Collections.Generic;
using System.Linq;
using System.Security;

namespace EShoppingApi.Services
{
    public class Seeder : ISeeder
    {
        List<Product> products = new List<Product>();
        public bool AddProduct(Product product)
        {
            products.Add(product);
            return true;
        }

        public List<Product> GetAllProducts()
        {

            Product product1 = new Product()
            {
                Id = 1,
                Name = "Sony Earbuds T5.3",
                Price = 1250.4,
                Description = "Good quality"
            };
            Product product2 = new Product()
            {
                Id = 2,
                Name = "Boat Earbuds T5.2",
                Price = 999.4,
                Description = "Average quality"
            };
            products.Add(product1);
            products.Add(product2);
            return products;
        }

        public bool RemoveProduct(int pid)
        {
            var product = products.Where(p => p.Id == pid).FirstOrDefault();
            products.Remove(product);
            return true;
        }

        public bool UpdateProduct(int id, Product updatedProduct)
        {
            var oldprod = products.FirstOrDefault(p => p.Id == id);
            if (oldprod != null)
            {
                products.Remove(oldprod);
                products.Add(updatedProduct);
                return true;
            }
            return false;
        }
    }
}

Register the Seeder service in Startup class as given below.


 public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddSingleton();
        }
Important: Without AddSingleton method, seeding will not work when we add a new product in the collection of products.

Save all and rebuild the solution. Now, update the ProductsController class as given below.


using EShopping.Domain.Models;
using EShoppingApi.Interfaces;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Linq;

namespace EShoppingApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
        private readonly ISeeder seeder;

        public ProductsController(ISeeder seeder)
        {
            this.seeder = seeder;
        }

        [HttpGet]
        public IActionResult GetAllProducts()
        {
            var products = seeder.GetAllProducts();

            return Ok(products);
        }

        [HttpGet]
        [Route("{id}")]
        public IActionResult GetProductById(int id)
        {
            var products = seeder.GetAllProducts();
            var product = products.Where(p => p.Id == id).FirstOrDefault();
            if (product == null)
            {
                return NotFound();
            }
            return Ok(product);
        }

        [HttpPost]
        public IActionResult CreateProduct([FromBody] Product product)
        {
            var success = seeder.AddProduct(product);
            if (success)
            {
                return Created("https://localhost:5001/api/products/{product.Id}", product);
            }
            return BadRequest("Failed to add product.");
        }

        [HttpDelete]
        [Route("{id}")]
        public IActionResult DeleteProduct(int id)
        {
            var success = seeder.RemoveProduct(id);
            if (success)
            {
                return Ok("Deleted product");
            }
            return BadRequest("Failed to delete product.");
        }

        [HttpPut]
        [Route("{id}")]
        public IActionResult UpdateProduct([FromBody] Product product, int id)
        {
            var success = seeder.UpdateProduct(id, product);
            if (success)
            {
                return Ok("Product updated successfully.");
            }
            return BadRequest("Failed to update product.");
        }
    }
}

Now an important point. Before testing in Postman, make some changes in Visual Studio so that API can be tested without running the browser.

We should not run the browser to test APIs. It is slow process. To disable it, we make changes in properties of API app in Visual Studio. Open the application Properties window. Note that we open Properties windows for API project, EShoppingApi, not class library. Go to the Debug tab. Click the URL. Launch Profiles window opens as given below. Select EShoppingApi. Uncheck the Launch Browser and delete weatherforecast in Url. Close the window. Click Save All button. 
Another change required is to run the application in project name profile, not IISExpress, because we use Kestrel not IISExpress during API testing. Look at the image below.

After making changes in the Visual Studio as done above, we should now begin testing. Save all and rebuild the solution. When we run the application, console window appears as given below and Kestrel server handles the request.

You can make change in the launchsettings.json file available in Properties folder. Set the value of "launchBrowser": false in profiles section in the file. It will disable the launching the app in browser.

Testing in Postman

Now open the Postman to test the APIs. I have done testing offline in Scratch Pad of Postman. When we are offline, click the Settings link as given below.

Then click Scratch Pad menu item.
Postman will open itself in Scratch pad. Look at the image below. A collection named ESHOP is created for testing the APIs. We should create a collection before starting the API testing. It helps to store all the tests in the collection. We can even import old tests in the collection.

First step is to create a collection for testing APIs of the project. Next, to create an API in collection, click the New button. Create New window will open as given below.
Click the Request icon. Save Request window appears as given below.
Fill the details and click Save button.

After creating collection and its different requests, now we are ready for testing APIs in Postman. We must first run the API application before testing in Postman. Open the Postman. Click the 'Get All products' request saved in the collection. Select GET from dropdown and fill the request URL as given below in the image and click Send button. We get the response in the body as given below. Note that in the Postman, you can select any other response format so that data will appear in that format in the body. By default, it is JSON.

Tip.

If you get issue in getting response for the API request, click Settings and turn off SSL certificate verification. For each request you may have to disable it. This settings is for Request sent, not for Postman app. Look at the image below.


Look at the Postman right pane carefully. Concise note is given in the below image about the pane.
Although, a working API is demonstrated in the above example, it is not the right approach which is followed in production. We must use DTO in the Web API to handle the requests. The drawbacks of this demo project and importance of DTO will be explained in the next post in PART-II.

No comments:

Post a Comment

Hot Topics