Search code examples
controllerblazor

use of controllers with Blazor Assembly service


The sample solution has for FetchData.Razor forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast"); The quoted part Seems to indicate the name of the controller. Is that correct ?

I want to extend the example to understand how to use it. To do this I would enable the user to choose a refresh rate and automatically refresh the data - but first step is to record the 2 values with a new class LastUpdateInfo in .shared weatherForcast.cs

public class LastUpDateInfo
    {
        public int updateRateSecs { get; set; }
        public DateTime RefreshDateTime { get; set; }
    }

While the solution does compile, I get an unhandled exception -response status code does not indicate success. (500)

I added this to controller

   public LastUpDateInfo GetUpdateInfo()
        {
            var thisUpDateInfo = new LastUpDateInfo();
            thisUpDateInfo.RefreshDateTime = DateTime.Now;
            thisUpDateInfo.updateRateSecs = 3;  // needs to be fed from UX
            return thisUpDateInfo;

        }

and called from FetchData.razor

 protected override async Task OnInitializedAsync()
    {
        currInfo = await Http.GetFromJsonAsync<LastUpDateInfo>("WeatherForecast");
        forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast"); // URI ? is the quoted the controllerName ?
        // why is there a literal here called "WeatherForecast" ??
    }

The blazor assembly feels quite different than previous Rest API with razor pages (cshtml) and Im struggling to get a grasp of the mechanics.

Thanks !

I added a second class, updated the controller and called from FetchData.razor However the call throws an error ( 500 ) but dont understand how it should work


Solution

  • In Client/Program.cs you can find a line like this:

    builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
    

    This registers an HttpClient as a scoped service. The BaseAddress of the HttpClient is configured to be the address of the "host environment". The host is basically the Server application where the api also exists. Let's say your server is listening on https://localhost:3377 so this is the BaseAddress of the HttpClient.

    When you call:

    await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
    

    the string parameter is the relative uri that should be appended to the base address, in this case "WeatherForecast". The final uri will be base address uri + relative uri so it will send a GET request to https://localhost:3377/WeatherForecast.

    This is the route for the Get endpoint in the WeatherForecastController. This happens because the controller has the [Route("[controller]")] attribute. This means all the endpoints inside this controller will start with /WeatherForecast in the route. The Get method does not have its own Route attribute so the final route to access it is https://localhost:3377/WeatherForecast.

    On your GetUpdateInfo method you also did not specify a route so both endpoints have the same route and the app can't differentiate between the two so it throws exception. Instead in your GetUpdateInfo endpoint you can add a [Route("UpdateInfo")] attribute.

    [HttpGet]
    [Route("UpdateInfo")]
    public LastUpDateInfo GetUpdateInfo()
    {
        var thisUpDateInfo = new LastUpDateInfo();
        thisUpDateInfo.RefreshDateTime = DateTime.Now;
        thisUpDateInfo.updateRateSecs = 3;  // needs to be fed from UX
        return thisUpDateInfo;
    }
    

    Then the absolute route to that endpoint will be https://localhost:3377/WeatherForecast/UpdateInfo and in your Client you can do:

    await Http.GetFromJsonAsync<LastUpDateInfo>("WeatherForecast/UpdateInfo");
    

    You can check Attribute routing for REST APIs to better understand how routing in ASP.NET Core controllers works.