AspNetCore.TypeSafe introduces a typesafe rest api to your ASP.NET Core projects.
Core-Components of the AspNetCore.TypeSafe library.
Server-Components of the AspNetCore.TypeSafe library.
An exemplary client implementation of the AspNetCore.TypeSafe library using the RestSharp-client.
An exemplary client implementation of the AspNetCore.TypeSafe library with a SignalR-client.
First of all you will have to enable this framework in your Startup.cs like shown below
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddJsonOptions(o => o.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto)
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddTypeSafe();
}
Please not, that it is mandatory to at least use the TypeNameHandling setting "Auto" to support the magic, that is happening in background.
Now you can define a service interface with all the methods your api should support:
public interface IServiceInterface
{
Task<string> Foo(string name);
Task<SumResponse> Bar(SumRequest request);
}
Finally, to implement the actual ServiceInterface, just implement the IServiceInterface in your Controller like this:
Please not that later on our whole communication will be done Task<object> Post(TypeSafeRequestWrapper typeSafeRequest)
by invoking the Post-method. The ResolveProvider will do the magic and forward it to each individual implemention of our interface.
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase, IServiceInterface
{
private readonly IResolveProvider m_resolveProvider;
public ValuesController(IResolveProvider resolveProvider)
{
m_resolveProvider = resolveProvider;
}
[HttpGet]
public IActionResult Get()
{
return BadRequest();
}
[HttpPost]
public async Task<object> Post(TypeSafeRequestWrapper typeSafeRequest)
{
// Here happens the "magic"
var result = await m_resolveProvider.ResolveRequestAsync(this, typeSafeRequest?.Request);
return result;
}
public Task<string> Foo(string name)
{
return Task.Run(() => $"Hello {name}");
}
public Task<SumResponse> Bar(SumRequest request)
{
return Task.FromResult(new SumResponse {SumResult = request.Number1 + request.Number2});
}
}
Basically, you just have to make use of the RestSharpServiceClientBase base class.
public class ServiceClient : RestSharpServiceClientBase<IServiceInterface>, IServiceInterface
{
public ServiceClient(string url) : base(url)
{
}
public async Task<string> Foo(string name)
{
var request = BuildRequest(BuildParams(name));
return await PostAsync<string>(request).ConfigureAwait(false);
}
public async Task<SumResponse> Bar(SumRequest sumRequest)
{
var request = BuildRequest(BuildParams(sumRequest));
return await PostAsync<SumResponse>(request).ConfigureAwait(false);
}
}
This is just an example of how to do it on the client.
Feel free to implement your own client by taking a peek at what I've done in the RestSharpServiceClientBase
.
To make SignalR request typesafe, you can use implement the Hub like this. It should look very similar to the way you'd implement it normally.
public class MyTypeSafeHub : Hub, IServiceInterface
{
private readonly IResolveProvider m_resolveProvider;
public MyTypeSafeHub(IResolveProvider resolveProvider)
{
m_resolveProvider = resolveProvider;
}
public Task<string> Foo(string name)
{
return Task.Run(() => $"Hello {name}");
}
public Task<SumResponse> Bar(SumRequest request)
{
return Task.FromResult(new SumResponse {SumResult = request.Number1 + request.Number2});
}
}
Your client can use the examplary implementaton by SignalRServiceClientBase
public class SignalRServiceClient : SignalRServiceClientBase, IServiceInterface
{
public SignalRServiceClient(HubConnection connection) : base(connection)
{
}
public async Task<string> Foo(string name)
{
return await InvokeAsync<string>(BuildParams(name)).ConfigureAwait(false);
}
public async Task<SumResponse> Bar(SumRequest request)
{
return await InvokeAsync<SumResponse>(BuildParams(request)).ConfigureAwait(false);
}
}