Build and then Create a mock with AutoNSubstitute gives ObjectCreationException - nsubstitute

I'm trying to create a mock object using AutoNSubstitute but I keep getting the following error:
AutoFixture.ObjectCreationException:
Message=The decorated ISpecimenBuilder could not create a specimen based on the request: MySpace.IEntity. This can happen if the request represents an interface or abstract class; if this is the case, register an ISpecimenBuilder that can create specimens based on the request. If this happens in a strongly typed Build<T> expression, try supplying a factory using one of the IFactoryComposer<T> methods.
The stripped down code I use that crashes is
IFixture _fixture = new Fixture().Customize(new AutoNSubstituteCustomization() { ConfigureMembers = true });
var mock = _fixture.Build<IEntity>().Create();
The target is
var mockCol = _fixture.Build<IEntity>().With(e => e.Code, "0").CreateMany();
I also tried these that do work with the las 2 havin their properties correctly filled
var test1 = _fixture.Build<IEntity>();
var test2 = _fixture.Build<IEntity>().With(e => e.Code, "0");
var test3 = _fixture.Create<IEntity>();
var test4 = _fixture.CreateMany<IEntity>();
Stack of the exception
at AutoFixture.Kernel.NoSpecimenOutputGuard.Create(Object request, ISpecimenContext context)
at AutoFixture.Kernel.Postprocessor`1.Create(Object request, ISpecimenContext context)
at AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object request, ISpecimenContext context)
at AutoFixture.Kernel.FilteringSpecimenBuilder.Create(Object request, ISpecimenContext context)
at AutoFixture.Dsl.NodeComposer`1.Create(Object request, ISpecimenContext context)
at AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object request, ISpecimenContext context)
at AutoFixture.Kernel.RecursionGuard.Create(Object request, ISpecimenContext context)
at AutoFixture.Dsl.CompositeNodeComposer`1.Create(Object request, ISpecimenContext context)
at AutoFixture.Kernel.SpecimenContext.Resolve(Object request)
at AutoFixture.Kernel.SeedIgnoringRelay.Create(Object request, ISpecimenContext context)
at AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object request, ISpecimenContext context)
at AutoFixture.Kernel.FilteringSpecimenBuilder.Create(Object request, ISpecimenContext context)
at AutoFixture.Dsl.NodeComposer`1.Create(Object request, ISpecimenContext context)
at AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object request, ISpecimenContext context)
at AutoFixture.Kernel.RecursionGuard.Create(Object request, ISpecimenContext context)
at AutoFixture.Dsl.CompositeNodeComposer`1.Create(Object request, ISpecimenContext context)
at AutoFixture.Kernel.SpecimenContext.Resolve(Object request)
at AutoFixture.SpecimenFactory.Create[T](ISpecimenContext context)
at AutoFixture.SpecimenFactory.Create[T](ISpecimenBuilder builder)
at AutoFixture.SpecimenFactory.Create[T](IPostprocessComposer`1 composer)
at MySpace.Tests.MyTest()

Related

UriFormatException getting caught in exception handler for 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);
});
});

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)

ASP.Net Core ODataController Attribute Routing throws 500 error during insert

I am trying to implement Create and update logic on my ODataController on ASP.Net core 5.0 using 8.0.0-beta. My controller looks like this:
[HttpPost]
[Route("users/{key}/{operation}")]
public async Task<IActionResult> UpdateUser([FromODataUri] string key, [FromODataUri] string operation, [FromBody] Delta<User> userPatch)
{
IActionResult result = BadRequest($"Key:{key} / Operation:{operation.ToLower()}");
if (operation.ToLower() == "update")
{
result = await Patch(key, userPatch);
return result;
}
else if (operation.ToLower() == "create")
{
var newUser = new User();
userPatch.Patch(newUser);
var entity = await AddNewEntity(newUser, key);
result = Created(entity);
}
return result;
}
The update is working fine, I am able to verify using the REST client(http://localhost:5000/users/MY-USER-6/update)
However when I call create method using the REST client (http://localhost:5000/users/MY-USER-6/create) I see that the method works and data is inserted in the database and function exits without an issue, however the result I get on the REST client the response says Status Code: 500 Internal Server Error with the following exception:
System.InvalidOperationException: The request must have an associated EDM model. Consider using the extension method HttpConfiguration.MapODataServiceRoute to register a route that parses the OData URI and attaches the model information.
at Microsoft.AspNetCore.OData.Results.ResultHelpers.GenerateODataLink(HttpRequest request, Object entity, Boolean isEntityId)
at Microsoft.AspNetCore.OData.Results.CreatedODataResult`1.GenerateLocationHeader(HttpRequest request)
at Microsoft.AspNetCore.OData.Results.CreatedODataResult`1.ExecuteResultAsync(ActionContext context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
BTW if I change result = Created(entity); to result = Ok(entity); its working fine!
As per some blogs and channel9 videos I am trying to add endpoints.EnableDependencyInjection(); but looks like this is no longer available or may be valid in the 8.0.0-beta.
What am I missing in this?

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();
}
}

Can I pass CancellationToken as parameter in AspNetCore WebAPI

As far as I know if I use services.AddControllers() or services.AddMvc()extension in my Startup.cs "MVC will automatically bind any CancellationToken parameters in an action method.
I have the following TestController and TestService as Transient service.
According to this informations, when the auto-binded CancellationToken IsCancellationRequested will the tokens that I have passed as parameters be also canceled?
public class TestController : ControllerBase
{
private readonly ITestService _testService;
public TestController(ITestService testService)
{
_testService = testService;
}
[HttpGet, ActionName("Get")]
public async Task<IActionResult> GetAsync(CancellationToken cancellationToken)
{
await _testService.GetAsync(cancellationToken);
return Ok();
}
}
public class TestService : ITestService
{
public async Task GetAsync(CancellationToken cancellationToken)
{
//I also send the cancellationToken as a parameter to external API calls
}
}
when the auto-binded CancellationToken IsCancellationRequested will
the tokens that I have passed as parameters be also canceled?
By injecting a CancellationToken into your action method, which will be automatically bound to the HttpContext.RequestAborted token for the request. After the request is cancelled by the user refreshing the browser or click the "stop" button, the original request is aborted with a TaskCancelledException which propagates back through the MVC filter pipeline, and back up the middleware pipeline. Then, You could check the value of IsCancellationRequested and exit the action gracefully. In this scenario, there is no need to transfer the CancellationToken as parameters.
If you want to Cancel an Async task, since, CancellationTokens are lightweight objects that are created by a CancellationTokenSource. When a CancellationTokenSource is cancelled, it notifies all the consumers of the CancellationToken. So, you could call the Cancel() method to cancel the task.
Check the following sample:
var cts = new CancellationTokenSource();
//cts.CancelAfter(5000);//Request Cancel after 5 seconds
_logger.LogInformation("New Test start");
var newTask = Task.Factory.StartNew(state =>
{
int i = 1;
var token = (System.Threading.CancellationToken)state;
while (true)
{
Console.WriteLine(i);
i++;
if (i == 10)
{
cts.Cancel(); //call the Cancel method to cancel.
}
_logger.LogInformation("thread running " + DateTime.Now.ToString());
Thread.Sleep(1000);
token.ThrowIfCancellationRequested();
}
}, cts.Token, cts.Token);
try
{
newTask.Wait(10000);
}
catch
{
Console.WriteLine("Catch:" + newTask.Status);
}
_logger.LogInformation("Test end");
More detail information about using CancellationToken, please check the following articles:
Using CancellationTokens in ASP.NET Core MVC controllers