Angular + ASP.NET Core Web API : 404 on new controller endpoints - c#

I have an Angular standalone project which has an ASP.NET Core Web API added to it. I followed this example.
When placing a breakpoint in the weatherforecast controller, it's hit with no issues. When I create a new controller called Account with an endpoint called Login - like this:
[ApiController]
[Route("[controller]")]
public class AccountController : ControllerBase
{
private ILogger<AccountController> _logger;
private IAccountService _accountService;
public AccountController(ILogger<AccountController> logger, IAccountService accountService)
{
_logger = logger;
_accountService = accountService;
}
[HttpPost(Name = "login")]
public async Task<IActionResult> Login(LoginModel model)
{
var response = await _accountService.Login(model);
return Ok(response);
}
}
and it's called via the Angular project, I get 404 not found:
#Injectable({
providedIn: 'root'
})
export class AccountService {
constructor(private http: HttpClient) { }
login(credentials: Credential) {
this.http.post<AuthResponse>('/account/login', credentials).subscribe({
next: (n) => console.log(n),
error: (e) => console.error(e),
complete: () => console.info('complete')
})
}
}
I've also tried just /account yet the outcome is still the same.
I've also introduced a new endpoint which is a GET request, and that also throws a 404. Can someone spot what I'm actually doing wrong here?

You forgot to set the action route in the login controller.
To reach your endpoint, simply add Route.
[Route("login")]
[HttpPost]
public async Task<IActionResult> Login(LoginModel model)
{
var response = await _accountService.Login(model);
return Ok(response);
}

Related

.Net core 5 set default startup route

I'm an old school ASP.Net & AngularJS dev and I'm now working on an existing .Net 5 Core app (that will be migrated to .Net Core 6). This is new tech to me and I'm still figuring things out. One thing I expected to be very simple was to change the starting point of the web UI but I have not been able to make this work as expected.
Right now the app starts up on the default Controller Route, Home. I want to change this so instead of landing on Home (via myapp.mydomain.com) I want it to land on another controller, Clients (via myapp.mydomain.com/Clients).
I've tried making a change in Startup.cs where the default route is defined:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
//pattern: "{controller=Home}/{action=Index}/{id?}");
pattern: "{controller=Clients}/{action=Index}/{id?}");
});
ClientsController:
namespace MyCompany.MyApp.Controllers
{
[Authorize]
public class ClientsController : Controller
{
private readonly ILogger<ClientsController> _logger;
private readonly IClientService _service;
private readonly IAppVersionService _appVersionService;
private readonly IIisService _iisService;
private readonly IWindowsServices _windowsServices;
private readonly IAuthHelpers _authHelpers;
public ClientsController(ILogger<ClientsController> logger, IClientService service,
IAppVersionService appVersionService, IIisService iisService, IWindowsServices windowsServices, IAuthHelpers authHelpers)
{
_logger = logger;
_service = service;
_appVersionService = appVersionService;
_iisService = iisService;
_windowsServices = windowsServices;
_authHelpers = authHelpers;
}
[HttpGet]
[Route("/Clients")]
public async Task<IActionResult> Index(int? pageNumber)
{
_logger.LogInformation("Client Index starting...");
await SyncAppVersionsWithS3();
await AddVersionsToViewData();
ViewBag.Error = TempData["Error"];
ViewBag.Success = TempData["Success"];
var paginatedClients = await PaginatedList<ClientDto>
.CreateAsync(_service.Query(), pageNumber ?? 1, 10);
return View(paginatedClients);
}
But that results in a 404 at startup. Back in the old asp.net days I'd just rt-click on a aspx file and select Set As Startup. I just want to do the equivalent here without it being a kludge. How do I do that? Thanks!
The problem is [Route] attribute in default controller (Clients). if you are defined [Route("Clients")], only access with domain/clients.
solution 1: Remove route attribute as Clients, Clients/Index, "" are handled automatically (MapRoute in program.cs)
[HttpGet]
public async Task<IActionResult> Index(int? pageNumber)
{
...
}
solution 2 multiple route: (in this case there is no need to change the default route)
[HttpGet]
[Route("")]
[Route("Clients")]
[Route("Clients/Index/{pageNumber?}")]
public async Task<IActionResult> Index(int? pageNumber)
{
...
}

Is it possible to use API versioning for specific methods within a controller in ASP.NET Core?

I'm using the API versioning methods provided by Microsoft.AspNetCore.Mvc.Versioning (v4.1).
So in the startup class I have the following:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMvc(options => options.Filters.Add<ExceptionFilter>());
services.AddApiVersioning(options =>
{
options.ApiVersionReader = new MyCustomVersionReader(configuration);
options.AssumeDefaultVersionWhenUnspecified = false;
options.ReportApiVersions = true;
options.ApiVersionSelector = new CurrentImplementationApiVersionSelector(options);
});
}
In the controller I can then add annotation [ApiVersion("1.0")] to the controller or individual methods.
[HttpGet]
[ApiVersion("1.0")]
public async Task<IActionResult> Get() {...}
Using this, all calls to the api have to have version info in the request header.
However, is it possible to have a specific method within the controller not require a version?
e.g.
[HttpGet]
[ApiVersion("1.0")]
public async Task<IActionResult> Method1() {...} //requires version in header
[HttpGet]
public async Task<IActionResult> Method2() {...} //does not require version in header
When I try the above (omitting the ApiVersion annotation for Method2) I still get the following error:
'An API version is required, but was not specified.'
Thank you for helping!
I think you can use [ApiVersionNeutral] attribute.You can see my demo below.
First in your startup add:
options.Conventions.Controller<HomeV1Controller>().HasApiVersion(new Microsoft.AspNetCore.Mvc.ApiVersion(1, 0));
Then in your HomeV1Controller:
[Route("api/[controller]")]
[ApiController]
public class HomeV1Controller : ControllerBase
{
[HttpGet("get")]
public string Get() => "Ok1";
[HttpGet("getv2")]
[ApiVersionNeutral]
public string GetV2() => "Ok2";
}
Test result:

Why does API return 404 when connecting through postman Kestrel environment?

I'm working on an Angular project with .net core api. in development environment. api enpoint returns 404 when connecting with postman
localhost:5000/api/OrderParts/ returns 404
controller
namespace Inventory.API.Controllers
{
[ServiceFilter(typeof(LogUserActivity))]
[Route("api/[controller]")]
[ApiController]
public class OrderPartController : ControllerBase
{
private readonly IOrderPartRepository _repo;
public OrderPartController(IOrderPartRepository repo)
{
this._repo = repo;
}
[HttpGet]
public async Task<IActionResult> GetOrderParts([FromQuery]UserParams userParams)
{
var orderParts = await _repo.GetOrderParts(userParams);
Response.AddPagination(orderParts.CurrentPage, orderParts.PageSize,
orderParts.TotalCount, orderParts.TotalPages);
return Ok(orderParts);
}
}
}
You called your controller OrderPartController so your API URL should be /api/orderpart. Take off the s at the end.

Webapi and normal methods in the same controller?

With the introduction of the Apicontroller attribute in asp.net core 2.1, I wonder how do I get the api and normal methods to work in the same controller.
[Route("api/[controller]")]
[ApiController]
public class OrderController : ControllerBase
{
[HttpPost]
public async Task<IActionResult> SaveOrder(SaveOrderModel model)
{
//...
}
public async Task<IActionResult> CustomerOrders()
{
if (!User.IsInRole("Customer"))
return Challenge();
var customer = await _workContext.CurrentCustomer();
var model = await orderModelFactory.PrepareCustomerOrderListModel();
return View(model);
}
}
I can call post method /api/order/saveorder but cannot run the https://example.com/order/customerorders.
It shows an exceptions: InvalidOperationException: Action
'.CustomerOrders ' does not have an attribute route. Action methods on
controllers annotated with ApiControllerAttribute must be attribute
routed.
If I remove [ApiController] and [Route("api/[controller]")] on the controller level and instead put on the method level, then it surely works. still don't know if there's any better hybrid solution for these methods as i want to use this new ApiController feature.
[Route("/api/controller/saveorder")]
public async Task<IActionResult> SaveOrder(SaveOrderModel model)
Any input is greatly appreciated.
You are saying, that you cannot call https://example.com/order/customerorders. In your [Route("api/[controller]")] you define, that all Methods inside this controller will be available at https://example.com/api/order/.
So to call your method, you need to call https://example.com/api/order/customerorders.
If you want to stay with https://example.com/order/customerorders, you need to put the [Route] attributes at your methods:
[ApiController]
public class OrderController : ControllerBase
{
[HttpPost("api/order")]
public async Task<IActionResult> SaveOrder(SaveOrderModel model)
{
...
}
[HttpGet("order/customerorders")]
public async Task<IActionResult> CustomerOrders()
{
if (!User.IsInRole("Customer"))
return Challenge();
var customer = await _workContext.CurrentCustomer();
var model = await orderModelFactory.PrepareCustomerOrderListModel();
return View(model);
}
}

Postman getting 404 error for simple ASP.NET Core Web API

I have set up a very simple ASP.NET Core 2.1 Web API project, and have created to following simple controller that fetches entites using EF Core.
The problem is that when trying to access the GetEntities method using Postman, the response is a 404 error. I used an HTTP GET method on the following URL.
https://localhost:44311/api/entities
The response body contains the message <pre>Cannot GET /api/entities</pre>.
Why is Postman receiving a 404 error for this route?
EntitiesController.cs
namespace MyProject.Controllers
{
[Route("api/controller")]
public class EntitiesController : Controller
{
private readonly ApplicationDbContext dbContext;
public EntitiesController(ApplicationDbContext _dbContext)
{
this.dbContext = _dbContext;
}
[HttpGet]
public IActionResult GetEntities()
{
var result = dbContext.Entities.ToListAsync();
return Ok(result);
}
}
}
Why is Postman receiving a 404 error for this route?
The issue was the controller token [controller] was missing from the route template on the controller, causing the route to be hard-coded to api/controller.
That meant that when requesting api/entities it technically did not exist and thus 404 Not Found when requested.
Update the route template on the controller.
[Route("api/[controller]")]
public class EntitiesController : Controller {
private readonly ApplicationDbContext dbContext;
public EntitiesController(ApplicationDbContext _dbContext) {
this.dbContext = _dbContext;
}
//GET api/entities
[HttpGet]
public async Task<IActionResult> GetEntities() {
var result = await dbContext.Entities.ToListAsync();
return Ok(result);
}
}
Reference Routing to controller actions in ASP.NET Core : Token replacement in route templates ([controller], [action], [area])
Your route is "api/controller", not "api/entities". You need to put square brackets around "controller" for the desired effect - "api/[controller]".
Makesure the controller file name and the class name is correct, it should be postfix with word"Controller" eg., UsersController.cs

Categories