skip to Main Content

I have a Blazor WASM app deployed to an Azure Static Web app with a managed .NET 8 isolated Azure Function API. When calling the API directly from Postman using the auto-generated web app URL + the API route I receive the expected response and can see a log in Application Insights. However when I call the same endpoint from within the deployed WASM application I receive an net::ERR_CONNECTION_REFUSED response and a request isn’t recorded in Application Insights. I have confirmed that this is functioning as expected locally.

The successful postman request:
Successful postman request

The Log in application insights:
Successful request in Azure Application Insights

My appsettings.json file that is packaged with the Blazor WASM app:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ApiBase": "http://localhost:7071"
}

The launchSettings.json of the Azure Function App API:

{
  "profiles": {
    "Project.Api": {
      "commandName": "Project",
      "commandLineArgs": "--port 7071",
      "launchBrowser": false
    }
  }
}

The Azure Function app is deployed to the Azure Static Web App and the SendContactEmailFunction function is registered in Azure:
Deployed azure function app with list of functions

The error I receive in the console from the deployed Blazor WASM app:
Blazor WASM app console error

How the HttpClient is registered:

var apiBase = builder.Configuration["ApiBase"]
              ?? throw new ArgumentException("API base address not found in config.");

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

The code that is calling the API function:

var response = await HttpClient.PostAsJsonAsync("/api/send-contact-email", sendEmailRequest);

The Azure function code:

public class SendContactEmailFunction(ILoggerFactory loggerFactory, ISender sender)
{
    private readonly ILogger logger = loggerFactory.CreateLogger<SendContactEmailFunction>();

    [Function(nameof(SendContactEmailFunction))]
    public async Task<HttpResponseData> Run(
        [HttpTrigger(AuthorizationLevel.Function, "post", Route = "send-contact-email")] HttpRequestData req,
        FunctionContext executionContext, CancellationToken cancellationToken)
    {
        logger.LogInformation($"{nameof(SendContactEmailFunction)} function received a request.");

        var requestBody = await new StreamReader(req.Body).ReadToEndAsync(cancellationToken);
        var request = JsonConvert.DeserializeObject<SendContactEmailRequest>(requestBody);

        HttpResponseData response;

        try
        {
            if (request == null)
            {
                throw new ApplicationException("Unable to deserialize response.");
            }

            var emailCommand = new SendContactEmailCommand(
                request.Name,
                request.FromEmail,
                request.Message,
                request.RecaptchaResponse);

            var sendEmailResult = await sender.Send(emailCommand, cancellationToken);

            if (sendEmailResult.IsFailure)
            {
                logger.LogError("Send email command failed. Error: {Error}", sendEmailResult.Error.Message);

                response = req.CreateResponse(HttpStatusCode.BadRequest);

                var error = new ErrorResponse
                {
                    Code = (int)HttpStatusCode.BadRequest,
                    Message = sendEmailResult.Error.Message
                };

                await response.WriteAsJsonAsync(error, cancellationToken);
            }
            else
            {
                response = req.CreateResponse(HttpStatusCode.OK);

                await response.WriteAsJsonAsync("", cancellationToken: cancellationToken);
            }
        }
        catch (Exception e)
        {
            logger.LogError(e, "Exception occured. Error: '{Message}'", e.Message);

            response = req.CreateResponse(HttpStatusCode.InternalServerError);
            
            await response.WriteAsJsonAsync(ErrorResponse.Generic, cancellationToken);
        }

        return response;
    }
}

2

Answers


  1. Chosen as BEST ANSWER

    I was able to get this working but I'm not quite sure what solved this in the end out of the two things I tried:

    1. I deleted and recreated the Azure Static Web App.

    2. I added a conditional check for hosting environment in the Program.cs file for the API base address. If in development - user the Function App URI from the appsettings.json. If not use the hosting environment base address:

      string apiBase; if (builder.HostEnvironment.IsDevelopment()) { apiBase = builder.Configuration["ApiBase"] ?? throw new ArgumentException("API base address not found in config."); } else { apiBase = builder.HostEnvironment.BaseAddress; }

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


  2. I’m just learning how to program with C#/Blazor so take this as you will. Why would the app be using the ApiBase of http://localhost:7071 when running as the Azure SWA? Shouldn’t that be relative such that the static web app url is substituted?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search