I'm building a Blazor Web App. I have identified that some pages and components need access to some external data, and I have defined an interface that allows them to access these data.
public interface IHelloWorld
{
Task<string> GetHelloWorldAsync();
}
In the Blazor Server part of the web app, I want to use a concrete implementation of IHelloWorld which fetches data from the appropriate source.
public class HelloWorld
{
public Task<string> GetHelloWorldAsync()
{
return Task.FromResult("Hello World!");
}
}
I also want to be able to retrieve these data from the WASM part of the web app, and rather than exposing database servers directly to the internet I want to send requests via a controller in the Blazor Server part. I've written a controller...
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class HelloWorldController : ApiController
{
private IHelloWorld _helloWorld;
public HelloWorldController(IHelloWorld helloWorld)
{
_helloWorld=helloWorld;
}
[HttpGet("HelloWorld")]
public Task<IActionResult> GetHelloWorld()
{
return Ok(await _helloWorld.GetHelloWorldAsync());
}
}
... and a client, using Refit...
public interface IHelloWorldClient
{
[Get("/api/HelloWorld/HelloWorld"])
Task<string> GetHelloWorld();
}
In my Razor component, I can inject an IHelloWorld and use this with server side rendering just fine.
@inject IHelloWorld HelloWorld
@if(!string.IsNullOrEmpty(_greeting)){
<p>@_greeting</p>
}
@code{
private string _greeting;
protected override async Task OnParametersSetAsync()
{
var helloWorld = HelloWorld.GetHelloWorldAsync();
}
}
However, this component cannot run client side because there is no suitable concrete implementation of IHelloWorld. I could inject IHelloWorldClient instead, but then the server side implementation gets a bit ropey because the component ends up calling controller methods against the Blazor Server instance, and I'm sure I've seen Microsoft say that's bad but I don't recall where...
I'd really like for my IHelloWorld to be the single interface that works server side and client side. Server side, I can inject my concrete implementation, and client side I'd like to inject a Refit implementation. I tried being a bit hacky and had my Refit interface inherit from the IHelloWorld and marked each method as new with the appropriate attributes to make Refit work...
public interface IHelloWorldClient : IHelloWorld
{
[Get("/api/HelloWorld/HelloWorld"])
new Task<string> GetHelloWorld();
}
... and then tried to use this in the DI container...
builder.Services.AddScoped<IHelloWorld,IHelloWorldClient>();
... but for obvious reasons this fails at runtime as IHelloWorldClient can't be instantiated. When I use Refit, a proxy is automatically generated which implements this interface but I've no idea how to instantiate this myself.
Is it possible to use a very similar approach to get IHelloWorld to work server side and client side, or do I need to write a client side class that implements IHelloWorld and then calls to an injected IHelloWorldClient instead? I'm trying to avoid this as it's a lot of repetition.