https://github.com/ssukhpinder/rest.api.features
Collection of REST API Features followed with best pratices
https://github.com/ssukhpinder/rest.api.features
aspnetcore beginner coding csharp dotnet dotnet-core programming rest restful-api
Last synced: 27 days ago
JSON representation
Collection of REST API Features followed with best pratices
- Host: GitHub
- URL: https://github.com/ssukhpinder/rest.api.features
- Owner: ssukhpinder
- License: mit
- Created: 2024-03-12T15:21:21.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2024-03-18T07:22:14.000Z (almost 2 years ago)
- Last Synced: 2025-01-11T12:37:48.115Z (11 months ago)
- Topics: aspnetcore, beginner, coding, csharp, dotnet, dotnet-core, programming, rest, restful-api
- Language: C#
- Homepage: https://medium.com/c-sharp-progarmming
- Size: 29.3 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# REST.API.Features
## 1. Common Response Model
All the API methods will adhere to the common response model structure. All features/controller methods defined in the above repository have followed the best practice.
### Example
```
///
/// Common wrapper class for all API response models where T can be a simple or complex data type
///
///
public class ResponseMetaData
{
public HttpStatusCode Status { get; set; }
public string? Message { get; set; }
public bool IsError { get; set; }
public string? ErrorDetails { get; set; }
public string? CorrealtionId { get; set; }
public T? Result { get; set; }
}
```
### 1.1 Return the model from the controller
```
public IActionResult Get()
{
var responseMetadata = new ResponseMetaData()
{
Status = System.Net.HttpStatusCode.OK,
IsError = false,
Message = CommonApiConstants.Responses.ResponseV1
};
return StatusCode((int)responseMetadata.Status, responseMetadata);
}
```
## 2. Exception Handling
### 2.1 Global Exception
A global middleware that gets activated whenever an exception occurs within the application.
- The middleware will also honour the common response model in case of exception
- Finally, the JSON response will be returned.
### Example
```
public class GlobalExceptionHandler
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public GlobalExceptionHandler(
RequestDelegate next,
ILogger logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception exception)
{
ResponseMetaData responseMetadata = new()
{
Status = HttpStatusCode.InternalServerError,
IsError = true,
ErrorDetails = CommonExceptionConstants.SomeUnknownError
};
var serializedResponseMetadata = JsonConvert.SerializeObject(responseMetadata);
_logger.LogError(exception, "Exception occurred: {Message}", JsonConvert.SerializeObject(serializedResponseMetadata));
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(serializedResponseMetadata);
}
}
}
```
### 2.2 Custom Exception
The first step is to create a CustomApiException as follows
```
public class CustomApiException : Exception
{
private readonly HttpStatusCode _statusCode;
private readonly string? _exceptionStrckTrace;
public HttpStatusCode StatusCode => _statusCode;
public string? ExceptionStackTrace => _exceptionStrckTrace;
public CustomApiException() { }
public CustomApiException(string message) : base(message) { }
public CustomApiException(string message, Exception ex) : base($"{CommonExceptionConstants.CustomExceptionPrefix} {message}", ex) { }
public CustomApiException(HttpStatusCode statusCode, string message) : base($"{CommonExceptionConstants.CustomExceptionPrefix} {message}")
{
_statusCode = statusCode;
}
public CustomApiException(HttpStatusCode statusCode, Exception ex) : base($"{CommonExceptionConstants.CustomExceptionPrefix} {ex.Message}")
{
_statusCode = statusCode;
_exceptionStrckTrace = ex.StackTrace;
}
public CustomApiException(HttpStatusCode statusCode, string message, Exception ex) : base($"{CommonExceptionConstants.CustomExceptionPrefix} {message}")
{
_statusCode = statusCode;
_exceptionStrckTrace = ex.StackTrace;
}
}
```
The CustomApiException class is a custom exception type that inherits from the base Exception class. It includes properties and constructors to handle and propagate exceptions in a customized manner within an API application.
A custom middleware that gets activated whenever an exception or **Custom Exception** occurs within the application.
>Note: A custom exception middleware is beneficial in scenarios where we need to customize the custom status codes, message, stack trace, and other properties.
- The middleware will also honour the common response model in case of any exception
- Finally, the JSON response will be returned.
### Example
```
public class CustomExceptionHandler
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public CustomExceptionHandler(
RequestDelegate next,
ILogger logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception exception)
{
ResponseMetaData responseMetadata = new()
{
Status = HttpStatusCode.InternalServerError,
IsError = true,
ErrorDetails = CommonExceptionConstants.SomeUnknownError
};
if (exception is CustomApiException)
{
responseMetadata.ErrorDetails = CommonExceptionConstants.CustomSomeUnknownError;
}
else
{
responseMetadata.ErrorDetails = CommonExceptionConstants.SomeUnknownError;
}
var serializedResponseMetadata = JsonConvert.SerializeObject(responseMetadata);
_logger.LogError(exception, "Exception occurred: {Message}", JsonConvert.SerializeObject(serializedResponseMetadata));
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(serializedResponseMetadata);
}
}
}
```
## 3. Versioning
Let's start by installing two NuGet packages that we'll need to implement API versioning:
- Asp.Versioning.Http
- Asp.Versioning.Mvc.ApiExplorer
### 3.1 Configuration
Now that the API Versioning package has been installed onto the project, the next step is to configure it. The following code shows how you can configure API Versioning in ASP.NET Core:
```
public static IServiceCollection UseAppVersioningHandler(this IServiceCollection services)
{
#region Versioning
services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1);
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
options.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader());
/*
* If you want to add both options i.e. URL versioning as well as header versioning.
* If attribute with version is supplied on controller then URL versioning will be applicable on Swqagger
* Else X-Api-Version will be added as a header attribute to the remaining controller methods
*/
/* Uncomment this code block
options.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("X-Api-Version"));
*/
}).AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'V";
options.SubstituteApiVersionInUrl = true;
});
#endregion
return services;
}
```
### 3.2 Registration
The above extension method will be used as follows in the Program.cs class
```
// Add versioning
builder.Services.UseAppVersioningHandler();
```
### 3.3 Update Folder Structure
The Controller folder structure breakdown is as follows:
- **REST.API.Features**
- **Controllers**
- **v1**
- VersionController
- **v2**
- VersionController
### 3.4 Add Version Attributes
Finally, the controller methods are structured with the following attributes:
```
///
/// Controller class contains API methods to demonstrate versioning
///
[ApiController]
[ApiVersion(1)]
[Route("api/v{v:apiVersion}/[controller]")]
```