REST API Development with .NET

This tutorial guides you through the process of building robust and scalable RESTful APIs using Microsoft's .NET platform. We'll cover fundamental concepts, best practices, and practical implementation details.

Introduction to REST APIs

Representational State Transfer (REST) is an architectural style for designing networked applications. It relies on a stateless, client-server communication protocol, typically HTTP. Key principles include:

  • Client-Server Architecture: Separation of concerns between client and server.
  • Statelessness: Each request from client to server must contain all the information needed to understand and complete the request.
  • Cacheability: Responses must implicitly or explicitly define themselves as cacheable or non-cacheable.
  • Uniform Interface: Constraints that simplify and decouple the architecture, enabling each part to evolve independently.
  • Layered System: Intermediaries can be used without affecting the client and server.
  • Code on Demand (Optional): Servers can temporarily extend or customize the functionality of a client.

Building REST APIs with .NET allows you to leverage a powerful, open-source, and cross-platform framework that supports building modern, cloud-based, and internet-connected applications.

Getting Started with ASP.NET Core Web API

ASP.NET Core provides a comprehensive framework for building Web APIs. Let's get started:

  1. Set up your development environment

    Ensure you have the .NET SDK installed. You can download it from the official .NET website.

    You'll also need a code editor like Visual Studio, Visual Studio Code, or JetBrains Rider.

  2. Create a new ASP.NET Core Web API project

    Open your terminal or command prompt and run the following command:

    dotnet new webapi -o MyFirstApi

    This command creates a new project named MyFirstApi in a directory of the same name.

  3. Explore the project structure

    Navigate into the project directory:

    cd MyFirstApi

    You'll find files like Program.cs (the application's entry point), Controllers/WeatherForecastController.cs (a sample API controller), and appsettings.json (configuration).

  4. Run the application

    Start the API server:

    dotnet run

    Your API will be running, typically at https://localhost:7001 or http://localhost:5001. The output in your terminal will show the URLs.

  5. Test the API

    Open your browser and navigate to https://localhost:7001/weatherforecast. You should see a JSON response with sample weather data.

    HTTPS Note:

    Your browser might show a security warning for HTTPS. This is because the development certificate is self-signed. You can accept the risk for local development.

Designing Your API Endpoints

A well-designed API is crucial for usability and maintainability. Consider the following:

Common HTTP Methods

  • GET: Retrieve a resource or a collection of resources.
  • POST: Create a new resource.
  • PUT: Update an existing resource (replaces the entire resource).
  • PATCH: Partially update an existing resource.
  • DELETE: Remove a resource.

Resource Naming Convention

Use nouns for resource names (e.g., /products, /users) and use HTTP methods to indicate the action.

Example API Endpoints for a To-Do List Application:

GET /api/todos - Retrieves a list of all to-do items.
GET /api/todos/{id} - Retrieves a specific to-do item by its ID.
POST /api/todos - Creates a new to-do item. The item's data is sent in the request body.
PUT /api/todos/{id} - Updates an existing to-do item. The updated data is sent in the request body.
DELETE /api/todos/{id} - Deletes a specific to-do item by its ID.

Implementing Controllers and Actions

Controllers in ASP.NET Core Web API handle incoming requests and return responses. Each controller typically represents a resource.

Creating a Custom Controller

Let's create a simple ProductsController:


// Controllers/ProductsController.cs
using Microsoft.AspNetCore.Mvc;
using MyFirstApi.Models; // Assuming you have a Product model

namespace MyFirstApi.Controllers
{
    [ApiController]
    [Route("api/[controller]")] // Routes requests to /api/products
    public class ProductsController : ControllerBase
    {
        private static List<Product> _products = new List<Product>
        {
            new Product { Id = 1, Name = "Laptop", Price = 1200.00M },
            new Product { Id = 2, Name = "Mouse", Price = 25.50M }
        };

        [HttpGet]
        public ActionResult<IEnumerable<Product>> GetProducts()
        {
            return Ok(_products); // Returns HTTP 200 OK with the list of products
        }

        [HttpGet("{id}")]
        public ActionResult<Product> GetProduct(int id)
        {
            var product = _products.FirstOrDefault(p => p.Id == id);
            if (product == null)
            {
                return NotFound(); // Returns HTTP 404 Not Found
            }
            return Ok(product); // Returns HTTP 200 OK with the product
        }

        [HttpPost]
        public ActionResult<Product> PostProduct(Product product)
        {
            product.Id = _products.Count + 1; // Simple ID assignment
            _products.Add(product);
            return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product); // Returns HTTP 201 Created
        }
    }
}
                

Product Model Example:


// Models/Product.cs
namespace MyFirstApi.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}
                

Data Handling and Validation

Properly handling data and implementing validation is crucial for API reliability.

Using Data Transfer Objects (DTOs)

DTOs are plain objects that are used to transfer data between processes. They are often used to shape the data sent to and received from the API.

Model Validation

ASP.NET Core includes built-in support for data annotations to validate incoming model data. The [ApiController] attribute automatically performs model validation.


using System.ComponentModel.DataAnnotations;

public class CreateProductDto
{
    [Required]
    [StringLength(100, MinimumLength = 1)]
    public string Name { get; set; }

    [Required]
    [Range(0.01, 10000.00)]
    public decimal Price { get; set; }
}
                

You would then use this DTO in your POST or PUT actions:


[HttpPost]
public ActionResult<Product> PostProduct([FromBody] CreateProductDto productDto)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState); // Returns validation errors
    }
    // ... map DTO to Product model and save ...
    return Ok(/* created product */);
}
                

Error Handling and Status Codes

Returning appropriate HTTP status codes and informative error messages helps clients understand what happened.

  • 200 OK: The request was successful.
  • 201 Created: A new resource was successfully created.
  • 204 No Content: The request was successful, but there's no content to return (e.g., for a DELETE operation).
  • 400 Bad Request: The request was malformed or invalid.
  • 401 Unauthorized: Authentication is required and has failed or not been provided.
  • 403 Forbidden: The server understood the request, but refuses to authorize it.
  • 404 Not Found: The requested resource could not be found.
  • 500 Internal Server Error: An unexpected error occurred on the server.

Leverage ControllerBase helper methods like Ok(), NotFound(), BadRequest(), and StatusCode().

Further Learning Resources