Azure Application Insight wrong shows URL in logs for ASP.NET Core 6 Web API application with API versioning - asp.net-core

I have an ASP.NET Web API application running on .NET 4.8. In this app, I'm using standard Microsoft API versioning from Microsoft.AspNet.WebApi.Versioning and Microsoft.AspNet.WebApi.Versioning.ApiExplorer.
For instance:
[ApiVersionExtended(SupportedApiVersions.V9)]
[RoutePrefix("v{version:apiVersion}/telemetry")]
public sealed class TelemetryController : ApiController
{
where ApiVersionExtended - my filter. In Azure Application Insight requests to my API are shown with the correct version. For instance:
But after migration to .NET 6, I lost the correct version number in AI logs, for instance:
My code has several changes after migration to .NET 6
[ApiController]
[AllowAnonymous]
[ApiVersionExtended]
[Route("v{version:apiVersion}/telemetry")]
public sealed class TelemetryController : ApiController
{
[HttpGet("ipInfo")]
public async Task<IActionResult> GetIpInfoAsync(CancellationToken cancellationToken)
{
/* some code here */
}
}
I can't find the analog [RoutePrefix] attribute in .NET 6.
Might someone know what the reason for this issue is? And how I can fix it?

As suggested by #Peter Bons, this can be the issue with your existing Nuget Package.
The Nuget Package required to Implement API Versioning in .NET 6 Core Web API is Microsoft.AspNetCore.Mvc.Versioning
Install Microsoft.AspNetCore.Mvc.Versioning Nuget Package
In Program.cs add the below services
To add versioning for WebAPI
builder.Services.AddApiVersioning(opt =>
{
opt.DefaultApiVersion = new Microsoft.AspNetCore.Mvc.ApiVersion(2, 0);
opt.AssumeDefaultVersionWhenUnspecified = true;
opt.ReportApiVersions = true;
opt.ApiVersionReader = ApiVersionReader.Combine(new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("x-api-version"),
new MediaTypeApiVersionReader("x-api-version"));
});
To Add versioning with Swagger, add the below services
builder.Services.AddSwaggerGen(Options => Options.SwaggerDoc("v1", new OpenApiInfo { Title = "Audit Self Serve platform", Version = "v1" }));
Use MapToApiVersion attribute to assign each action to a distinct version
[MapToApiVersion("1.0")]
[HttpGet]
OutPut:

Related

Add Blazor Client to ASP .Net Core Web API (.NET 6)

I was wondering if there was a specific way to add a Blazor front end to an ASP .NET Core project. I know when creating an Blazor WebAssembly App there is the option to select ASP.Net Core Hosted and it would create a template, but I would like to add the client to the host manually.
Specifically, I've been looking at this ASP.Net to ASP.Net Core tutorial here and would like to add a Blazor front-end to the ASP.NET Core Web API project that is created.
Sorry in advance if the question isn't clear, this is my first time doing this sort of project.
If you want to add the API to the host manually you can create an ASP.NET Core Web API project. Create your post/get/ect controllers in the Web API, and in the Blazor Project inject an HttpClient with a BaseUri that points at your Web API controllers.
This is how I set mine up too. I keep all my components and services separate in class library and razor library projects. Let me know if you need further assistance.
Update
Seems like their might be some confusion on the project layout. For this example I named my Blazor Server App BlazorServerFrontEnd. I named my ASP.NET Core Web API BlazorServerBackEnd. I created mine on .NET Core 6.0. It doesn't use a Startup.cs like 3.1. Instead everything is done in the Program.cs file.
BlazorServerBackEnd (Web Api) will not have services.AddServerSideBlazor() like our BlazorServerFrontEnd (Blazor Server App). If you're seeing this in both projects you probably selected the wrong type. When you run BlazorServerBackEnd (Web Api) you'll see it has one controller built in (Controllers/WeatherForecastController.cs).
BlazorServerFrontEnd (Blazor Server App) will have services.AddServerSideBlazor(). You'll see in Data folder there is also a WeatherForecatService.cs. When you look at the service it doesn't connect to any Api. The service just grabs the data from a hardcoded readonly string[].
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(Name = "GetWeatherForecast")]
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();
}
}
This is what we need to change. Instead of the data being fetched from within BlazorServerFrontEnd (Blazor Server App) we want to connect to our BlazorServerBackEnd (Web Api). Open up Program.cs in the BlazorServerFrontEnd (Blazor Server App) and change the injected WeatherForecastService from a Singleton to an HttpClient.
//builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddHttpClient<WeatherForecastService>(client =>
{
// Path pointing to BlazorServerBackEnd (Web Api) baseUri
client.BaseAddress = new Uri("https://localhost:7246/");
});
Open up WeatherForecastService.cs in the BlazorServerFrontEnd (Blazor Server App) and add a constructor to inject our HttpClient that we just setup. We'll use the HttpClient to connect to our BlazorServerBackEnd (Web Api) using _httpClient.GetAsync("WeatherForecast"). Now it looks like this;
public class WeatherForecastService
{
private readonly HttpClient _httpClient;
public WeatherForecastService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
var response = await _httpClient.GetAsync("WeatherForecast");
if (response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync();
WeatherForecast[] forecasts = JsonConvert.DeserializeObject<WeatherForecast[]>(json);
return forecasts;
}
return null;
}
}
Please keep in mind this is a very brief example to get you on your feet. Ideally you'll want to have an interface for your services, and you'll want to inject the interface into your pages instead of the class. So in the real world it would look more like this;
builder.Services.AddHttpClient<IWeatherForecastService, WeatherForecastService>(client =>
{
// Path pointing to BlazorServerBackEnd (Web Api) baseUri
client.BaseAddress = new Uri("https://localhost:7246/");
});
And you would be injecting your interface into the page like so;
#inject IWeatherForecastService ForecastService
I also recommend separating components, services, controllers, models ect. into seperate razor/class libraries. Keep everything very loosely coupled. This will make it easy for you to port your application over to something like a MAUI or Windows Forms App BlazorWebView.
P.S. Swagger comes built into the Web Api project. It is very useful when developing and debugging. I would recommend familiarizing yourself with it if your new.

.NET 6 IHubContext Dependency Injection

I'm working on a simple .NET 6 application to enable data update notifications in our front-end application. I've built something similar in .NET 5 before, but I'm running across a DI issue that's got me stumped. In 5, all hubs that were mapped automatically have an IHubContext that is set up in the container for them as well. That doesn't appear to be the case anymore in 6.
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNet.SignalR.IHubContext`1[SignalRNotifications.Hubs.NotificationHub]' while attempting to activate 'SignalRNotifications.Controllers.NotificationController'.
The new non-startup DI in 6 looks weird to me, but I'm just not seeing anything available that says how to fix it. Any suggestions on how to get an IHubContext to inject into my controller?
Thanks!
Update: Here is some pertinent code:
using Microsoft.AspNetCore.Builder;
using SignalRNotifications.Hubs;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddSignalR().AddAzureSignalR();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<NotificationHub>("/NotificationHub");
});
app.Run();
Dependency injection is done in the controller in the most predictable of ways:
namespace SignalRNotifications.Controllers
{
[AllowAnonymous]
[Route("api/[controller]")]
[ApiController]
public class NotificationController : ControllerBase
{
private readonly IHubContext<NotificationHub> _notificationContext;
public NotificationController(IHubContext<NotificationHub> notificationContext)
{
_notificationContext = notificationContext;
}
System.InvalidOperationException: Unable to resolve service for type
'Microsoft.AspNet.SignalR.IHubContext`1[SignalRNotifications.Hubs.NotificationHub]'
while attempting to activate
'SignalRNotifications.Controllers.NotificationController'.
The issue might be related to you having installed the wrong version of SignalR and adding the wrong namespace reference. You are using Microsoft.AspNet.SignalR.IHubContext, instead of Microsoft.AspNetCore.SignalR.IHubContext.
According to your code and refer to the Asp.net Core SignalR document, I create a sample and inject an instance of IHubContext in a controller, everything works well. But I notice that when using the IHubContext, we need to add the using Microsoft.AspNetCore.SignalR; namespace, like this:
So, please check your code and try to use:
using Microsoft.AspNetCore.SignalR;

Asp.Net Core 3.1 Web Application, Api page not found issue

My Environment Windows 10. Visual Studio 2019 Professional, Asp.Net Core 3.1
I am following a Pluralsight course to teach myself Asp.Net Core 3.1. Following the instructor, I have created the web application. Everything goes well until the instructor adds an api controller to the application. It works for him but not for me.
Here's my api controller
namespace OdeToFood.Api
{
[Route("api/[controller]")]
[ApiController]
public class RestaurantsController : ControllerBase
{
private readonly OdeToFoodDbContext _context;
public RestaurantsController(OdeToFoodDbContext context)
{
_context = context;
}
// GET: api/Restaurants
[HttpGet]
public async Task<ActionResult<IEnumerable<Restaurant>>> GetRestaurants()
{
return await _context.Restaurants.ToListAsync();
}
// GET: api/Restaurants/5
[HttpGet("{id}")]
public async Task<ActionResult<Restaurant>> GetRestaurant(int id)
{
var restaurant = await _context.Restaurants.FindAsync(id);
if (restaurant == null)
{
return NotFound();
}
return restaurant;
}
. . . . .
Here's my project structure and hierarchy.
When I rebuild my project, and call the app from local IIS Express, i.e. https://localhost:44351/ It loads fine. I can interact fully, browse and CRUD entities. However when I point to any api url, e.g. https://localhost:44351/api/Restaurants or https://localhost:44351/api/Restaurants/2 I get "This localhost page can’t be found". The api simply does not load or respond in any way.
I am familiar with MVC5 where, when creating a new project, in the create project wizard scaffolding, you could check a box to add api functionality. I am not seeing this in VS2019 for Asp.Net Core 3.1 We Apps.
I promise you have have done my homework before asking this question here. I have googled to death. I have seen MS articles on core 3.1 breaking changes. I have looked at online project templates. I have searched stackoverflow. Maybe my search terms are flawed and I'm simply missing something simple.
Questions:
Why is the api shown above not loading?
Is there a way to add api functionality to an existing Asp.Net Core 3.1 Web Application or do I need to create a separate api project?
Is there a way to create a new Asp.Net Core 3.1 Web Application with api functionality included?
My thanks in advance
Kieran
If you'd like to add web APIs feature into an existing Razor Pages project, you need to do additional configuration, like below.
public void ConfigureServices(IServiceCollection services)
{
//add services for controllers
services.AddControllers();
services.AddRazorPages();
//...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//...
app.UseRouting();
//...
app.UseEndpoints(endpoints =>
{
//add endpoints for controller actions
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
Testing code of controller and action(s)
[Route("api/[controller]")]
[ApiController]
public class RestaurantsController : ControllerBase
{
public IActionResult GetRestaurants()
{
return Ok("Restaurants List Here");
}
}
Test Result

How can I host ASP.NET API and Blazor Web Assembly like an JavaScript-SPA?

Context:
We want to create a Single Page Application that runs with Blazor WebAssembly on the client-side. On the server-side, the solution has an ASP.NET MVC which includes some ApiController classes for our REST APIs.
We want to use ASP.NET API on the server-side instead of Blazor Server because we want to provide a REST interface with ApiController classes for unknown consumers.
Here is my client-side (Blazor WebAssembly) and server-side (ASP.NET API) project in a single solution:
A first try to request the API via Blazor´s HttpClient-class in our FetchData-component:
#inject HttpClient Http
...
#code {
private TodoItem[] TodoItems;
protected override async Task OnInitializedAsync()
{
TodoItems = await Http.GetJsonAsync<TodoItem[]>("api/ToDo");
}
}
On server-side the API-Controller looks like:
namespace ToDoListAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
[Produces("application/json")]
public class ToDoController : ControllerBase
{
[HttpGet]
public string IGetAll()
{
var lResult = new List<ToDoList.TodoItem>();
// create dummies
for (var i = 0; i < 10; i++)
{
lResult.Add(new ToDoList.TodoItem() { Title = $"Title {i}", IsDone = false });
}
return JsonSerializer.Serialize(lResult);
}
}
}
Problem: In my Blazor WebAssembly Project the request to the API fails. The Blazor WebAssembly Project is hosted via https://localhost:44340/ and the API is hosted via https://localhost:44349/. How can I host both projects together as I would it do with a JavaScript Framework?
You can either, depending on how you want to host and deploy your solution :
API and application in 2 different hosts
Enable CORS in the API project Startup class :
public void Configure(IApplicationBuilder app)
{
...
app.UseCors(configure =>
{
// configure here your CORS rule
}
...
}
All in one host
In your API project
add a package reference to Microsoft.AspNetCore.Components.WebAssembly.Server
Setup the blazor server in your Startup class
public void Configure(IApplicationBuilder app)
{
app.UseBlazorFrameworkFiles();
...
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapFallbackToFile("index.html");
});
}
You can create a sample solution with : dotnet new blazorwasm --hosted. It'll create a solution with a Blazor wasm project and a host.
Docs
With the latest update to the templates dotnet new -i Microsoft.AspNetCore.Components.WebAssembly.Templates::3.2.0-preview2.20160.5
You can create a Blazor WebAssembly app setup with authentication using ASP.NET Core Identity and IdentityServer by running the following command:
dotnet new blazorwasm --hosted --auth Individual -o BlazorAppWithAuth1
This creates:
Client Side Blazor
A single Project that can be used for MVC, API and razor pages, that contains an "inline" IdentityServer which can be used to secure the API calls
I was stuck on how to have IS4 in the same project as the APi (it's a small project and a independently hosted IDP would be overkill and I just want to deploy one thing) but this template shows how.
source: https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-preview-2-release-now-available/

Can we Host ASP.NET SignalR v2.4.1 in an ASP.NETCORE App?

I have a situation where my codebase is stuck in .Net 4.7.2 for now but I need to push some notifications on a Website which is built on Asp.Core 2.2.
Across the system we use SignalR 2.4.1 but it is completely re-written in .Net Core.
I tried hosting it in the same app without success. Owin does not seem to be happy.
Has anyone had any success with it or has any suggestion?
There has to be a way for projects migrating from .Net to Core.
Thanks
Ok so after along night I got a solution to this issue.
First just to make my setup clear.
There is an API project targetting .Net 4.7.2 which is broadcasting some messages via a SignalR 2.4.1 Hub.
There are some other Asp.Net 4.7.2 Projects consuming those Hubs which are working fine.
And also there is a new website build in .Net Core but targetting 4.7.2 framework.
The solution I ended up is essentially hosting an OWIN pipeline within the AspCore Pipeline.
First I needed to install the following packages:
Microsoft.Owin
Microsoft.AspNetCore.Owin
I also added a new extension method for the Core IApplicationBuilder interface that sets up OWIN on the same pipeline:
public static class OwinExtensions
{
public static IApplicationBuilder UseOwinApp(this IApplicationBuilder app, Action<IAppBuilder> configuration)
{
return app.UseOwin(setup => setup(next =>
{
IAppBuilder owinApp = new AppBuilder();
var aspNetCoreLifetime = (IApplicationLifetime)app.ApplicationServices.GetService(typeof(IApplicationLifetime));
var owinAppProperties = new AppProperties(owinApp.Properties)
{
OnAppDisposing = aspNetCoreLifetime?.ApplicationStopping ?? CancellationToken.None,
DefaultApp = next
};
configuration(owinApp);
return owinApp.Build<Func<IDictionary<string, object>, Task>>();
}));
}
}
Then in the Startup class of the Core project, in the Configure method I was able to use my extension and register SignalR hubs to it like this:
Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseOwinApp(owinApp =>
{
owinApp.MapSignalR();
});
...
}
This way we can add more middlewares to the OWIN pipeline if we need to for whatever reasons.
I hope this helps.