REST API Implementations in .NET and Java

Below are production-ready code snippets illustrating how to build a clean REST API (featuring a resource controller, pagination parameters, proper status codes, and DTO validation) in both .NET (C#) and Java (Spring Boot).

1. .NET implementation (.NET 8 / C# ASP.NET Core)

using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
 
namespace RestApiDemo.Controllers
{
    // DTO for incoming user creation requests
    public record CreateUserDto(
        [Required] string Name,
        [EmailAddress][Required] string Email
    );
 
    // DTO for user resource representation
    public record UserDto(Guid Id, string Name, string Email, DateTime CreatedAt);
 
    // Response wrapper for paginated collections
    public record PagedResult<T>(
        IEnumerable<T> Items,
        int Limit,
        int Offset,
        int TotalCount
    );
 
    [ApiController]
    [Route("api/v1/users")]
    public class UsersController : ControllerBase
    {
        // Simulated in-memory database
        private static readonly List<UserDto> _users = new();
 
        /// <summary>
        /// GET: api/v1/users?limit=10&offset=0
        /// Demonstrates query-parameter pagination returning a structured paged payload
        /// </summary>
        [HttpGet]
        public ActionResult<PagedResult<UserDto>> GetUsers(
            [FromQuery, Range(1, 100)] int limit = 10, 
            [FromQuery, Range(0, int.MaxValue)] int offset = 0)
        {
            var totalCount = _users.Count;
            
            // Apply pagination logic using LINQ Skip and Take
            var pagedItems = _users
                .Skip(offset)
                .Take(limit)
                .ToList();
 
            var result = new PagedResult<UserDto>(
                Items: pagedItems,
                Limit: limit,
                Offset: offset,
                TotalCount: totalCount
            );
 
            return Ok(result);
        }
 
        /// <summary>
        /// GET: api/v1/users/{id}
        /// Demonstrates resource retrieval and 404 Not Found handling
        /// </summary>
        [HttpGet("{id:guid}")]
        public ActionResult<UserDto> GetUserById(Guid id)
        {
            var user = _users.FirstOrDefault(u => u.Id == id);
            if (user == null)
            {
                return NotFound(new { message = $"User with ID {id} not found." });
            }
            return Ok(user);
        }
 
        /// <summary>
        /// POST: api/v1/users
        /// Demonstrates resource creation, validation, 201 Created status, 
        /// and returning a Location header.
        /// </summary>
        [HttpPost]
        public ActionResult<UserDto> CreateUser([FromBody] CreateUserDto createDto)
        {
            // Check for unique email constraint (simulating 409 Conflict)
            if (_users.Any(u => u.Email.Equals(createDto.Email, StringComparison.OrdinalIgnoreCase)))
            {
                return Conflict(new { message = "A user with this email already exists." });
            }
 
            var newUser = new UserDto(Guid.NewGuid(), createDto.Name, createDto.Email, DateTime.UtcNow);
            _users.Add(newUser);
 
            // Returns 201 Created with Location header pointing to GET /api/v1/users/{id}
            return CreatedAtAction(nameof(GetUserById), new { id = newUser.Id }, newUser);
        }
 
        /// <summary>
        /// DELETE: api/v1/users/{id}
        /// Demonstrates 204 No Content success response
        /// </summary>
        [HttpDelete("{id:guid}")]
        public IActionResult DeleteUser(Guid id)
        {
            var user = _users.FirstOrDefault(u => u.Id == id);
            if (user == null)
            {
                return NotFound();
            }
 
            _users.Remove(user);
            return NoContent(); // Standard 204 response for successful deletion
        }
    }
}

2. Java Implementation (Spring Boot 3 / Java)

package com.example.restapi.controller;
 
import jakarta.validation.Valid;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
 
import java.net.URI;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
 
// Records introduced in Java 14+ / fully supported in Spring Boot 3 for DTOs
record CreateUserDto(
    @NotBlank String name,
    @Email @NotBlank String email
) {}
 
record UserDto(
    UUID id,
    String name,
    String email,
    Instant createdAt
) {}
 
record PagedResult<T>(
    List<T> items,
    int limit,
    int offset,
    int totalCount
) {}
 
@RestController
@RequestMapping("/api/v1/users")
public class UsersController {
 
    // Simulated in-memory database
    private static final List<UserDto> users = new ArrayList<>();
 
    /**
     * GET: /api/v1/users?limit=10&offset=0
     * Demonstrates pagination query params and 200 OK response.
     */
    @GetMapping
    public ResponseEntity<PagedResult<UserDto>> getUsers(
            @RequestParam(defaultValue = "10") int limit,
            @RequestParam(defaultValue = "0") int offset) {
        
        int totalCount = users.size();
        
        List<UserDto> pagedItems = users.stream()
                .skip(offset)
                .limit(limit)
                .collect(Collectors.toList());
                
        PagedResult<UserDto> result = new PagedResult<>(pagedItems, limit, offset, totalCount);
        
        return ResponseEntity.ok(result);
    }
 
    /**
     * GET: /api/v1/users/{id}
     * Demonstrates path variables and 404 Not Found handling.
     */
    @GetMapping("/{id}")
    public ResponseEntity<UserDto> getUserById(@PathVariable UUID id) {
        Optional<UserDto> user = users.stream()
                .filter(u -> u.id().equals(id))
                .findFirst();
 
        return user.map(ResponseEntity::ok)
                .orElseGet(() -> ResponseEntity.notFound().build());
    }
 
    /**
     * POST: /api/v1/users
     * Demonstrates request validation (@Valid), 201 Created status, 
     * and Location header generation.
     */
    @PostMapping
    public ResponseEntity<?> createUser(@Valid @RequestBody CreateUserDto createDto) {
        // Check for duplicate unique constraint (simulating 409 Conflict)
        boolean emailExists = users.stream()
                .anyMatch(u -> u.email().equalsIgnoreCase(createDto.email()));
        
        if (emailExists) {
            return ResponseEntity.status(409).body("A user with this email already exists.");
        }
 
        UserDto newUser = new UserDto(
                UUID.randomUUID(),
                createDto.name(),
                createDto.email(),
                Instant.now()
        );
        users.add(newUser);
 
        // Build location URI for the newly created resource (Header: Location)
        URI location = ServletUriComponentsBuilder.fromCurrentRequest()
                .path("/{id}")
                .buildAndExpand(newUser.id())
                .toUri();
 
        return ResponseEntity.created(location).body(newUser);
    }
 
    /**
     * DELETE: /api/v1/users/{id}
     * Demonstrates 204 No Content success response on deletion.
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable UUID id) {
        boolean removed = users.removeIf(u -> u.id().equals(id));
        
        if (!removed) {
            return ResponseEntity.notFound().build();
        }
        
        return ResponseEntity.noContent().build(); // 204 No Content
    }
}