Skip to content

Challenge to create REST API C# application without ASP.NET Web API

License

Notifications You must be signed in to change notification settings

tokKurumi/RestApiChallenge

Repository files navigation

RestApiChallenge

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!

Features:

  1. Easy to Use: Designed for simplicity and ease of use.
  2. DI Support: Built-in support for Dependency Injection.
  3. Content-Type Support: Supports various Content-Types (application/json out of the box).
  4. Asynchronous Multithreading: Asynchronous processing of requests for improved performance.
  5. Logging: Integration of a simple and flexible logging mechanism to track actions and errors.
  6. Routing: Powerful mechanism for defining and handling routes, making it easy to configure entry points for different requests.
  7. Extensibility: Easy extension of framework functionality through additional modules or plugins.

Getting Started:

Prerequisites:

  • .NET 8
  • .NET Runtime

Installation:

Clone the repository:

git clone https://github.com/tokKurumi/RestApiChallenge.git

How to Use

  1. Add the following code to your Program.cs file:
using MultithreadRest.HostDefaults;

var host = MultithreadRestHost
    .CreateDefaultBuilder().Build();

await host.RunAsync();
  1. 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!" });
    }
}
  1. Everything is ready!

Getting user provided parameters

To retrieve parameters in your endpoint, you can utilize various sources:

  1. QueryParameters
  2. Body
  3. Cookies
  4. 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));
}

Return Values

You can use the following methods to return values to the user:

  1. OkAsync(T @object)
  2. OkAsync()
  3. Ok(ReadOnlyMemory memory)
  4. BadRequestAsync(T @object)
  5. NotFoundAsync(T @object)
  6. NotFoundAsync(T @object)
  7. InternalServerErrorAsync(T @object)

Dependency Injection Support

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);
    }
}

Dynamic Execution of Endpoints from Other Endpoints

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("]")));
    }
}

About

Challenge to create REST API C# application without ASP.NET Web API

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published