Skip to main content

RA.Utilities.Core.Constants

NuGet version NuGet Downloads Codecov Publish NuGet GitHub license

A centralized and consistent set of core values for the RA.Utilities ecosystem. It replaces magic strings and magic numbers with named read-only fields and a semantic ResponseType enum, giving every service a single source of truth for HTTP codes, messages, header names, and validation errors.

🎯 Purpose​

The primary goal of this package is to ensure consistency across different projects and services. By using these predefined values, you can:

  • Avoid Typos: Prevent hard-to-find bugs caused by typos in strings or numbers.
  • Improve Readability: Make your code more self-documenting (e.g., BaseResponseCode.NotFound is clearer than 404).
  • Simplify Maintenance: Update a value in one place, and the change propagates everywhere it's used.
  • Stay Consistent: Every service in the ecosystem reports the same status code and wording for the same outcome.

✨ Key Features​

  • HTTP Status Codes — BaseResponseCode exposes named int fields for all common HTTP status codes (200–504)
  • Standardized Messages — BaseResponseMessages delivers consistent, production-ready API response text
  • Validation Error Codes & Messages — BaseErrorCode and BaseErrorMessage standardize field-level validation failures (email, currency, money, SSN, IBAN)
  • Semantic Response Types — ResponseType is an enum for API outcome classification; its numeric values are stable and serializable
  • HTTP Header Names — HeaderParameters centralizes common header names for tracing, auth, and location
  • Patchable Values — fields are static readonly (not const), so a corrected value ships in a package update without recompiling every consumer
  • Zero Dependencies — No external package dependencies; pairs seamlessly with RA.Utilities.Core.Exceptions
  • No Configuration — No DI registration, no options, and no setup required
Upgrading from v10.0.3 or earlier?

v10.0.4 made two breaking changes: every field moved from const to static readonly, and ResponseType became a plain enum. The values themselves are unchanged. Later releases are purely additive: v10.0.5 adds four IBAN validation codes and messages, and v10.1.1 adds PaymentRequired (402) to BaseResponseCode, BaseResponseMessages, and ResponseType. See the Migration Guides before upgrading.


🛠️ Getting Started​

Prerequisites​

  • .NET 10.0 or later

Installation​

.NET CLI

dotnet add package RA.Utilities.Core.Constants

Package Manager Console

Install-Package RA.Utilities.Core.Constants

PackageReference (csproj)

<PackageReference Include="RA.Utilities.Core.Constants" Version="10.1.1" />

Hello World​

The simplest possible usage — replace a magic number with a named value:

using RA.Utilities.Core.Constants;

// Before: magic number
return StatusCode(404, "The requested resource was not found.");

// After: self-documenting values
return StatusCode(BaseResponseCode.NotFound, BaseResponseMessages.NotFound);

No DI registration, no setup, no configuration. Just add the using directive and reference the values.


🧠 Core Concepts​

The package provides six types, each serving a distinct role in your API layer:

TypeRoleExample
BaseResponseCodeHTTP status codes as static readonly int fieldsBaseResponseCode.NotFound → 404
BaseResponseMessagesHuman-readable response messages as static readonly string fieldsBaseResponseMessages.NotFound → "The requested resource was not found."
BaseErrorCodeMachine-readable validation error codes as static readonly string fieldsBaseErrorCode.EmailRequired → "REQUIRED_EMAIL"
BaseErrorMessageHuman-readable validation error messages as static readonly string fieldsBaseErrorMessage.EmailRequired → "The 'Email address' field is required."
ResponseTypeSemantic outcome labels as an enumResponseType.NotFound → 8 (numeric value)
HeaderParametersHTTP header names as static readonly string fieldsHeaderParameters.XRequestId → "x-request-id"
Not compile-time constants

Since v10.0.4, the constant holders expose static readonly fields rather than compile-time constants. You can read them anywhere, but you cannot use them where C# requires a constant expression — attribute arguments, default parameter values, case labels, or const declarations. (ResponseType is an enum, so its members remain constant expressions.) See Troubleshooting for workarounds.

How they work together​

BaseResponseCode and BaseResponseMessages handle the transport-level concerns (what HTTP code and what message), while ResponseType adds a machine-readable semantic label inside the JSON response body. HeaderParameters ensures header access is consistent across middleware, endpoints, and HTTP clients.

BaseErrorCode and BaseErrorMessage operate one level deeper: they describe why a request was rejected at the field level. Each code/message pair (e.g., BaseErrorCode.EmailRequired + BaseErrorMessage.EmailRequired) populates a structured ValidationError, which the value objects in RA.Utilities.Core.ValueObjects (Email, Currency, Money, SSN, IBAN) throw and the RA.Utilities.Api middleware translates into standardized HTTP 400 responses.

HTTP Response
├─ Status Code: BaseResponseCode.NotFound (404)
├─ Body:
│ ├─ responseCode: 404
│ ├─ responseType: 8 ← ResponseType.NotFound (numeric by default)
│ └─ responseMessage: "Product with value '99' not found."
└─ Headers:
└─ x-request-id: "abc-123" ← HeaderParameters.XRequestId

ResponseType is a plain C# enum, so System.Text.Json writes its numeric value (ResponseType.NotFound → 8) unless you register JsonStringEnumConverter. See ResponseType for the exact setup.

Terminology​

  • Magic strings/numbers: Hard-coded literals scattered throughout code. Replacing them with named values improves readability and prevents typos.
  • Static read-only field: A public static readonly member whose value is assigned once when the type is initialized. It behaves like a constant at call sites but is not a compile-time constant, so it cannot be used in constant-required contexts.
  • Semantic response type: An enum member that labels the outcome inside the JSON body. System.Text.Json writes the numeric member value by default; register JsonStringEnumConverter if your contract expects names such as "NotFound". See ResponseType for details.

📖 How-To Guides​

Return a 404 with consistent messaging​

using Microsoft.AspNetCore.Mvc;
using RA.Utilities.Core.Constants;

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
var product = _productService.GetById(id);

if (product == null)
{
return NotFound(BaseResponseMessages.NotFound);
}

return Ok(product);
}
}

Return a 400 for invalid input​

[HttpPost]
public IActionResult CreateProduct([FromBody] Product newProduct)
{
if (!ModelState.IsValid)
{
return BadRequest(BaseResponseMessages.BadRequest);
}

var createdProduct = _productService.Create(newProduct);
return CreatedAtAction(nameof(GetProduct), new { id = createdProduct.Id }, createdProduct);
}

Use consistent header names​

[HttpGet("trace")]
public IActionResult GetTraceId()
{
if (Request.Headers.TryGetValue(HeaderParameters.XRequestId, out var requestId))
{
return Ok(new { requestId });
}

return BadRequest(BaseResponseMessages.BadRequest);
}

Reject invalid input with a structured validation error​

using RA.Utilities.Core.Constants;
using RA.Utilities.Core.Exceptions;

if (string.IsNullOrWhiteSpace(request.Email))
{
// Pair the machine-readable code with its human-readable message
throw new BadRequestException(new ValidationError(BaseErrorMessage.EmailRequired)
{
PropertyName = "Email",
ErrorCode = BaseErrorCode.EmailRequired
});
}

See BaseErrorCode and BaseErrorMessage for the full catalog and the JSON shape these errors produce.

Validate an IBAN and pick the right error code​

IBAN validation has four codes so clients can tell a missing value from a country-length mismatch, a malformed value, and a failed check digit:

SituationError codeMessage
Value is null, empty, or whitespaceBaseErrorCode.IbanRequiredBaseErrorMessage.IbanRequired
Structure is invalid (country code, check digits, or 13–34 characters)BaseErrorCode.IbanNotValidBaseErrorMessage.IbanNotValid
ISO 7064 mod-97 check failsBaseErrorCode.IbanCheckDigitsBaseErrorMessage.IbanCheckDigits
Length does not match the country's registry entryBaseErrorCode.IbanLengthBaseErrorMessage.IbanLength

The IBAN value object in RA.Utilities.Core.ValueObjects applies the first three checks and throws the matching BadRequestException:

using RA.Utilities.Core.Exceptions;
using RA.Utilities.Core.ValueObjects;

IBAN iban = new(request.Iban); // REQUIRED_IBAN, NOT_VALID_IBAN, or IBAN_CHECK_DIGITS

The value object validates the IBAN structure (13–34 characters) rather than a per-country length table, so use IbanLength when you enforce the registry length yourself:

using System.Collections.Generic;
using RA.Utilities.Core.Constants;
using RA.Utilities.Core.Exceptions;

// A subset of the SWIFT IBAN registry: country code -> total length.
private static readonly Dictionary<string, int> IbanLengths = new()
{
["DE"] = 22,
["FR"] = 27,
["GB"] = 22,
["NL"] = 18
};

public static void EnsureCountryLength(string countryCode, string compactIban)
{
if (IbanLengths.TryGetValue(countryCode, out int expected) && compactIban.Length != expected)
{
throw new BadRequestException(new ValidationError(BaseErrorMessage.IbanLength)
{
PropertyName = "IBAN",
ErrorCode = BaseErrorCode.IbanLength,
AttemptedValue = compactIban.Length,
ExpectedValue = expected
});
}
}

See BaseErrorCode and BaseErrorMessage for the full catalog.

Serialize ResponseType as a string​

ResponseType is an enum with no built-in converter. If your API contract exposes names ("NotFound") instead of numbers (8), register JsonStringEnumConverter for the serializer used by your endpoints:

using System.Text.Json.Serialization;

// Minimal APIs / Results.Json — this is what RA.Utilities.Api uses
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
});

// MVC controllers — configure AddControllers().AddJsonOptions(...) the same way

Verify the behavior your clients depend on: with JsonStringEnumConverter, names are written as-is, matched case-insensitively on read, and unknown names throw JsonException. See ResponseType for details.

Handle an outcome that has no dedicated ResponseType member​

The set of ResponseType values is closed. For an outcome without a dedicated member (e.g., HTTP 502 Bad Gateway), use the closest built-in member and communicate the specifics through the error code and message:

throw new RaBaseException(502, ResponseType.Error, "The upstream service failed.");

⚙️ Configuration​

This package has no configuration options. There is no DI registration, no appsettings.json entries, and no options classes. All values are public static readonly fields or enum members — reference them directly.

The only optional setup is JSON serialization: if your API contract requires enum names instead of numbers, register JsonStringEnumConverter as shown in Serialize ResponseType as a string. That configures System.Text.Json, not this package.


✨ Available Constants​


🙌 Contributing & Support​

Found a bug or have a suggestion?​

Contributing​

Contributions are welcome! Please follow the standard workflow:

  1. Fork the RA.Utilities repository
  2. Create a branch from main with a descriptive name (e.g., feature/add-new-constant)
  3. Make your changes — ensure XML doc comments are included for any new public API
  4. Submit a pull request to the main branch with a clear description
ResourceURL
NuGetRA.Utilities.Core.Constants
SourceGitHub — RedonAlla/RA.Utilities
LicenseMIT
Build StatusGitHub Actions