dotnet / AspNetCore.Docs

Documentation for ASP.NET Core
https://docs.microsoft.com/aspnet/core
Creative Commons Attribution 4.0 International
12.61k stars 25.3k forks source link

BlazorWebAssembly - CallWebApi not working #17563

Closed oridespavaneli closed 4 years ago

oridespavaneli commented 4 years ago

I try to test the CallWebApi official sample, it’s not working for me.

Sample: AspNetCore.Docs-master\aspnetcore\blazor\common\samples\3.x\BlazorWebAssemblySample System.Net.Http.Json (3.2.0-preview3.20175.8) package used to replace Microsoft.AspNetCore.Blazor.HttpClient package. The required WebApi is up and running, all intructions applied.

Option: Call a Web Api (https://localhost:44365/CallWebApi) When clicked, shows the alert “An unhandled erros has ocorred. Reload”. The page is prepared for sending the request. If you dismiss the alert and click on the button “Request”, i got the following response:

Response Status: RedirectMethod

Using Google Chrome (version 80.0.3987.149 (Oficial Version) 64 bits)

Body: TypeError: Failed to fetch at WebAssembly.Net.Http.HttpClient.WasmHttpMessageHandler.doFetch (System.Threading.Tasks.TaskCompletionSource1[TResult] tcs, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x2d7e5c0 + 0x00988> in :0 at WebAssembly.Net.Http.HttpClient.WasmHttpMessageHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x2d565d8 + 0x00184> in :0 at System.Net.Http.HttpClient.FinishSendAsyncBuffered (System.Threading.Tasks.Task1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) in :0 at BlazorSample.Components.HTTPRequestTester.DoRequest () [0x001db] in E:\Desenvolvimento\repos\Samples\BlazorWasm\BlazorWebAssemblySample\Components\HTTPRequestTester.razor:109

Using Firefox (version 74.0 64bits)

Body: TypeError: NetworkError when attempting to fetch resource. at WebAssembly.Net.Http.HttpClient.WasmHttpMessageHandler.doFetch (System.Threading.Tasks.TaskCompletionSource1[TResult] tcs, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x2cc78b0 + 0x00988> in :0 at WebAssembly.Net.Http.HttpClient.WasmHttpMessageHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x2ca1110 + 0x00184> in :0 at System.Net.Http.HttpClient.FinishSendAsyncBuffered (System.Threading.Tasks.Task1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) in :0 at BlazorSample.Components.HTTPRequestTester.DoRequest () in :0

Using Microsoft Edge (version 44.18362.449.0)

Body: TypeError: Falha ao buscar at WebAssembly.Net.Http.HttpClient.WasmHttpMessageHandler.doFetch (System.Threading.Tasks.TaskCompletionSource1[TResult] tcs, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x2cc78b0 + 0x00988> in :0 at WebAssembly.Net.Http.HttpClient.WasmHttpMessageHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x2ca1110 + 0x00184> in :0 at System.Net.Http.HttpClient.FinishSendAsyncBuffered (System.Threading.Tasks.Task1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) in :0 at BlazorSample.Components.HTTPRequestTester.DoRequest () in :0

Using any browser:

Headers: empty…


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

guardrex commented 4 years ago

Hello @oridespavaneli ... When you set up the web API according to https://docs.microsoft.com/aspnet/core/tutorials/first-web-api with the recommended CORS policy, you should make your cross-origin request from the Blazor app running on either port 5000 (HTTP) or port 5001 (HTTPS). However, it looks like your app is running on port 44365 from what you posted.

Try switching your Blazor app profile over from "IIS Express" to "BlazorWebAssemblySample" and confirm that the app is running on 5001 with HTTPS. This is based on the web API allowing the request with the recommended policy ...

app.UseCors(policy => 
    policy.WithOrigins("http://localhost:5000", "https://localhost:5001")
    .AllowAnyMethod()
    .WithHeaders(HeaderNames.ContentType));

Of course, the alternative would be to change that policy to permit the port that your Blazor app is running on. You could change the policy in the web API config to ...

app.UseCors(policy => 
    policy.WithOrigins("https://localhost:44365")
    .AllowAnyMethod()
    .WithHeaders(HeaderNames.ContentType));

... and then continue to run the Blazor app on 44365.

Leave this issue open even if you get it running ... I might need to make an update to further reinforce these points if this turns out to be the problem.

oridespavaneli commented 4 years ago

Thank you. 1) The WebApi app is running on localhost port 44308 with HTTPS. I´m using IIS Express profile. Content of launchsettings.json { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:51553/", "sslPort": 44308 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "TodoApi": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:44308;http://localhost:51553" } } }

2) In the WebAPI app, CORS policy in the startup.cs is as follows:

        app.UseCors(policy =>
         policy.WithOrigins("http://localhost:57520", "https://localhost:44365")
         .AllowAnyMethod()
         .AllowAnyHeader());

3.1) So, the CORS port is the same the WebAssembly app is running.

4) In the WebAssemply app, where the WebApi is called, the code of file HttpResquestTester.razor is:

@using Microsoft.AspNetCore.Components.WebAssembly.Http @inject System.Net.Http.HttpClient Http @inject NavigationManager NavigationManager

HTTP Request Tester

The default values of the following form POST (add a Todo item) to the web API created in the Tutorial: Create a web API with ASP.NET Core MVC topic with:

Add the following CORS middleware configuration to the web API's Startup.Configure method before it calls UseMvc:

app.UseCors(policy =>
    policy.WithOrigins("http://localhost:5000", "https://localhost:5001")
    .AllowAnyMethod()
    .WithHeaders(HeaderNames.ContentType, HeaderNames.Authorization)
    .AllowCredentials());

Adjust the domains and ports of WithOrigins as needed for the Blazor app.

The web API is configured for CORS to permit authorization cookies/headers and requests from client code, but the web API as created by the tutorial doesn't actually authorize requests. See the ASP.NET Core Security and Identity articles for implementation guidance.

URI:

Method:

Request body:

Request headers:
@foreach (var header in _requestHeaders) {
Name: Value:
}

<button class="btn btn-success" id="send-request" @onclick="@DoRequest">Request

@try { @if (_responseStatusCode.HasValue) {

Response

                <p><div>Status:</div><span id="response-status">@_responseStatusCode</span></p>
                                    <p><div>Body:</div><textarea id="response-body" readonly>@_responseBody</textarea></p>
                                                        <p><div>Headers:</div><textarea id="response-headers" readonly>@_responseHeaders</textarea></p>}}

catch (Exception ex) {

Exception

            <p><div>Status:</div><span id="response-status">@ex.Message</span></p>

}

@code { private string _uri = "https://localhost:44308/api/TodoItems"; private string _method = "POST"; private string _requestBody = @"{""name"":""A New Todo Item"",""isComplete"":false}"; private List _requestHeaders = new List () { new RequestHeader() { Name = "Content-Type", Value = "application/json" }, new RequestHeader() { Name = "Authorization", Value = "Bearer MHrHDcEfxjoYZgeFONFh7HgQ" } }; private System.Net.HttpStatusCode? _responseStatusCode; private string _responseBody; private string _responseHeaders;

private async void DoRequest()
{
    _responseStatusCode = null;

    try
    {
        var requestMessage = new HttpRequestMessage()
        {
            Method = new HttpMethod(_method),
            RequestUri = new Uri(_uri),
            Content = string.IsNullOrEmpty(_requestBody) ? null : new StringContent(_requestBody)
        };

        foreach (var header in _requestHeaders)
        {
            // StringContent automatically adds its own Content-Type header with default value "text/plain"
            // If the developer is trying to specify a content type explicitly, we need to replace the default value,
            // rather than adding a second Content-Type header.
            if (header.Name.Equals("Content-Type", StringComparison.OrdinalIgnoreCase) && requestMessage.Content != null)
            {
                requestMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(header.Value);
                continue;
            }

            if (!requestMessage.Headers.TryAddWithoutValidation(header.Name, header.Value))
            {
                requestMessage.Content?.Headers.TryAddWithoutValidation(header.Name, header.Value);
            }
        }

        var response = await Http.SendAsync(requestMessage);
        _responseStatusCode = response.StatusCode;
        _responseBody = await response.Content.ReadAsStringAsync();
        var allHeaders = response.Headers.Concat(response.Content?.Headers ?? Enumerable.Empty<KeyValuePair<string, IEnumerable<string>>>());
        _responseHeaders = string.Join(Environment.NewLine, allHeaders.Select(pair => $"{pair.Key}: {pair.Value.First()}").ToArray());
    }
    catch (Exception ex)
    {
        if (ex is AggregateException)
        {
            ex = ex.InnerException;
        }
        _responseStatusCode = System.Net.HttpStatusCode.SeeOther;
        _responseBody = ex.Message + Environment.NewLine + ex.StackTrace;
    }

    StateHasChanged();
}

private void AddHeader()
     => _requestHeaders.Add(new RequestHeader());

private void RemoveHeader(RequestHeader header)
     => _requestHeaders.Remove(header);

private class RequestHeader
{
    public string Name { get; set; }
    public string Value { get; set; }
} }
guardrex commented 4 years ago

the CORS port is the same the WebAssembly app is running.

Ok ... that's the main thing to check for CORS.

I can't repro anything wrong with the apps here, but I admit that I'm using the web API topic as it existed at the time that this topic was put up. It's possible that the docs team changed that app in the meantime. I'll need to research that further, so I'll try to take a look on Thursday.

Until then, my suggestion is for you to confirm manually with a tool like Postman or Fiddler if the web API is really running and responding properly. Then if the web API seems to be running and responding ok, you can take a look at the request from the Blazor app to see if it's making a valid request to the web API.

I'll get back to you on this as soon as possible ... I should have time tomorrow (Thursday) to look.

oridespavaneli commented 4 years ago

Thank you again. The WebAssembly sample has a lot of options. I've teste many of them, and they are OK. The last menu option is "Call a Web API". Every time I click on, appears the alert “An unhandled erros has ocorred. Reload”, at the page bottom. Despite this, the page is prepare for sending the request, by clicking on "Request". Maybe the problem is related with de alert error.

guardrex commented 4 years ago

I took a look at the current version of the web API topic relative to the sample app that I'm using from an earlier version of the topic. There are some minor changes, but nothing that looks like it would prevent the Blazor app from interacting with it. I ran the current WebAssembly sample app here with the web API app that I have, and it works. I can't repro any errors here.

General advice ...

I don't have anything actionable at this time. I can only react to a specific error with a known cause. If you find out the ultimate cause of the problem, post it back here.

guardrex commented 4 years ago

Re-opening to use the latest version of the web API example: confirm this guidance isn't broken or fix it.

17656

Rick-Anderson commented 4 years ago

I don't see any CORS errors in @oridespavaneli report. Take a look at https://github.com/dotnet/AspNetCore.Docs/pull/17345 which has a public preview and will merge in a couple days. That show how to test CORS

guardrex commented 4 years ago

Ok ... I guess we can wait and see. I don't want to work this unless I need to because I'm 🏃😅 with many high priority Blazor issues. If I get multiple reports of the web API being broken with the Blazor topic, then I'm sure that I'll need to take a fresh web API from the latest version of that tutorial and see if it breaks with the guidance here.