UriFormatException getting caught in exception handler for ASP .Net Core - asp.net-core

I've added exception handling to my Asp .Net Core application to log any unhandled exceptions. I am getting unhandled UriFormatExceptions within the middleware due to what appears to be traffic from various web crawlers targeting non-existent endpoints. I am not sure how to fix this, or at least elegantly silence it so it does not show up in my telemetry. In general, I'd like to ignore traffic hitting endpoints not defined by my application.
Exception logged
[Unhandled exception][UriString][https:///favicon.ico][Exception]
[System.UriFormatException: Invalid URI: The hostname could not be parsed.]
Stacktrace:
at System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind, UriCreationOptions& creationOptions)
at System.Uri..ctor(String uriString)
at Microsoft.AspNet.OData.Routing.ODataPathRouteConstraint.Match(HttpContext httpContext, IRouter route, String routeKey, RouteValueDictionary values, RouteDirection routeDirection)
at Microsoft.AspNetCore.Routing.RouteConstraintMatcher.Match(IDictionary`2 constraints, RouteValueDictionary routeValues, HttpContext httpContext, IRouter route, RouteDirection routeDirection, ILogger logger)
at Microsoft.AspNetCore.Routing.RouteBase.RouteAsync(RouteContext context)
at Microsoft.AspNetCore.Routing.RouteCollection.RouteAsync(RouteContext context)
at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)]
Exception Handler
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
IExceptionHandlerPathFeature exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();
string uriString = null;
uriString = context.Request.GetEncodedUrl();
Logger.EmitLog(
"Unhandled exception",
"UriString",
uriString,
"Exception",
exceptionHandlerPathFeature?.Error);
});
});

Related

Blazor WASM controller: read request body causes the IIS process to crash

So I am trying to simply read the body (with string content) in a Blazor WASM ApiController. My code on the server-side:
[AllowAnonymous]
[ApiController]
[Route("[controller]")]
public class SmartMeterDataController : ControllerBase
{
[HttpPost("UploadData")]
public async void UploadData()
{
string body = null;
if (Request.Body.CanRead && (Request.Method == HttpMethods.Post || Request.Method == HttpMethods.Put))
{
Request.EnableBuffering();
Request.Body.Position = 0;
body = await new StreamReader(Request.Body).ReadToEndAsync();
}
}
}
My app builder in Program.cs is pretty much out of the box:
//enable REST API controllers
var mvcBuillder = builder.Services.AddMvcCore(setupAction: options => options.EnableEndpointRouting = false).ConfigureApiBehaviorOptions(options => //activate MVC and configure error handling
{
options.InvalidModelStateResponseFactory = context => //error 400 (bad request)
{
JsonApiErrorHandler.HandleError400BadRequest(context);
return new Microsoft.AspNetCore.Mvc.BadRequestObjectResult(context.ModelState);
};
});
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
...
app.UseRouting();
app.UseMvcWithDefaultRoute();
app.MapRazorPages();
app.MapControllers();
The request body looks like this:
{"api_key":"K12345667565656", "field1":"1.10", "field2":"0.76",
"field3":"0.65", "field4":"455", "field5":"0", "field6":"1324",
"field7":"433761", "field8":"11815" }
Yes, this is JSON. No, I don't want to parse it with [FromBody] or similar.
POSTing to this endpoint causes the following exception (as seen in the Windows event viewer thingy):
Application: w3wp.exe
CoreCLR Version: 6.0.1222.56807
.NET Version: 6.0.12
Description: The process was terminated due to an unhandled exception.
Exception Info: System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'HttpRequestStream'.
at Microsoft.AspNetCore.Server.IIS.Core.HttpRequestStream.ValidateState(CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.IIS.Core.HttpRequestStream.ReadAsync(Memory`1 destination, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.IIS.Core.WrappingStream.ReadAsync(Memory`1 destination, CancellationToken cancellationToken)
at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
at System.IO.StreamReader.ReadBufferAsync(CancellationToken cancellationToken)
at System.IO.StreamReader.ReadToEndAsyncInternal()
After that, a second error is always logged. It states something like it is described here.
Note that it's usually not the first, but the second or third POST that causes this. After this, the error keeps happening with every POST and after a short while the application stops working and the Windows Server 2019 need to be rebooted.
According to the internet, the code should work. Anyone have a guess why it doesn't?
I use this HttpContext extension method to read the request body and cache it in the context in case needed later in the pipeline. It works for me.
Notice the condition around EnableBuffering. Perhaps adding that condition to your code will help.
public static async Task<string> GetRequestBodyAsStringAsync(
this HttpContext httpContext)
{
if (httpContext.Items.TryGetValue("BodyAsString", out object? value))
return (string)value!;
if (!httpContext.Request.Body.CanSeek)
{
// We only do this if the stream isn't *already* rewindable,
// as EnableBuffering will create a new stream instance
// each time it's called
httpContext.Request.EnableBuffering();
}
httpContext.Request.Body.Position = 0;
StreamReader reader = new(httpContext.Request.Body, Encoding.UTF8);
string bodyAsString = await reader.ReadToEndAsync().ConfigureAwait(false);
httpContext.Request.Body.Position = 0;
httpContext.Items["BodyAsString"] = bodyAsString;
return bodyAsString;
}
EDIT ...
Possibly, your issue could also be related to fact your controller method is returning a void instead of Task?
Finally, I found the original article I used for my extension method. Interestingly, if you that extension method for the FIRST time after model-binding then it won't work (in my project I do call it from middleware).
https://markb.uk/asp-net-core-read-raw-request-body-as-string.html
Adding:
public class EnableRequestBodyBufferingMiddleware
{
private readonly RequestDelegate _next;
public EnableRequestBodyBufferingMiddleware(RequestDelegate next) =>
_next = next;
public async Task InvokeAsync(HttpContext context)
{
context.Request.EnableBuffering();
await _next(context);
}
}
and
app.UseMiddleware<EnableRequestBodyBufferingMiddleware>();
may therefore also help.

How to authenticate the .NET core API using JWT access token with SSL enabled OpenID connect?

I've two separate .NET core API projects (OpenIDConnect and Features API) and deployed into IIS. OpenIDConnect is to authenticate the users with their credentials and it will issue accessToken if authentication succeeded. Features API will authenticate using the OpenIDConnect API with that accessToken.
services.AddAuthentication(options =>
{
options.DefaultChallengeScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
options.DefaultForbidScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
});
Everything works fine until enable mTLS in OpenIdConnect. That is, enabled SSL in OpenIdConnect API from IIS and so it requires client to include the certificate on their request.
OpenIdConnect API:
services
.AddAuthentication(o =>
{
o.DefaultScheme = CertificateAuthenticationDefaults.AuthenticationScheme;
})
.AddCertificate(options =>
{
options.AllowedCertificateTypes = CertificateTypes.All;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var claims = new[]
{
new Claim(
ClaimTypes.NameIdentifier,
context.ClientCertificate.Subject,
ClaimValueTypes.String, context.Options.ClaimsIssuer),
new Claim(
ClaimTypes.Name,
context.ClientCertificate.Subject,
ClaimValueTypes.String, context.Options.ClaimsIssuer)
};
context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
context.Success();
return Task.CompletedTask;
}
};
})
Once enabled it, I'm able to get accessToken from OpenIdConnect API by adding certificate in Postman.
But unable to access OpenIdConnect API from Features API even though added the client certificate into Features API project like below.
services.AddOpenIddict()
.AddServer(options =>
{
options.SetIssuer(new Uri(tokenServiceBaseUrl));
options.AddSigningCertificate(signingCertificate);
})
.AddValidation(options =>
{
options.SetIssuer(tokenServiceBaseUrl);
options.UseAspNetCore();
options.UseSystemNetHttp();
options.AddEncryptionCertificate(signingCertificate);
});
Getting below exception when trying to request from Postman:
System.InvalidOperationException: IDX20803: Unable to obtain
configuration from: 'System.String'. --->
System.Text.Json.JsonException: '<' is an invalid start of a value.
Path: $ | LineNumber: 0 | BytePositionInLine: 0. --->
System.Text.Json.JsonReaderException: '<' is an invalid start of a
value. LineNumber: 0 | BytePositionInLine: 0. at
System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader&
json, ExceptionResource resource, Byte nextByte, ReadOnlySpan1 bytes) at System.Text.Json.Utf8JsonReader.ConsumeValue(Byte marker) at System.Text.Json.Utf8JsonReader.ReadFirstToken(Byte first) at System.Text.Json.Utf8JsonReader.ReadSingleSegment() at System.Text.Json.Utf8JsonReader.Read() at System.Text.Json.Serialization.JsonConverter1.ReadCore(Utf8JsonReader&
reader, JsonSerializerOptions options, ReadStack& state) --- End of
inner exception stack trace --- at
System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state,
JsonReaderException ex) at
System.Text.Json.Serialization.JsonConverter1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) at System.Text.Json.Serialization.JsonConverter1.ReadCoreAsObject(Utf8JsonReader&
reader, JsonSerializerOptions options, ReadStack& state) at
System.Text.Json.JsonSerializer.ReadCore[TValue](JsonConverter
jsonConverter, Utf8JsonReader& reader, JsonSerializerOptions options,
ReadStack& state) at
System.Text.Json.JsonSerializer.ReadCore[TValue](JsonReaderState&
readerState, Boolean isFinalBlock, ReadOnlySpan1 buffer, JsonSerializerOptions options, ReadStack& state, JsonConverter converterBase) at System.Text.Json.JsonSerializer.ReadAsync[TValue](Stream utf8Json, Type returnType, JsonSerializerOptions options, CancellationToken cancellationToken) at System.Net.Http.Json.HttpContentJsonExtensions.ReadFromJsonAsyncCore[T](HttpContent content, Encoding sourceEncoding, JsonSerializerOptions options, CancellationToken cancellationToken) at OpenIddict.Validation.SystemNetHttp.OpenIddictValidationSystemNetHttpHandlers.ExtractJsonHttpResponse1.HandleAsync(TContext
context) at
OpenIddict.Validation.OpenIddictValidationDispatcher.DispatchAsync[TContext](TContext
context) at
OpenIddict.Validation.OpenIddictValidationDispatcher.DispatchAsync[TContext](TContext
context) at
OpenIddict.Validation.OpenIddictValidationService.<>c__DisplayClass2_0.<g__ExtractConfigurationResponseAsync|2>d.MoveNext()
--- End of stack trace from previous location --- at OpenIddict.Validation.OpenIddictValidationService.GetConfigurationAsync(Uri
address, CancellationToken cancellationToken) at
OpenIddict.Validation.OpenIddictValidationService.GetConfigurationAsync(Uri
address, CancellationToken cancellationToken) at
OpenIddict.Validation.OpenIddictValidationRetriever.Microsoft.IdentityModel.Protocols.IConfigurationRetriever<Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfiguration>.GetConfigurationAsync(String
address, IDocumentRetriever retriever, CancellationToken cancel) at
Microsoft.IdentityModel.Protocols.ConfigurationManager1.GetConfigurationAsync(CancellationToken cancel) --- End of inner exception stack trace --- at Microsoft.IdentityModel.Protocols.ConfigurationManager1.GetConfigurationAsync(CancellationToken
cancel) at
OpenIddict.Validation.OpenIddictValidationHandlers.ValidateIdentityModelToken.HandleAsync(ProcessAuthenticationContext
context) at
OpenIddict.Validation.OpenIddictValidationDispatcher.DispatchAsync[TContext](TContext
context) at
OpenIddict.Validation.OpenIddictValidationDispatcher.DispatchAsync[TContext](TContext
context) at
OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandler.HandleAuthenticateAsync()
at
Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.AuthenticateAsync()
at
Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext
context, String scheme) at
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext
context) at
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext
context)

Unhandled TaskCancelledException when request is aborted by client in ASP.NET Core MVC

ASP.NET Core MVC provides approach to handle situations when request is aborted by the client. Framework passes CancellationToken that can be accessed via HttpContext.RequestAborted property, or can be bound into controller's action.
In terms of .NET, this approach looks pretty clear, consistent and natural. What doesn't look natural and logical to me is that framework, which initializes, populates and 'cancels' this access token doesn't handle appropriate TaskCancelledException.
So, if
I create a new project from the "ASP.NET Core Web API" template,
Add an action with CancellationToken argument, something like this:
[HttpGet("Delay")]
public async Task<IActionResult> GetDelayAsync(CancellationToken cancellationToken)
{
await Task.Delay(30_000, cancellationToken);
return Ok();
}
And then send request via postman and cancel it before completion
Then the application records this error in the log:
fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HMCHB3SQHQQR", Request id "0HMCHB3SQHQQR:00000002": An unhandled exception was thrown by the application.
System.Threading.Tasks.TaskCanceledException: A task was canceled.
<<>>
My expectation is that exception in this particular case exception is handled and absorbed by asp.net, with no "fail" records in logs.
Error-wise behavior should be the same as with synchronous action:
[HttpGet("Delay")]
public IActionResult GetDelay()
{
Thread.Sleep(30_000);
return Ok();
}
This implementation doesn't record any errors in logs when request is aborted.
Technically exception can be absorbed and hided by exception filter, but this approach looks weird and overcomplicated. At least because this is routine situation, and writing code for any application doesn't make any sense. Also, I want to hide "exception caused by aborted request when client isn't interested in response" and behavior related to other unhandled TaskCancelledException should remain as is...
I'm wondering how and when it's supposed to properly handle and absorb exception when request is aborted by client?
There are number of articles how to access cancellation token, however I was unable to find any explicit statement that answers my question.
From https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-cancellation:
If you are waiting on a Task that transitions to the Canceled state, a
System.Threading.Tasks.TaskCanceledException exception (wrapped in an
AggregateException exception) is thrown. Note that this exception
indicates successful cancellation instead of a faulty situation.
Therefore, the task's Exception property returns null.
That's why this block does not throw (there's no task awaited that is tied to a cancellation token):
[HttpGet("Delay")]
public IActionResult GetDelay(CancellationToken cancellationToken)
{
Thread.Sleep(30_000);
return Ok();
}
I stumbled upon the same issue you described in your post. Genuinely speaking, middleware might not be the worst approach. I found good example in Ocelot API gateway on Github.
Pay attention it will return HTTP 499 Client Closed Request afterwards.
You may modify it in way that no logs will be written.
/// <summary>
/// Catches all unhandled exceptions thrown by middleware, logs and returns a 500.
/// </summary>
public class ExceptionHandlerMiddleware : OcelotMiddleware
{
private readonly RequestDelegate _next;
private readonly IRequestScopedDataRepository _repo;
public ExceptionHandlerMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IRequestScopedDataRepository repo)
: base(loggerFactory.CreateLogger<ExceptionHandlerMiddleware>())
{
_next = next;
_repo = repo;
}
public async Task Invoke(HttpContext httpContext)
{
try
{
httpContext.RequestAborted.ThrowIfCancellationRequested();
var internalConfiguration = httpContext.Items.IInternalConfiguration();
TrySetGlobalRequestId(httpContext, internalConfiguration);
Logger.LogDebug("ocelot pipeline started");
await _next.Invoke(httpContext);
}
catch (OperationCanceledException) when (httpContext.RequestAborted.IsCancellationRequested)
{
Logger.LogDebug("operation canceled");
if (!httpContext.Response.HasStarted)
{
httpContext.Response.StatusCode = 499;
}
}
catch (Exception e)
{
Logger.LogDebug("error calling middleware");
var message = CreateMessage(httpContext, e);
Logger.LogError(message, e);
SetInternalServerErrorOnResponse(httpContext);
}
Logger.LogDebug("ocelot pipeline finished");
}
private void TrySetGlobalRequestId(HttpContext httpContext, IInternalConfiguration configuration)
{
var key = configuration.RequestId;
if (!string.IsNullOrEmpty(key) && httpContext.Request.Headers.TryGetValue(key, out var upstreamRequestIds))
{
httpContext.TraceIdentifier = upstreamRequestIds.First();
}
_repo.Add("RequestId", httpContext.TraceIdentifier);
}
private void SetInternalServerErrorOnResponse(HttpContext httpContext)
{
if (!httpContext.Response.HasStarted)
{
httpContext.Response.StatusCode = 500;
}
}
private string CreateMessage(HttpContext httpContext, Exception e)
{
var message =
$"Exception caught in global error handler, exception message: {e.Message}, exception stack: {e.StackTrace}";
if (e.InnerException != null)
{
message =
$"{message}, inner exception message {e.InnerException.Message}, inner exception stack {e.InnerException.StackTrace}";
}
return $"{message} RequestId: {httpContext.TraceIdentifier}";
}
}
If you use multiple middlewares it should be first on the invocation list (It's .NET 6)
app.UseMiddleware(typeof(ExceptionHandlerMiddleware));
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

How to resolve "TypeError: Failed to fetch" error on Blazor?

I am trying to send an HTTP request from my Blazor app to my ASP.NET Core API. I have breakpoints everywhere. The application gives an exception right after the action method on the API controller returns. I am familiar with .NET overall, however, I could not decipher the error message.
Blazor http call:
var response = await _httpClient.GetStreamAsync($"Customer/GetAllCustomers");
ASP.NET Core API Controller action:
[HttpGet]
[Route("GetAllCustomers")]
public async Task<IEnumerable<Customer>> GetAllCustomersAsync()
{
return await _service.GetAllCustomersAsync();
}
Error stack:
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: TypeError: Failed to fetch
WebAssembly.JSException: TypeError: Failed to fetch
at System.Net.Http.WebAssemblyHttpHandler.doFetch (System.Threading.Tasks.TaskCompletionSource`1[TResult] tcs, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x301ab40 + 0x00a30> in <filename unknown>:0
at System.Net.Http.WebAssemblyHttpHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x2ff3590 + 0x00174> in <filename unknown>:0
at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x2ff1e98 + 0x00160> in <filename unknown>:0
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x2fc8a98 + 0x00182> in <filename unknown>:0
at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered (System.Threading.Tasks.Task`1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) <0x301ff08 + 0x00134> in <filename unknown>:0
at System.Net.Http.HttpClient.FinishGetStreamAsync (System.Threading.Tasks.Task`1[TResult] getTask) <0x2ffa720 + 0x000cc> in <filename unknown>:0
at WebClient.Services.LectureVideoService.GetAllLectureVideos (Content.Data.Enums.LessonType lessonType) [0x00040] in D:\AlbidersSoftware\CSharp\Albiders Content MS\Albiders\WebClient\Services\LectureVideoService.cs:21
at WebClient.Pages.MathAccList.OnInitializedAsync () [0x00026] in D:\AlbidersSoftware\CSharp\Albiders Content MS\Albiders\WebClient\Pages\MathAccList.razor:18
at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync () <0x2bed718 + 0x0013a> in <filename unknown>:0
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask (System.Threading.Tasks.Task taskToHandle) <0x2e3a0b0 + 0x000b6> in <filename unknown>:0
Unhandled exception rendering component: TypeError: Failed to fetch
WebAssembly.JSException: TypeError: Failed to fetch
I did a test to make request(s) from my Blazor WebAssembly app to action method with testing data, which works well on my side.
[Route("[controller]")]
[ApiController]
public class CustomerController : ControllerBase
{
[HttpGet]
[Route("GetAllCustomers")]
public async Task<IEnumerable<Customer>> GetAllCustomersAsync()
{
//for testing purpose
return new List<Customer> { new Customer { Id = 1, Name = "Test1" }, new Customer { Id = 2, Name = "Test2" } };
//return await _service.GetAllCustomersAsync();
}
}
GetAllCustomers.razor
#page "/getallcustomers"
#using BlazorWasmApp1.Shared
#inject HttpClient _httpClient
<h3>Customer List</h3>
#if (customers == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
</thead>
<tbody>
#foreach (var customer in customers)
{
<tr>
<td>#customer.Id</td>
<td>#customer.Name</td>
</tr>
}
</tbody>
</table>
}
#code {
private Customer[] customers;
protected override async Task OnInitializedAsync()
{
//call external API
//_httpClient.BaseAddress = new Uri("https://localhost:44312/");
//your API action would return a collection of Customer
//you can try to call .GetFromJsonAsync<Customer[]>() to get the expected data
//rather than get stream
customers = await _httpClient.GetFromJsonAsync<Customer[]>($"Customer/GetAllCustomers");
}
}
Test Result
To troubleshoot the issue, please try:
check the URL of your request in browser developer tool Network tab, and make sure you are making request to correct endpoint
if your ASP.NET Core Web API project is hosting on separate site, please make sure you configured and enabled CORS to allow request(s) from your Blazor WebAssembly app, and make sure that API is running
Also check if your api is requesting from HTTP, change it to https: it will work
EDIT NOTE: I just realised that my original answer was a red-herring, so I have deleted it and replaced it. The actual cause of my error is quite different to your question, but I'll provide a summary of resolution here because it was the first post that came up with this error message and it might help someone else troubleshoot it.
I am building a Web Assembly Hosted project, but with multiple clients and have been trialing combining AddMicrosoftIdentityWebApi and AddMicrosoftIdentityWebApp in my Server project as per following.
Program.cs (Server Project)
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
Everything worked fine with just AddMicrosoftIdentityWebApi. However, when I added AddMicrosoftIdentityWebApp and tried to call my api from the client, I got the error that you identified.
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: TypeError: Failed to fetch
System.Net.Http.HttpRequestException: TypeError: Failed to fetch
---> System.Runtime.InteropServices.JavaScript.JSException: TypeError: Failed to fetch
at System.Net.Http.BrowserHttpHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.BrowserHttpHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Components.WebAssembly.Authentication.AuthorizationMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
at System.Net.Http.Json.HttpClientJsonExtensions.<GetFromJsonAsyncCore>d__13`1[[BlazorApp14.Shared.WeatherForecast[], BlazorApp14.Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext()
at BlazorApp14.Client.Pages.FetchData.OnInitializedAsync() in C:\Temp\BlazorApp14\BlazorApp14\Client\Pages\FetchData.razor:line 50
at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
If I had looked more closely, I would have seen that my error is not quite the same as yours, but anyway.....
I then noticed that there was an additional error showing in Dev Tools on the browser.
Access to fetch at 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=0e5f2876-c...................
redirected from 'https://localhost:5001/WeatherForecast') from origin 'https://localhost:5001' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
This sent me down another rabbit-hole and I setup Cors etc. on my WebApi project, which did not help.
I then thought that perhaps using the same registered AzureAD application in the AddMicrosoftIdentityWebApi and AddMicrosoftIdentityWebApp calls. However, this was not the problem either.
It turns out that the [Authorize] attribute on my controller couldn't choose which Authentication scheme to use and chose the "wrong" one. The simple fix to my error was to explicitly call out the Authentication Scheme in the Authorize attribute as shown following.
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}

Kestrel error when sending a request from an emulator

When making a request to Kestrel via Fiddler, the following request succeeds.
GET http://192.168.1.148:5000/ HTTP/1.1
Host: 192.168.1.148:5000
Connection: keep-alive
When making the request thru the NETMF Emulator, the following request fails.
GET http://192.168.1.148:5000 HTTP/1.1
Host: 192.168.1.148:5000
Connection: keep-alive
This is the ASP.NET Core error message. The error appears to be about logging!
Request finished in 24788.6203ms 500
fail: Microsoft.AspNet.Server.Kestrel[13]
An unhandled exception was thrown by the application.
System.AggregateException: An error occurred while writing to logger(s).
---> System.ArgumentException: Parameter name: value
at Microsoft.AspNet.Http.PathString..ctor(String value)
at Microsoft.AspNet.Http.Internal.DefaultHttpRequest.get_Path()
at Microsoft.AspNet.Hosting.Internal.HostingLoggerExtensions
.HostingRequestStarting.ToString()
at Microsoft.Extensions.Logging.Console.ConsoleLogger.Log(
LogLevel logLevel, Int32 eventId, Object state,
Exception exception, Func`3 formatter)
at Microsoft.Extensions.Logging.Logger.Log(
LogLevel logLevel, Int32 eventId, Object state,
Exception exception, Func`3 formatter)
--- End of inner exception stack trace ---
at Microsoft.Extensions.Logging.Logger.Log(
LogLevel logLevel, Int32 eventId, Object state,
Exception exception, Func`3 formatter)
at Microsoft.AspNet.Hosting.Internal.HostingLoggerExtensions
.RequestStarting(ILogger logger, HttpContext httpContext)
at Microsoft.AspNet.Hosting.Internal.HostingEngine
.<>c__DisplayClass32_0.<<Start>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter
.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Server.Kestrel.Http.Frame
.<RequestProcessingAsync>d__79.MoveNext()
---> (Inner Exception #0) System.ArgumentException: Parameter name: value
at Microsoft.AspNet.Http.PathString..ctor(String value)
at Microsoft.AspNet.Http.Internal.DefaultHttpRequest.get_Path()
at Microsoft.AspNet.Hosting.Internal.HostingLoggerExtensions
.HostingRequestStarting.ToString()
at Microsoft.Extensions.Logging.Console.ConsoleLogger.Log(
LogLevel logLevel, Int32 eventId, Object state,
Exception exception, Func`3 formatter)
at Microsoft.Extensions.Logging.Logger.Log(
LogLevel logLevel, Int32 eventId, Object state,
Exception exception, Func`3 formatter)<---
This is the entire ASP.NET Core program.
using System;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.Extensions.Logging;
namespace EmptyApplication01
{
public class Startup
{
public void Configure(
IApplicationBuilder app,
ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(minLevel: LogLevel.Verbose);
app.Run(async (context) =>
{
var logger = loggerFactory.CreateLogger("CatchAll");
logger.LogInformation(DateTime.Now.ToString());
await context.Response.WriteAsync("head, body");
});
}
}
}
I have figured out. Kestrel will break if the AbsoluteURL is in the path. in order to make it work this is the work around
app.Use(next => async context => {
// get the frame from kestrel and then but the path by removing the hostname
var Frame = (Microsoft.AspNet.Server.Kestrel.Http.Frame)context.Features;
var newpath = Frame.RequestUri.Replace("http://" + context.Request.Host.Value, "");
context.Request.Path = newpath;
// Invoke the rest of the pipeline.
await next(context);
});
Turning off logging fixed the problem. This is probably a bug in the ASP.NET logging implementation.
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using System;
namespace EmptyApplication01
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Run(async (context) =>
{
// no more logging!
System.Console.WriteLine(DateTime.Now.ToString());
await context.Response.WriteAsync("head, body");
});
}
}
}