Why removing CompatibilityVersion results in returning 404 when an exception is thrown in controller (instead of 500) - asp.net-core

Consider this aspnet core 2.2 code (very boilerplate):
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc();
}
}
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
throw new Exception();
}
}
As it is, calling api/values returns 500.
Commenting out services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); makes it return 404, however.
What's happening in SetCompatibilityVersion that makes it behave this way?

The default compatibility for ASP.NET Core 2.2 is 2.0, so you probably would need to downgrade your app to 2.0 to make it work without calling SetCompatibilityVersion.
For more information see Compatibility version for ASP.NET Core MVC
.
You can also have a look at which options are affected in the source.

Related

How to get the directory path in a string variable having the given directory name in Asp.Net core API

I am trying to get the full path name of the folder 'Images' in API controller. But the following syntax is not working. If Someone can help, it would be very appreciated
string path = HttpContext.Current.Server.MapPath("~/Images/");
You don't need System.Web or HttpContext. You can read the web app's root path from IHostingEnvironment.WebRootPath in ASP.NET Core 2.x, or IWebHostEnvironment.WebPath in ASP.NET Core 3.x.
The dependency injection mechanism knows about that interface which means you can add it as a dependency to your controllers or services, eg :
public class MyController : Controller
{
private IWebHostEnvironment _hostingEnvironment;
public MyController(IWebHostEnvironment environment) {
_hostingEnvironment = environment;
}
[HttpGet]
public IActionResult Get() {
var path = Path.Combine(_hostingEnvironment.WebRootPath, "Images");
...
}
You can pass the root path to your class's constructor. After all, a class named ImagesFilesRepository only cares about its local folder, not whether it's hosted on a web or console application. For that to work, the methods should not be static:
public class ImagesFilesRepository
{
public ImagesFilesRepository (string rootPath)
{
_rootPath=rootPath;
}
public DriveService GetService()
{
//Operations....
public List<GoogleDriveFiles> GetDriveFiles()
{
// Other operations....
}
}
You can register that class as a service in Startup.cs :
public class Startup
{
private readonly IWebHostEnvironment _env;
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
_env = env;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSignleton<GoogleDriveFilesRepository>(_ =>{
var gdriveRoot=Path.Combine(_env.WebRootPath,"GoogleDriveFiles");
return new GoogleDriveFilesRepository(gdrivePath);
});
...
}
}
This class can now be used as a dependency on a controller. It's no longer necessary to use IWebHostEnvironment in the controller :
public class MyController : Controller
{
private ImagesFilesRepository _gdrive;
public MyController(ImagesFilesRepository gdrive) {
_gdrive=gdrive;
}
}

Http Post in Orchard Core asp net core Web App returns bad request

I'm using Orchard core in asp net core web app project. I have a controller with two simple get and post Apis. As I'm using OrchardCore the Startup.cs file has different config and I dont use services.AddControllers() in configureServices.
Every thing is fine untill I'm using HttpGet. But when I want to have an Api with HttpPost postMan says badRequest. So I Added services.AddControllers() in Startup.cs and the post Api was fine in post Man but the orchard project says I have multipe Endpoints.
I used services.AddMvc().AddNewtonsoftJson(), and every thing was fine but the admin page didn't load and had error as below:
InvalidOperationException: The view 'Index' was not found. The
following locations were searched:
/Areas/OrchardCore.AdminDashboard/Views/Dashboard/Index.cshtml
/Areas/OrchardCore.AdminDashboard/Views/Shared/Index.cshtml
/Views/Shared/Index.cshtml /Pages/Shared/Index.cshtml
I wold appreciate it if you can help me how to call Post Api.
here is my code:
[HttpPost("post")]
public Task<string> post()
{
return Task.FromResult("hiPost");
}
[HttpGet("get")]
public Task<string> get()
{
return Task.FromResult("hiGet");
}
and this is my startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
//services.AddControllers();
services.AddOrchardCms();
services.AddMediatR(typeof(SelectedWebSiteBlogQueryHandler).Assembly);
services.AddAutoMapper(typeof(Startup));
services.AddCors();
services.AddMvc().AddNewtonsoftJson();
}
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(o => o.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseOrchardCore();
}
}
You are probably missing an IgnoreAntiForgeryToken attribute on your controller.
AntiForgery is enabled by default by OrchardCore
For an ApiController in OrchardCore I would expect to see the controller decorated as follows.
[ApiController]
[Authorize(AuthenticationSchemes = "Api"), IgnoreAntiforgeryToken, AllowAnonymous]
However this depends if you are using the OpenId module to authenticate with, or simply need to post to a normal controller, without an AuthenticationScheme
Depending on what you are actually posting from in real life, it may be better to supply an anti forgery token as part of your post.

OData integration with CosmosDb does not return expected result

I have created a .NET Core 3.1 WebAPI application which connect with Azure Cosmos Db. The WebAPI is returning data from CosmosDb correctly. When I tried to integrate OData to this solution, and tried to query data using the Select method, it does not return expected result.
The following are my code:
StartUp.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddOData();
services.AddControllersWithViews();
services.AddSingleton<ICosmosDbService>(InitializeCosmosClientInstanceAsync(Configuration.GetSection("CosmosDb")).GetAwaiter().GetResult());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=ToDo}/{action=Index}/{id?}");
endpoints.EnableDependencyInjection();
endpoints.Select().Filter().OrderBy().Expand();
});
}
}
WebAPI controller:
[Produces("application/json")]
[Route("api/[controller]")]
[ApiController]
public class ItemsController : ControllerBase
{
private readonly ICosmosDbService _cosmosDbService;
public ItemsController(ICosmosDbService cosmosDbService)
{
_cosmosDbService = cosmosDbService;
}
// GET: api/<ItemsController>
[HttpGet]
[EnableQuery()]
public async Task<IEnumerable<Item>> Get()
{
return await _cosmosDbService.GetItemsAsync("SELECT * FROM c");
}
}
When I try to retrieve data using the API call(https://localhost:44357/api/items), I am getting expected result:
[{"id":"5f4f5d02-9217-4591-8f8c-2af9fe7d9ae4","name":"Brush","description":"Brush your teeth every night","completed":true,"partitionKey":null},{"id":"6a5edfe3-9c84-4398-bed4-963dbb4a42e3","name":"Excercise","description":"Hit the gym in the evening","completed":true,"partitionKey":null}]
But when I try to use the OData method(https://localhost:44357/api/items?$select=name), I am not getting expected result. Instead, I am getting this:
[{"instance":null,"container":{},"modelID":"7c0ae376-1666-46f8-886f-9bf758824063","untypedInstance":null,"instanceType":null,"useInstanceForProperties":false},{"instance":null,"container":{},"modelID":"7c0ae376-1666-46f8-886f-9bf758824063","untypedInstance":null,"instanceType":null,"useInstanceForenter code hereProperties":false}]
Any idea why it is like this?
There is incompatible situation with the JSON serializer in Asp.Net 3.1. Try to AddNewtonsoftJson.
services.AddControllers(mvcOptions =>
mvcOptions.EnableEndpointRouting = false)
.AddNewtonsoftJson();

Controller's action not invoked in ASPNETCORE console app running Kestrel

I'd like to have a console application running a standalone webserver accepting REST calls. My application is a .NET Core app with ASP .NET Core inside. I am completely new in this area. I found some examples and now I am struggling with controller route configuration. With the code below I always get "404 Not Found" error when using http://localhost:3354/api/Demo/Hello. Does anyone have an idea what am I doing wrong? Thank you for any suggestion!
I use VS2019 and ASPNETCORE 2.2.8.
class Program
{
static void Main(string[] args)
{
var builder = WebHost.CreateDefaultBuilder()
.ConfigureKestrel(options => options.ListenAnyIP(3354))
.UseStartup<Startup>();
builder.Build().Run();
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder builder, IHostingEnvironment env)
{
builder.UseMvc(delegate(IRouteBuilder routeBuilder)
{
routeBuilder.MapRoute("default", "api/{controller}/{action}");
});
}
}
Here comes the DemoController class.
public class DemoController : Controller
{
public IActionResult Hello()
{
return Ok("Hello world");
}
}
Your example is working fine for me on .net core 2.2
You could try explicitly declare routes like
[ApiController]
[Route("api/[controller]")]
public class DemoController : Controller
{
[HttpGet("hello")]
public IActionResult Hello()
{
return Ok("Hello world");
}
}
Also you could consider using Visual studio built-in templates of api web applications
After some investigation and comparison of my project with the sample project of Roman Kalinchuk I found out that the problem is that mvc controller provider doesn't look for controller types in my application assembly. It is enought to add my application assembly to the application parts collection.
See the .AddApplicationPart(typeof(DemoController).Assembly); line.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.AddApplicationPart(typeof(DemoController).Assembly);
}
public void Configure(IApplicationBuilder builder, IHostingEnvironment env)
{
env.EnvironmentName = "Development";
builder.UseMvc(delegate(IRouteBuilder routeBuilder)
{
routeBuilder.MapRoute("test", "api/{controller}/{action}");
});
}
}

Asp.Net Core No service for type has been registered

I have the following two classes
public class RepositoryConnection : IRepositoryConnection{
public RepositoryConnection(IConfiguration configuration, ILogger<RepositoryConnection> logger){
//STUFF
}
}
public class AuthenticationTokenFactory : IAuthenticationTokenFactory {
public AuthenticationTokenFactory(ILogger<AuthenticationTokenFactory> logger) {
//STUFF
}
}
Here is my Startup.cs
public class Startup {
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services) {
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSingleton<IAuthenticationTokenFactory, AuthenticationTokenFactory>();
services.AddSingleton<IRepositoryConnection,RepositoryConnection>();
}
}
I can successfully inject IAuthenticationTokenFactory to controllers but when i try to inject IRepositoryConnection i get the following error→
InvalidOperationException: No service for type 'TrainingCommerce.Accessors.RepositoryConnection' has been registered.
Thanks to comments i immediately noticed my wrongful ways.
I was trying to access at another line
var debug = ControllerContext.HttpContext.RequestServices.GetRequiredService<RepositoryConnection>();
Try injecting the interface instead of the implementation:
In your sample you inject ILogger<RepositoryConnection> logger this is a typo and should be: ILogger<IRepositoryConnection> logger.
So:
public class RepositoryConnection : IRepositoryConnection{
public RepositoryConnection(IConfiguration configuration, ILogger<IRepositoryConnection> logger){
//STUFF
}
}