1

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?

5
  • routing issue. because of route prefix on controller you are hitting GetGebruiker which expects id to be an int but it sees login Commented Nov 20, 2017 at 10:46
  • next FromBody can only be used once in an action parameter. consolidate those parameters into one model and then use the FromBody attribute Commented Nov 20, 2017 at 10:47
  • You also need to specify which HTTP verb was used when you got the error as login is suppose to be a POST request. Commented Nov 20, 2017 at 10:49
  • If using a route prefix then there is no need to include the prefix on the action routes. Commented Nov 20, 2017 at 10:49
  • so I need to remove the rout and change the FromBody parameter? Commented Nov 20, 2017 at 10:51

1 Answer 1

6

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

Sign up to request clarification or add additional context in comments.

2 Comments

ah ok, and I have to send this object: "login" : [ { "email" : "[email protected]", "password" : "test" } ] if I'm correct?
@B.Hulshof the object would just be { "email" : "[email protected]", "password" : "test" }. The model binder would map that to the actio parameter. what you had in your last comment is an array not a single object

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.