WebApi.ServiceResult 1.0.1

dotnet add package WebApi.ServiceResult --version 1.0.1
                    
NuGet\Install-Package WebApi.ServiceResult -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="WebApi.ServiceResult" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="WebApi.ServiceResult" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="WebApi.ServiceResult" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add WebApi.ServiceResult --version 1.0.1
                    
#r "nuget: WebApi.ServiceResult, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package WebApi.ServiceResult@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=WebApi.ServiceResult&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=WebApi.ServiceResult&version=1.0.1
                    
Install as a Cake Tool

WebApi.ServiceResult

A lightweight and flexible library for standardizing API responses between the Service and Controller layers in ASP.NET Core applications using the Result pattern. It enables consistent, type-safe HTTP responses with built-in support for common status codes and customizable response contexts.

Features

  • 🎯 Type-safe generic Result pattern
  • 📦 Built-in HTTP status codes for common scenarios
  • 🔄 Easy conversion to ASP.NET Core IActionResult
  • 🎨 Flexible response formatting with Context and Data properties
  • Clean API with static factory methods
  • 🚀 Targets .NET 10 with C# 14.0

Installation

dotnet add package WebApi.ServiceResult

Quick Start

Success Responses

// 200 OK - Return data with optional message
Result<User>.Ok(user);
Result<User>.Ok(user, "User retrieved successfully");

// 201 Created - For resource creation
Result<Product>.Created(newProduct, "Product created");

// 202 Accepted - For async operations
Result<string>.Accepted("Request accepted for processing");

// 204 No Content - For successful operations with no data
Result<object>.NoContent();

Client Error Responses

// 400 Bad Request
Result<User>.BadRequest("Invalid user data provided");

// 401 Unauthorized
Result<User>.Unauthorized("Authentication required");

// 403 Forbidden
Result<User>.Forbidden("You don't have permission to access this resource");

// 404 Not Found
Result<Product>.NotFound("Product not found");

// 409 Conflict
Result<User>.Conflict("A user with this email already exists");

// 422 Unprocessable Entity
Result<Order>.UnprocessableEntity("Order validation failed");

// 429 Too Many Requests
Result<object>.TooManyRequests("Rate limit exceeded. Please try again later");

Server Error Responses

// 500 Internal Server Error
Result<object>.InternalError("An unexpected error occurred");

// 503 Service Unavailable
Result<object>.ServiceUnavailable("Service is temporarily unavailable");

Custom Responses

// Custom status code with context
var context = new { Code = "CUSTOM_ERROR", Details = "Custom error details" };
Result<object>.Custom(context, 418, isSuccess: false);

Advanced Usage

Service Layer Integration

public interface IProductService
{
    Result<Product> GetById(int id);
    Result<Product> Create(CreateProductDto dto);
    Result<IEnumerable<Product>> GetAll();
}

public class ProductService : IProductService
{
    public Result<Product> GetById(int id)
    {
        var product = _repository.FindById(id);
        
        return product == null 
            ? Result<Product>.NotFound($"Product with ID {id} not found")
            : Result<Product>.Ok(product);
    }

    public Result<Product> Create(CreateProductDto dto)
    {
        try
        {
            var product = _repository.Add(dto);
            return Result<Product>.Created(product, "Product created successfully");
        }
        catch (DuplicateException)
        {
            return Result<Product>.Conflict("Product already exists");
        }
        catch (Exception ex)
        {
            return Result<Product>.InternalError($"Failed to create product: {ex.Message}");
        }
    }

    public Result<IEnumerable<Product>> GetAll()
    {
        var products = _repository.GetAll();
        return Result<IEnumerable<Product>>.Ok(products);
    }
}

Controller with Service Integration

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _productService;

    public ProductsController(IProductService productService)
    {
        _productService = productService;
    }

    [HttpGet("{id}")]
    public IActionResult GetProduct(int id)
    {
        var result = _productService.GetById(id);
        return result.ToResult();
    }

    [HttpGet]
    public IActionResult GetAllProducts()
    {
        return _productService.GetAll().ToResult();
    }

    [HttpPost]
    public IActionResult CreateProduct([FromBody] CreateProductDto dto)
    {
        return _productService.Create(dto).ToResult();
    }
}

Using ToResultAll() for Full Response

The ToResultAll() extension returns all properties of the Result object:

[HttpGet("{id}")]
public IActionResult GetProductWithMetadata(int id)
{
    var result = _productService.GetById(id);
    return result.ToResultAll();
}

// Response body will include:
// {
//   "success": true,
//   "statusCode": 200,
//   "message": null,
//   "context": null,
//   "data": { "id": 1, "name": "Product", "price": 29.99 }
// }

Error Handling Pattern

public Result<Product> Update(int id, UpdateProductDto dto)
{
    try
    {
        var existingProduct = _repository.FindById(id);
        if (existingProduct == null)
            return Result<Product>.NotFound($"Product with ID {id} not found");

        var updatedProduct = _repository.Update(id, dto);
        return Result<Product>.Ok(updatedProduct, "Product updated successfully");
    }
    catch (UnauthorizedAccessException)
    {
        return Result<Product>.Forbidden("You don't have permission to update this product");
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error updating product {ProductId}", id);
        return Result<Product>.InternalError("An error occurred while updating the product");
    }
}

Using Custom Context for Additional Data

public Result<Order> PlaceOrder(PlaceOrderDto dto)
{
    var order = _orderService.Place(dto);
    if (order != null)
    {
        var context = new
        {
            Details = "Order placed successfully",
            OrderId = order.Id,
        };
        
        return Result<Order>.Custom(context, 200, isSuccess: true);
    }

    return Result<Order>.InternalError("Failed to place order");
}

Using Context for Rich Error Information

[HttpPost]
public IActionResult CreateUser([FromBody] CreateUserDto dto)
{
    var validationErrors = ValidateUser(dto);
    
    if (validationErrors.Any())
    {
        var errorContext = new
        {
            Errors = validationErrors,
            Timestamp = DateTime.UtcNow
        };
        
        return Result<User>.Custom(errorContext, 400, isSuccess: false).ToResult();
    }
    
    var user = _userService.Create(dto);
    return Result<User>.Created(user, "User created successfully").ToResult();
}

API Reference

Result<T> Properties

Property Type Description
Success bool Indicates whether the operation was successful
StatusCode int HTTP status code (e.g., 200, 404, 500)
Message string? Optional message providing additional information
Context object? Optional context object for additional data
Data T? The actual data returned from the operation

Extension Methods

Method Description
ToResult() Converts Result to IActionResult (returns Context, Data, or Message)
ToResultAll() Converts Result to IActionResult (returns all Result properties)

Response Priority in ToResult()

The ToResult() extension method determines the response body in this order:

  1. Context - if not null, returns Context
  2. Data - if Context is null, returns Data
  3. Message - if both Context and Data are null (or on error), returns { Message: "..." }
  4. StatusCode only - if all are null, returns just the status code

License

MIT License

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues, questions, or suggestions, please open an issue on the GitHub repository.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 167 4/7/2026