Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing and Testing ASP.NET Web API Controllers

Tech 2

Modifying the Web API Route Configuraton

By default, ASP.NET Web API uses a routing convention that maps HTTP methods (GET, POST, PUT, DELETE) to controller actions without requiring an explicit action name in the URL. This convention-based routing can become problematic when you need multiple operations on the same resource using the same HTTP method.

To resolve this, you can modify the default route template to include an {action} parameter. This allows you to call specific controller methods directly via the URL.

Navigate to the App_Start/WebApiConfig.cs file and update the route registration as follows:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            // Include {action} to enable direct method invocation
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

The {id} parameter corresponds to the idantifier parameter in your API methods. This change prevents conflicts between different controller actions that might have identical parameters.

Creating Controllers and Models

Defining Data Models

First, create model classes within the Models folder to represent your data structures.

// Models/VideoGame.cs
public class VideoGame
{
    public string Title { get; set; }
    public string Developer { get; set; }
    public string Publisher { get; set; }
    public string Genre { get; set; }
    public decimal Cost { get; set; }
}

// Models/InventoryItem.cs
public class InventoryItem
{
    public int ItemId { get; set; }
    public string ItemName { get; set; }
    public string Category { get; set; }
    public decimal UnitPrice { get; set; }
}

Implementing API Controllers

Add new Web API controllers by right-clicking the Controllers folder and selecting Add → Controller. Choose the Web API 2 Controller - Empty tmeplate.

// Controllers/GameLibraryController.cs
public class GameLibraryController : ApiController
{
    VideoGame[] gameCollection = new VideoGame[]
    {
        new VideoGame { Title="CyberQuest", Developer="Nexus Studios", Publisher="A", Genre="RPG", Cost=49.99M},
        new VideoGame { Title="ShadowFall", Developer="Umbra Games", Publisher="B", Genre="Horror", Cost=39.99M},
        new VideoGame { Title="Starlight", Developer="Cosmic Dev", Publisher="C", Genre="Adventure", Cost=29.99M},
        new VideoGame { Title="IronFront", Developer="Tactical Soft", Publisher="D", Genre="Strategy", Cost=59.99M}
    };

    public IEnumerable<VideoGame> GetAllGames()
    {
        return gameCollection;
    }

    public IHttpActionResult FindGame(string title)
    {
        var selectedGame = gameCollection.FirstOrDefault(g => g.Title == title);
        if (selectedGame == null)
        {
            return NotFound();
        }
        return Ok(selectedGame);
    }
}

// Controllers/StockController.cs
public class StockController : ApiController
{
    InventoryItem[] stockItems = new InventoryItem[]
    {
        new InventoryItem { ItemId = 101, ItemName = "Tomato Soup", Category = "Groceries", UnitPrice = 1.50M },
        new InventoryItem { ItemId = 102, ItemName = "Building Blocks", Category = "Toys", UnitPrice = 15.99M },
        new InventoryItem { ItemId = 103, ItemName = "Hammer", Category = "Hardware", UnitPrice = 12.75M }
    };

    public IEnumerable<InventoryItem> GetCompleteInventory()
    {
        return stockItems;
    }

    public IHttpActionResult LocateItem(int id)
    {
        var item = stockItems.FirstOrDefault(i => i.ItemId == id);
        if (item == null)
        {
            return NotFound();
        }
        return Ok(item);
    }
}

// Controllers/UtilityController.cs
public class UtilityController : ApiController
{
    [HttpGet]
    public string CombineParameters(string textValue, int numericValue)
    {
        string output = "";
        output = textValue + numericValue.ToString();
        // Additional processing logic
        return output;
    }
}

Testing the API Endpoints

Run the application and test the endpoints by navigating to URLs like:

  • http://localhost:port/api/GameLibrary/GetAllGames
  • http://localhost:port/api/Stock/LocateItem/102
  • http://localhost:port/api/Utility/CombineParameters?textValue=test&numericValue=5

Configuring JSON Response Format

To ensure the API returns JSON instead of XML, update the WebApiConfig.cs file:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        // Remove XML formatter to default to JSON
        var xmlFormatter = config.Formatters.XmlFormatter.SupportedMediaTypes
            .FirstOrDefault(media => media.MediaType == "application/xml");
        config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(xmlFormatter);
    }
}

Standardizing API Response Structure

Create a helper class to provide consistent response formatting across all endpoints.

// Helpers/ApiResponseFormatter.cs
public class ApiResponseFormatter
{
    private readonly string responseTemplate = "{{\"statusCode\":{0},\"statusMessage\":\"{1}\",\"data\":{2}}}";

    public HttpResponseMessage CreateResponse(ApiStatusCode code, string message, string dataContent)
    {
        string numericPattern = @"^(\-|\+)?\d+(\.\d+)?$";
        string formattedJson;

        if (Regex.IsMatch(dataContent, numericPattern) || 
            dataContent.ToLower() == "true" || 
            dataContent.ToLower() == "false" || 
            dataContent == "[]" || 
            dataContent.Contains('{'))
        {
            formattedJson = string.Format(responseTemplate, (int)code, message, dataContent);
        }
        else
        {
            formattedJson = string.Format(responseTemplate, (int)code, message, "\"" + dataContent + "\"");
        }

        return new HttpResponseMessage 
        { 
            Content = new StringContent(formattedJson, Encoding.UTF8, "application/json") 
        };
    }
}

public enum ApiStatusCode
{
    OperationFailed = 0,
    OperationSuccessful = 10000
}

// Controllers/ValidationController.cs
public class ValidationController : ApiController
{
    private ApiResponseFormatter formatter = new ApiResponseFormatter();

    [HttpGet]
    public HttpResponseMessage VerifyUsername(string userName)
    {
        int userCount = CheckUserExistence(userName);
        
        if (userCount > 0)
        {
            return formatter.CreateResponse(ApiStatusCode.OperationFailed, 
                "Username already registered", 
                "1 " + userName);
        }
        else
        {
            return formatter.CreateResponse(ApiStatusCode.OperationSuccessful, 
                "Username available", 
                "0 " + userName);
        }
    }

    private int CheckUserExistence(string username)
    {
        // Simulated database check
        return username == "existinguser" ? 1 : 0;
    }
}

Consuming the Web API from JavaScript

Create an HTML page that uses jQuery to interact with you're Web API endpoints.

<!DOCTYPE html>
<html>
<head>
    <title>Product Inventory</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
    <div>
        <h2>Complete Inventory</h2>
        <ul id="itemList"></ul>
    </div>
    <div>
        <h2>Search by Item ID</h2>
        <input type="text" id="itemIdInput" size="5" />
        <button onclick="searchItem()">Search</button>
        <p id="itemDetails"></p>
    </div>

    <script>
    const apiBaseUrl = 'api/Stock';

    $(function() {
        $.getJSON(apiBaseUrl + '/GetCompleteInventory')
            .done(function(data) {
                $.each(data, function(index, item) {
                    $('#itemList').append('<li>' + formatItemDisplay(item) + '</li>');
                });
            });
    });

    function formatItemDisplay(item) {
        return item.ItemName + ': $' + item.UnitPrice;
    }

    function searchItem() {
        const itemId = $('#itemIdInput').val();
        $.getJSON(apiBaseUrl + '/LocateItem/' + itemId)
            .done(function(data) {
                $('#itemDetails').text(formatItemDisplay(data));
            })
            .fail(function(jqXHR, textStatus, error) {
                $('#itemDetails').text('Error: ' + error);
            });
    }
    </script>
</body>
</html>

Deployment to IIS

  1. Publish the Application: In Visual Studio, right-click the project and select Publish. Choose the IIS target and configure the publish settings.
  2. IIS Configuration: Open Internet Information Services (IIS) Manager, right-click Sites, and select Add Website. Specify the site name, physical path (where you published the files), and binding information (typically localhost).

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.