Search code examples
c#asp.net-coreasp.net-core-webapiasp.net-core-routing

ASP.NET Web API custom post action not working


So I have a GebruikerController in my API. Gebruiker is dutch for User, what this controller does is that it logs the user in, gets list of users, adds users and get a specific user. But I have encountered a problem when I introduced my own custom post method for a simple login function. Whenever I send some data from PostMan to the function I get the following response:

{"id":["The value 'login' is not valid."]}

I access it with this url:

http://localhost:52408/api/gebruikers/login

this is my controller:

[Produces("application/json")]
[Route("api/Gebruikers")]
public class GebruikersController : Controller
{
    private readonly flowerpowerContext _context;

    public GebruikersController(flowerpowerContext context)
    {
        _context = context;
    }

    // GET: api/Gebruikers
    [HttpGet]
    public IEnumerable<Gebruiker> GetGebruiker()
    {
        return _context.Gebruiker;
    }

    // GET: api/Gebruikers/5
    [HttpGet("{id}")]
    public async Task<IActionResult> GetGebruiker([FromRoute] int id)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var gebruiker = await _context.Gebruiker.SingleOrDefaultAsync(m => m.Id == id);

        if (gebruiker == null)
        {
            return NotFound();
        }

        return Ok(gebruiker);
    }

    [Route("api/gebruikers/login")]
    [HttpPost]
    public async Task<IActionResult> PostLogin([FromBody] string email, [FromBody] string password)
    {
        if(!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if(GebruikerVerify(email, password))
        {
            var gebruiker = await _context.Gebruiker.FirstOrDefaultAsync((g) => (g.GebruikerEmail == email && g.GebruikerWachtwoord == password));
            return Ok(gebruiker);
        }
        else
        {
            return BadRequest("invalid data");
        }
    }

    // PUT: api/Gebruikers/5
    [HttpPut("{id}")]
    public async Task<IActionResult> PutGebruiker([FromRoute] int id, [FromBody] Gebruiker gebruiker)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if (id != gebruiker.Id)
        {
            return BadRequest();
        }

        _context.Entry(gebruiker).State = EntityState.Modified;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!GebruikerExists(id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return NoContent();
    }

    // POST: api/Gebruikers
    [HttpPost]
    public async Task<IActionResult> PostGebruiker([FromBody] Gebruiker gebruiker)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        _context.Gebruiker.Add(gebruiker);
        await _context.SaveChangesAsync();

        return CreatedAtAction("GetGebruiker", new { id = gebruiker.Id }, gebruiker);
    }

    // DELETE: api/Gebruikers/5
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteGebruiker([FromRoute] int id)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var gebruiker = await _context.Gebruiker.SingleOrDefaultAsync(m => m.Id == id);
        if (gebruiker == null)
        {
            return NotFound();
        }

        _context.Gebruiker.Remove(gebruiker);
        await _context.SaveChangesAsync();

        return Ok(gebruiker);
    }

    private bool GebruikerExists(int id)
    {
        return _context.Gebruiker.Any(e => e.Id == id);
    }

    private bool GebruikerVerify(string email, string wacthwoord)
    {
        if(_context.Gebruiker.Any(e => e.GebruikerEmail == email))
        {
            Gebruiker gebruiker = _context.Gebruiker.FirstOrDefault(e => e.GebruikerEmail == email);
            if(wacthwoord == gebruiker.GebruikerWachtwoord)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }
}

the following code is my login code, which can also be seen in the code above.

        [Route("api/gebruikers/login")]
    [HttpPost]
    public async Task<IActionResult> PostLogin([FromBody] string email, [FromBody] string password)
    {
        if(!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if(GebruikerVerify(email, password))
        {
            var gebruiker = await _context.Gebruiker.FirstOrDefaultAsync((g) => (g.GebruikerEmail == email && g.GebruikerWachtwoord == password));
            return Ok(gebruiker);
        }
        else
        {
            return BadRequest("invalid data");
        }
    }

    private bool GebruikerVerify(string email, string wacthwoord)
    {
        if(_context.Gebruiker.Any(e => e.GebruikerEmail == email))
        {
            Gebruiker gebruiker = _context.Gebruiker.FirstOrDefault(e => e.GebruikerEmail == email);
            if(wacthwoord == gebruiker.GebruikerWachtwoord)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

I'm fairly new to this and I'm clueless about what I'm doing wrong here. Can someone please help me with this?


Solution

  • This is a routing issue. Because of route prefix on controller you are hitting GetGebruiker which expects id to be a int but it sees "login" string.

    Next FromBody can only be used once in an action parameter. Consolidate those parameters into one model and then use the FromBody attribute.

    public class LoginModel {
        [Required]
        public string email { get; set; }
        [Required]
        public string password { get; set; }
    }
    

    Note the comments used to demonstrate mapped routes.

    [Produces("application/json")]
    [Route("api/Gebruikers")]//route prefix for this controller
    public class GebruikersController : Controller {
        //...code removed for brevity
    
        // GET: api/Gebruikers
        [HttpGet]
        public IEnumerable<Gebruiker> GetGebruiker() {
            //...code removed for brevity
        }
    
        // GET: api/Gebruikers/5
        [HttpGet("{id:int}")] // Note the route constraint
        public async Task<IActionResult> GetGebruiker([FromRoute] int id) {
            //...code removed for brevity
        }
    
        // POST: api/Gebruikers/login
        [HttpPost("login")]
        public async Task<IActionResult> PostLogin([FromBody] LoginModel login) {
            if(!ModelState.IsValid) {
                return BadRequest(ModelState);
            }
    
            if(GebruikerVerify(login.email, login.password)) {
                //...code removed for brevity
            } else {
                return BadRequest("invalid data");
            }
        }
    
        // PUT: api/Gebruikers/5
        [HttpPut("{id:int}")]
        public async Task<IActionResult> PutGebruiker([FromRoute] int id, [FromBody] Gebruiker gebruiker) {
            //...code removed for brevity
        }
    
        // POST: api/Gebruikers
        [HttpPost]
        public async Task<IActionResult> PostGebruiker([FromBody] Gebruiker gebruiker) {
            //...code removed for brevity
        }
    
        // DELETE: api/Gebruikers/5
        [HttpDelete("{id:int}")]
        public async Task<IActionResult> DeleteGebruiker([FromRoute] int id) {
            //...code removed for brevity
        }
    
        //..code removed for brevity
    }
    

    References

    Routing in ASP.NET Core # Route Constraint Reference

    Routing to Controller Actions

    Model Binding