Asp.Net Core 3.1 Web Application, Api page not found issue - asp.net-core

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

Related

Asp.Versioning.Http AmbiguousMatchException: The request matched multiple endpoints on controllers

I am trying to achieve header based versioning on my controllers with Asp.Versioning.Http package version 6.4.0
it is supposed to be super simple here however i get AmbiguousMatchException: The request matched multiple endpoints exception
Here is my Program class
and my controllers defined like that:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddApiVersioning(options => {
// options.ApiVersionReader = new HeaderApiVersionReader("api-version");
options.DefaultApiVersion = new ApiVersion(1.0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
}).EnableApiVersionBinding();
builder.Services.AddMvc();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
// app.UseSwagger();
//app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapHealthChecks("/health/live");
app.MapControllers();
app.Run();
namespace Things.Service.Controllers.V1
{
[ApiController]
[ApiConventionType(typeof(DefaultApiConventions))]
[Route("[controller]")]
[Asp.Versioning.ApiVersion(1.0)]
public class ThingsController : ControllerBase
{
// controller logic
}
}
namespace Things.Service.Controllers.V2
{
[ApiController]
[ApiConventionType(typeof(DefaultApiConventions))]
[Route("[controller]")]
[Asp.Versioning.ApiVersion(2.0)]`your text`
public class ThingsController : ControllerBase
{
// controller logic
}
}
I get this exception:
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints. Matches:
Things.Service.Controllers.V2.ThingsController.GetAllAsync (Things.Service)
Things.Service.Controllers.V1.ThingsController.GetAllAsync (Things.Service)
This is happening because you are missing AddMvc. Don't let the name fool you, this adds MVC Core, not the full MVC stack. Starting in 6.0, the new setup pivots on IApiVersioningBuilder so that all of the setup is in one place and, hopefully, easier to follow. If you're coming from earlier versions (e.g. <= 5.x), this might be a surprise. This change was necessary because AddApiVersioning is now the foundation for Minimal APIs, which doesn't include MVC Core or controller support. AddMvc adds those features.
services
.AddApiVersioning() // Asp.Versioning.Http : Core services and Minimal APIs
.AddMvc() // Asp.Versioning.Mvc : MVC Core
.AddApiExplorer() // Asp.Versioning.Mvc.ApiExplorer : API Explorer
.AddOData() // Asp.Versioning.OData : OData support
.AddODataApiExplorer(); // Asp.Versioning.OData.ApiExplorer : OData API Explorer
Since you're using MVC Core and controllers, you do not need EnableApiVersionBinding. MVC Core has support for Model Binders. AddMvc will register all those services. If you want to receive the incoming ApiVersion in your controller action, you need only add a parameter of type ApiVersion with the name of your choice. For example:
namespace Things.Service.Controllers.V1
{
[ApiVersion(1.0)]
[ApiController]
[ApiConventionType(typeof(DefaultApiConventions))]
[Route("[controller]")]
public class ThingsController : ControllerBase
{
[HttpGet]
public IActionResult Get(ApiVersion version) => Ok();
}
}
Minimal APIs do not have a way to support this type of model binding. EnableApiVersionBinding provides a way to make it work. It won't hurt anything if you've added it, but it's unnecessary.
Finally, it looks like you have included a version number in your namespaces. If this is indeed your setup, you might consider using the VersionByNamespaceConvention. This would negate the need to decorate controllers with [ApiVersion]. The API version would be derived from the namespace itself. For additional details, see the Version By Namespace Convention documentation.

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.

dotnet core change swagger.json server url wrong under subdir

I have a dotnet core webapi project that is setup with swagger.
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSwaggerGen();
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("./v1/swagger.json", "myapi.Api V1");
});
}
Controllers are like so:
[Route("api/[controller]/[action]")]
[ApiController]
[Authorize(Policy = "policyname")]
public class ThingController : ControllerBase
{
[HttpGet]
public async Task<ActionResult> GetThing()
{
....
}
}
This works fine locally and once deployed to the server and accessed directly (eg https://myservername/myapi/swagger/index.html can correctly make api calls to https://myservername/myapi/api/thing)
However we have an alias setup to access via https://myapp.company.com/services/myapi.
Now making direct api calls works eg https://myapp.company.com/services/myapi/api/thing returns the same as https://myservername/myapi/api/thing
Swagger UI displays correctly at https://myapp.company.com/services/myapi/swagger/index.html and it is loading swagger.json correctly but all api calls are made as https://myapp.company.com/myapi/api/thing i.e.. /services is missing from the api base url, needless to say these api calls fail.
Swagger.json contains the following:
"servers": [
{
"url": "/myapi"
}
],
I presume this should be /services/myapi and the rest should fall into place. How can I set this?
I've done some googleing and found plenty of questions asking how to move swagger ui to a new location (UseSwaggerUI(c => c.RoutePrefix) and some on how to move swagger.json to a new location (app.UseSwagger(c => c.RouteTemplate) but neither of them is what I want.
How can I configure swagger's server url at runtime? Preferably using local paths so it works on all environments (eg it 'just knows' its at https://myservername/myapi/, https://myapp.company.com/services/myapi/ or even https://localhost:5001/ and sets the base url as appropriate.)

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/

How to add global metadata to ASP.NET Core logging?

I'd like to add my app's build number to all logs in an ASP.NET Core 3.1 app that is using Application Insights for log storage. Is this possible without having to use BeginScope and EndScope everywhere? I assumed it would be part of the ConfigureLogging startup hook, but didn't see anything. I've done this in the past with Serilog's enrichers, but am not using that library currently.
You can achieve that with TelemetryInitializer. (https://learn.microsoft.com/en-us/azure/azure-monitor/app/api-filtering-sampling#addmodify-properties-itelemetryinitializer)
public class BuildNumberTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
(telemetry as ISupportProperties).Properties.Add("BuildNumber", "ValueForBuildNumber");
}
You need to add this initializer to the config, which is done like below if you are on Asp.Net Core applications.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ITelemetryInitializer, BuildNumberTelemetryInitializer >();
}