RestApiChallenge is a custom framework for building simple REST API applications. This framework is not intended for production use; it is merely the result of a challenge to create a custom framework!
- Easy to Use: Designed for simplicity and ease of use.
- DI Support: Built-in support for Dependency Injection.
- Content-Type Support: Supports various Content-Types (application/json out of the box).
- Asynchronous Multithreading: Asynchronous processing of requests for improved performance.
- Logging: Integration of a simple and flexible logging mechanism to track actions and errors.
- Routing: Powerful mechanism for defining and handling routes, making it easy to configure entry points for different requests.
- Extensibility: Easy extension of framework functionality through additional modules or plugins.
- .NET 8
- .NET Runtime
Clone the repository:
git clone https://github.com/tokKurumi/RestApiChallenge.git
- Add the following code to your
Program.cs
file:
using MultithreadRest.HostDefaults;
var host = MultithreadRestHost
.CreateDefaultBuilder().Build();
await host.RunAsync();
- Define your custom Endpoint in a new file:
namespace Example.Endpoints;
using System.Net.Http;
using System.Threading.Tasks;
using MultithredRest.Core.Attributes;
using MultithredRest.Core.Endpoint;
using MultithredRest.Core.HttpServer;
using MultithredRest.Core.Result;
[RegistrateEndpoint]
public class HelloWorldEndpoint : EndpointBase
{
public override HttpMethod Method => HttpMethod.Get;
public override string Route => @"/helloworld";
public override async Task<IActionResult> GenerateResponseAsync(HttpRequest request, CancellationToken cancellationToken = default)
{
return await OkAsync(new { Message = "Hello world!" });
}
}
- Everything is ready!
To retrieve parameters in your endpoint, you can utilize various sources:
- QueryParameters
- Body
- Cookies
- Headers
public override async Task<IActionResult> GenerateResponseAsync(HttpRequest request, CancellationToken cancellationToken = default)
{
var queryParameters = request.QueryParameters;
var body = await request.DeserializeBodyAsync<WeatherParam>(cancellationToken);
var cookies = request.Cookies;
var headers = request.Headers;
var encoding = request.ContentEncoding;
var contentLength = request.ContentLength64;
return await OkAsync(await _weatherService.GetCityWeather(body.Postcode, body.CountryCode));
}
You can use the following methods to return values to the user:
- OkAsync(T @object)
- OkAsync()
- Ok(ReadOnlyMemory memory)
- BadRequestAsync(T @object)
- NotFoundAsync(T @object)
- NotFoundAsync(T @object)
- InternalServerErrorAsync(T @object)
You can easily add dependency injection containers directly to your application! Here's an example of how to configure DI containers:
using Example.Data;
using Example.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MultithredRest.HostDefaults;
var host = MultithreadRestHost
.CreateDefaultBuilder()
.ConfigureServices(services =>
{
services.AddSingleton<IWeatherService, WeatherService>();
services.AddHttpClient<IWeatherService, WeatherService>(client =>
{
client.BaseAddress = new Uri(@"https://api.openweathermap.org/");
});
})
.Build();
await host.RunAsync();
namespace Example.Services;
using System.Text.Json;
using Example.Models.WeatherApi;
public class WeatherService(HttpClient api)
: IWeatherService
{
private readonly HttpClient _api = api;
public async Task<CityWeather?> GetCityWeather(string postCode, string countryCode, CancellationToken cancellationToken = default)
{
var cityWeatherResponse = await _api.GetAsync(@$"data/2.5/weather?q={postCode},{countryCode}&APPID={Resources.Weather.WeatherApi.key}", cancellationToken);
if (!cityWeatherResponse.IsSuccessStatusCode)
{
throw new HttpRequestException("Invalid request");
}
return await JsonSerializer.DeserializeAsync<CityWeather>(
await cityWeatherResponse.Content.ReadAsStreamAsync(cancellationToken),
cancellationToken: cancellationToken);
}
}
namespace Example.Endpoints;
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using MultithredRest.Core.Attributes;
using MultithredRest.Core.Endpoint;
using MultithredRest.Core.HttpServer;
using MultithredRest.Core.RequestDispatcher;
using MultithredRest.Core.Result;
using MultithredRest.Helpers;
[RegistrateEndpoint]
public class DynamicExecuteEndpoint(IServiceProvider serviceProvider)
: EndpointBase
{
private readonly IServiceProvider _serviceProvider = serviceProvider;
public override string Route => @"/dynamic";
public override HttpMethod Method => HttpMethod.Post;
protected IRequestDispatcher RequestDispatcher { get => _serviceProvider.GetRequiredService<IRequestDispatcher>(); }
public override async Task<IActionResult> GenerateResponseAsync(HttpRequest request, CancellationToken cancellationToken = default)
{
var parsedRequests = request.DeserializeEnumerableBodyAsync<HttpDynamicRequest>(cancellationToken);
var results = new List<ReadOnlyMemory<byte>>();
await foreach (var parsedRequest in parsedRequests)
{
if (parsedRequest is not null)
{
results.Add((await RequestDispatcher.DispatchAsync(parsedRequest.ToHttpRequest(request))).Buffer);
}
}
return Ok(results.ConcatenateReadOnlyMemories(request.ContentEncoding.GetBytes("["), request.ContentEncoding.GetBytes(","), request.ContentEncoding.GetBytes("]")));
}
}