I have created the following PersonController.cs:
using Microsoft.AspNetCore.Mvc;
using PersonApi.Models;
namespace donau_lead_generator.Controllers;
[ApiController]
[Route("[controller]")]
public class PersonController : ControllerBase
{
private readonly ILogger<PersonController> _logger;
public PersonController(ILogger<PersonController> logger)
{
_logger = logger;
}
[HttpPost("addData")]
public Task<ActionResult<Person>> Post(Person person)
{
Console.WriteLine("I am here");
return null;
}
And the following service:
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { Person } from './person';
#Injectable()
export class HomeService {
constructor(
private http: HttpClient) {
}
/** POST: add a new user to the database */
addUser(user: Person): Observable<Person> {
return this.http.post<Person>("person/addData", user);
}
}
Which is called in the component like this:
this.homeService.addUser(newUser).subscribe(user => {console.warn(user)
}, error => console.error(error)); //maybe create Person
I know that the return value etc. is not correct yet, but the main problem is that the endpoint is not recognized:
My Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
// 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.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
app.MapFallbackToFile("index.html");;
app.Run();
If I add the method to the pre generated (weatherforecast) endpoint everything works.
My folder structure:
EDIT
I changed it to, as requested in an answer:
[HttpPost("addData")]
public ActionResult<Person> Post(Person person)
{
Console.WriteLine("I am here");
return Ok(person);
}
However, it still throws the same 404 not found error.
I finally found the problem.
The generated project via the dotnet cli, configures a proxy.conf.js where it specifies which urls are redirected to the server and /weatherforecast is the only url allowed. Thats why the endpoint added in /weatherforecast worked and in an extra controller file not.
To make it work for other endpoints add person in proxy.conf.js:
const { env } = require('process');
const target = env.ASPNETCORE_HTTPS_PORT ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` :
env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'http://localhost:42175';
const PROXY_CONFIG = [
{
context: [
"/weatherforecast",
"/person"
],
target: target,
secure: false,
headers: {
Connection: 'Keep-Alive'
}
}
]
module.exports = PROXY_CONFIG;
Replace this:
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
to this: app.MapControllers();
And your url will be like: https://localhost:44416/Person/addData
Note: When using the [Route("[controller]")] attribute with the "[controller]" parameter, in the URL the controller name begins with an uppercase letter
Update
Also try to replace this builder.Services.AddControllersWithViews();
to this: builder.Services.AddControllers(); because you don't use controller with views
This is because the ActionResult types represent various HTTP status codes.
You have an Task<ActionResult<Person>> endpoint return type, but you are trying to return null with no defined code status, so it needs to be wrapped with Ok() or NotFound() methods, etc.
[HttpPost("addData")]
public ActionResult<Person> Post(Person person)
{
Console.WriteLine("I am here");
return OK(person);
}
Related
I asked a similar question a few days ago, but I've got an issue with another controller now and for the life of me I can't figure out why this is returning a 404.
API Controller...
[Route("api/[controller]")]
[ApiController]
public class FilesController
{
private readonly IFilesService _filesService;
public FilesController(IFilesService filesService)
{
_filesService = filesService;
}
[HttpGet("{id}")]
public IEnumerable<SupportFile> GetFiles(int id) {
return _filesService.GetFiles(id);
}
[HttpGet("DownloadFile/{fileName}")]
public async Task<FileStreamResult> DownloadFile(string fileName)
{
return await _filesService.DownloadFile(fileName);
}
}
Program.cs...
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddSingleton<DapperContext>();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<ISupportService, SupportService>();
builder.Services.AddScoped<IFilesService, FilesService>();
builder.Services.AddControllersWithViews();
builder.Services.AddControllers();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
// 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.MapControllers();
app.UseRouting();
app.MapDefaultControllerRoute();
app.MapFallbackToFile("index.html"); ;
app.Run();
Proxy.conf.js...
const { env } = require('process');
const target = env.ASPNETCORE_HTTPS_PORT ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` :
env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'http://localhost:19229';
const PROXY_CONFIG = [
{
context: [
"/api/*"
],
target: target,
secure: false,
headers: {
Connection: 'Keep-Alive'
}
}
]
module.exports = PROXY_CONFIG;
Another api controller that works fine...
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
public UsersController(IUserService userService)
{
_userService = userService;
}
[HttpPost]
public User ModifyUser(AzureUser user)
{
return _userService.ModifyUser(user);
}
[HttpGet]
public IEnumerable<User> GetUsers()
{
return _userService.GetUsers();
}
}
But no matter what I do...
https://localhost:44427/api/files/123 returns a 404.
https://localhost:44427/api/files/DownloadFile/test.csv returns a 404.
Other API controller methods work, so the port is correct.
The other api controllers only have one GET though, whenever I try to add multiple GETs using attribute routing, they both just end of returning a 404.
Solved this.
It was the proxy.conf.js.
I had to change "/api/*" to "/api/**" in the context array, ** seems to be a catch all.
Your controller should inherit the class ControllerBase.
Instead of this:
public class FilesController
Try this:
public class FilesController : ControllerBase
Have you tried debugging?
Does a file with the ID 123 or name test.csv actually exist?
https://localhost:44427/api/files/123
https://localhost:44427/api/files/DownloadFile/test.csv
If not then then the service methods may return null which will lead to a 404 by default.
I want to add constraints to routing.I am using conventional routing. Following is my code
Startup.cs
using Lab4MVC.Routing;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
//builder.Services.AddMvc(x => x.EnableEndpointRouting = false);
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
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.UseMvc();
//app.UseMvcWithDefaultRoute();
//app.UseMvc(routes => {
// Routing.LoadRoutes(routes);
//});
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
Routing.LoadRoutes(endpoints)
);
app.Run();
Routing.cs
using Microsoft.AspNetCore.Builder;
namespace Lab4MVC.Routing
{
public static class Routing
{
public static void LoadRoutes(IEndpointRouteBuilder routeBuilder)
{
routeBuilder.MapControllerRoute(name:"Route1",pattern: "/Customer/Add/{Id}",defaults: new
{
contoller = "Customer",
action = "Add"
});
//routeBuilder.MapControllerRoute("default", "{controller=Customer}/{action=Add}");
//routeBuilder.MapFallbackToController("Add", "Customer");
}
}
}
CustomerController.cs
using Microsoft.AspNetCore.Mvc;
namespace Lab4MVC.Controllers
{
public class CustomerController : Controller
{
public IActionResult Add(int Id)
{
return View("CustomerScreen");
}
public IActionResult Update()
{
return View();
}
public IActionResult Delete()
{
return View();
}
}
}
I am learning ASP.NET Core. Struggling to call Route with the pattern. I am able to call Controller using the default route.
When adding constraint it is not called. Also, please guide me on how to use the following pattern lets say Customer/New/1. Here I want to change my action name. Don't want to use Add URL.
Please help me. Thanks in advance.
In my opinion, the correct meaning you want to express should be that you don't want others to know what method you call in the background. Suppose you have a get request, and the original route is /Customer/Add? param=test, you want to display the path as /Person/Add through a custom method, similar to this requirement, right?
If it is like this,you can use Rewrite method.And you can refer to this Link.
It is worth noting that:app.UseRewriter() must be used before app.UseStaticFiles().
I have a small ASP.NET Core web app project named "OdeToFood". The dev environment includes:
IDE: Visual Studio 2019 on Windows 10 PC
ASP.NET Core 3.1
Use Dapper for data access from SQL DB
Not using MVC
In one of web pages, the jQuery .ajax will be used retrive a record from DB. I added an ApiController with type of "API controller with read/write actions" because the EF is not used in this project.
Here is the code auto generated by VS (no change was made).
namespace OdeToFood.Api
{
[Route("api/[controller]")]
[ApiController]
public class RestaurantsController : ControllerBase
{
// GET: api/<RestaurantsController>
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<RestaurantsController>/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/<RestaurantsController>
[HttpPost]
public void Post([FromBody] string value)
{
}
// PUT api/<RestaurantsController>/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/<RestaurantsController>/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}
I tried to test it from the brower with following URLs:
https://localhost:44361/api/Restaurants
https://localhost:44361/api/Restaurants/8
https://localhost:44361/api/RestaurantsController
https://localhost:44361/api/RestaurantsController/8
They all failed with HTTP 404 error.
Since above code is already using the "Attribute Routing' with [Route ...], I think the first 2 URLs int test should work. But they didn't. I could not figure out why. Any help or suggestion will be greatly appreciated.
In the startup.cs file, the configure section has following settings:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/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.MapRazorPages();
});
}
Try below in your startup.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
Comment below;
endpoints.MapRazorPages();
Your Scenario:
Though the solution is very straight forward, I felt more explanation
for you to get rid of any further confustions.
As you said "Visual Studio 2019" so if you have taken asp.net core web api project, it would be created with weather forecast api controller with default startup.cs like below:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
So if you directly run the project it should run with the default action like below:
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
So now come to your projects configuration which is containing like
below:
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
Means its looking for simple mvc razor page and when you are trying
to hit either of this URLs 1. https://localhost:44361/api/Restaurants or 2.https://localhost:44361/api/Restaurants/8 it should get 404 because it's not routed accordingly. So your routing template is not correct to route api controller directly. endpoints.MapRazorPages() is usually used for Razor mvc page routing.
Solution:
If you could add the API controller while you were taking your RestaurantsController I think it would be look like what you posted.
and request should route as expected if you replace the below service
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
Note: You even can override your route template if you want. You could have a look here also if you want which a
conventional route.
Output:
What I tried show you is what mislead you and sharpen your idea. If you have any further query you have a look our official document here. Hope it would help you accordingly.
I have migrated an existing API project from 2.2 to 3.0 based on guidelines from this page.
Thus I've removed:
app.UseMvc(options =>
{
options.MapRoute("Default", "{controller=Default}/{action=Index}/{id?}");
});
and inserted:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "Default", pattern: "{controller=Default}/{action=Index}/{id?}");
});
But no controller and action would be bound. All I get for any API I call is 404.
How should I debug it and what do I miss here?
Update: The Startup.cs file is located in another assembly. We reuse a centralized Startup.cs file across many projects.
From Attribute routing vs conventional routing:
It's typical to use conventional routes for controllers serving HTML pages for browsers, and attribute routing for controllers serving REST APIs.
From Build web APIs with ASP.NET Core: Attribute routing requirement:
The [ApiController] attribute makes attribute routing a requirement. For example:
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
Actions are inaccessible via conventional routes defined by UseEndpoints, UseMvc, or UseMvcWithDefaultRoute in Startup.Configure.
If you want to use conventional routes for web api , you need to disable attribute route on web api.
StartUp:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
// 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();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "Default",
pattern: "{controller=default}/{action=Index}/{id?}");
});
}
Web api controller:
//[Route("api/[controller]")]
//[ApiController]
public class DefaultController : ControllerBase
{
public ActionResult<string> Index()
{
return "value";
}
//[HttpGet("{id}")]
public ActionResult<int> GetById(int id)
{
return id;
}
}
This could be requested by http://localhost:44888/default/getbyid/123
I can recommend my solution.
Create your CUSTOM base controller like that.
[Route("api/[controller]/[action]/{id?}")]
[ApiController]
public class CustomBaseController : ControllerBase
{
}
And use CustomBaseController
public class TestController : CustomBaseController
{
public IActionResult Test()
{
return Ok($"Test {DateTime.UtcNow}");
}
}
Rout` api/Test/test
You should try:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
It seems to not support conventional routing even in middleware like UseEndpoints and UseMvc
You can find that here
Our app loads controllers in external assemblies we call packages. I want to create a route that routes to a package using URLs like package/BillingPackage/Invoice rather than api/BillingPackage/Invoice. Here is what I have done:
Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseEndpointRouting()
.UseMvc(routes =>
{
routes.MapRoute(
name: "package",
template: "package/{package}/{controller}/{id?}");
routes.MapRoute("api", "api/{controller}/{action=Get}/{id?}");
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
app.UseStaticFiles();
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var source = new PackageAssemblySource(Configuration);
var packageAssemblies = source.Load();
var builder = new ContainerBuilder();
builder.RegisterModule(new WebApiModule(packageAssemblies));
services
.AddMvc()
.ConfigureApplicationPartManager(manager =>
{
// Add controllers and parts from package assemblies.
foreach (var assembly in packageAssemblies)
{
manager.ApplicationParts.Add(new AssemblyPart(assembly));
}
});
.AddControllersAsServices() // Now that AssemblyParts are loaded.
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);;
builder.Populate(services);
ApplicationContainer = builder.Build();
return new AutofacServiceProvider(ApplicationContainer);
}
Then I define a controller like this:
[Route("package/BillingPackage/[controller]", Name = "Invoice")]
public class InvoiceController : ControllerBase
{
[HttpGet()]
public ActionResult<Invoice> Get()
{
return new SampleInvoice();
}
}
Even with all that, package/BillingPackage/Invoice yields a 404 whereas api/BillingPackage/Invoice does not. How do I get my WebApi to serve endpoints from package rather than api?
You are probably experiencing route conflict with template: "package/{package}/{controller}/{id?}".
If using attribute routing on the controller then remove that convention-based route
To get the desired behavior, you would need to include a template parameter [Route("package/{package}/[controller]", Name = "Invoice")] along with a method/action argument public ActionResult<Invoice> Get(string package) which will be populated from the matched value from the URL.
For example
[Route("package/{package}/[controller]", Name = "Invoice")]
public class InvoiceController : ControllerBase {
//GET package/BillingPackage/Invoice
[HttpGet()]
public ActionResult<Invoice> Get(string package) {
return new SampleInvoice();
}
}
Reference Routing to controller actions in ASP.NET Core