I'm working through a "hello world" tutorial on Asp.Net Core. I'm using WebApi (not MVC).
Here is the controller for the REST API I'm trying to invoke:
...
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class ManageCarController : ControllerBase
{
private IMapper mapper;
private ApplicationDbContext dbContext;
public ManageCarController(IMapper mapper, ApplicationDbContext dbContext)
{
this.mapper = mapper;
this.dbContext = dbContext;
}
// GET api/values
[HttpGet]
public IEnumerable<CarViewModel> Get()
{
IEnumerable<CarViewModel> list =
this.mapper.Map<IEnumerable<CarViewModel>>(this.dbContext.cars.AsEnumerable());
return list;
}
...
Here is my controller for Login:
...
[Authorize]
[Route("[controller]/[action]")]
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger _logger;
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
ILogger<AccountController> logger)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
}
[TempData]
public string ErrorMessage { get; set; }
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login([FromBody]LoginViewModel model)
{
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync
(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
var msg = "User logged in.";
return Ok(msg);
}
}
// If we got this far, something failed, redisplay form
return BadRequest("Fail to login with this account");
}
I can log in (http://localhost:5000/Login) OK, the response is "User logged in."
When I browse to http://localhost:5000/api/ManageCar, it redirects here and gives me an HTTP 404: https://localhost:44342/Account/Login?ReturnUrl=%2Fapi%2FManageCar, and I never hit the controller.
If I comment out [Authorize], then http://localhost:5000/api/ManageCar works OK.
What am I missing?
More importantly, what is a good way to troubleshoot the problem?
Update
Prior to calling
http://localhost:5000/api/ManageCar, I first log in (successfully).Here is what I see in Edge > Developer Tools > Network:
Name Protocol Method Result Content type Received Time Initiator https://localhost:44342/Account/Login HTTP/2 POST 200 application/json 9.31 s XMLHttpRequest <= Login: OK https://localhost:44342/Account/Login HTTPS GET 200 (from cache) 0 s <= ManageCars (GET@1): OK https://localhost:44342/api/ManageCar HTTP/2 GET 302 0 B 97.43 ms XMLHttpRequest <= ManageCars (GET@2 - 302 redirect to REST API): OK https://localhost:44342/Account/Login?ReturnUrl=%2Fapi%2FManageCar HTTP/2 GET 404 0 B 16.77 ms XMLHttpRequest <= ManageCars (GET@3 - 404: not found): FAILS - Console: HTTP 404: NOT FOUND - The server has not found anything matching the requested URI (Uniform Resource Identifier). (XHR)GET - https://localhost:44342/Account/Login?ReturnUrl=%2Fapi%2FManageCar
CLARIFICATION FOR Tân Nguyễn's RESPONSE:
- I have a REST API, written in C# using Asp.Net Core 2.1 + Web API.
- The API has a "GET" method,
/api/ManageCar. If I call with without [Authorize], it works. - I'm "securing" the API with Asp.Net Core Identity. The URL is '/Account/Login'. It needs to use POST (to pass username and password). That works, too.
- If I annotate "ManageCar" with [Authorize], and then log in (successfully), then THEN GET
/api/ManageCar... it DOESN'T go directly to my controller for "/api/ManageCar". - Instead, it goes to "/Account/Login" (I'm already logged in, the result is HTTP 200), then redirects to "https://localhost:44342/Account/Login?ReturnUrl=%2Fapi%2FManageCar"/
- I should be able to do a POST for my login, and a GET for my (now authenticated) query - it should "just work".
- Unfortunately, I don't know what Asp.Net is doing "behind the scenes" ... and I don't know what's causing the problem, or how to fix it.
Update
I still haven't resolved the problem - I'm still getting HTTP 404 with
[Authorize], and it works without[Authorize]Both my AccountController and ManageCarController have the same path:
[Route("api/[controller]/[action])]' and[Route("api/[controller])]`, respectively. I can still log in successfully, I still get HTTP 404 when I try to read the "Cars" list.I enabled "Trace" logging in my
appsettings.json. Here is a summary of the output of the failed API call:Console log: - Request starting HTTP/1.1 GET http://localhost:63264/api/ManageCar Request finished in 81.994ms 302 - Request starting HTTP/1.1 GET http://localhost:63264/Account/Login?ReturnUrl=%2Fapi%2FManageCar AuthenticationScheme: Identity.Application was successfully authenticated. The request path /Account/Login does not match a supported file type The request path does not match the path filter Request finished in 31.9471ms 404Summary:
a) request to "ManageCar" redirects to AccountController => OK
b) AccountController gets the request => OK
c) Q: Does AccountController authenticate the request? <= it seems to ("successfully authenticated"...)
d) Q: What do "match a supported file type" or "match the path filter" mean?
What can I do about them?
POSTbut a redirect will be aGETrequest.[HttpGet](I'm new to Asp.Net Core - don't disbelieve you), then how exactly would I do it? Could you give me a "response" that points me in the right direction?[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]. You are trying to authorize the web api using cookies, it may fails without additional configurations