Chris
Chris

Reputation: 187

Correct way to pass object reference into .NET Core Web API

I'm just starting out with .NET Web API programming, and I have a question for seasoned .NET developers - what is the "correct" way to pass an object reference into a Create endpoint?

I have the following models:

public class Task
{
    public int ID { get; set; }
    public string Title { get; set; }
    public virtual User User { get; set; }
}

public class User
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

And my Controller - endpoint to create a Task:

[HttpPost]
public async Task<IActionResult> PostTask([FromBody] Models.Task task)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    _context.Task.Add(task);
    await _context.SaveChangesAsync();

    return CreatedAtAction("GetTask", new { id = task.ID }, task);
}

By default, this has some interesting behavior. It expects an entire User model to be passed into the POST request (see below), and will actually create the user when passed:

{
  "id": 0,
  "title": "string",
  "user": {
    "id": 0,
    "firstName": "string",
    "lastName": "string"
  }
}

I understand technically why it would do this, but this is definitely not acceptable behavior in a real app - so my question is - what is the "correct/appropriate" way to pass a UserID and do validation on the model in .NET? Should I forego the use of "ModelState.IsValid" and do manual validation?

As a secondary question - I am using NSwag to generate Swagger docs from my API, and it's showing "id" as a parameter that needs to be passed into the POST method. Obviously, ID cannot be passed as it's generated in code, but is there a way to get Swagger to not show ID as being a passable property?

Upvotes: 1

Views: 1829

Answers (2)

Xiang Levi
Xiang Levi

Reputation: 1

The attribute [Required] is mandatory and then you can check parameter.

Upvotes: 0

Nkosi
Nkosi

Reputation: 247451

Then create a data transfer model that exposes only the data you want sent over the wire.

public class NewTaskDto {
    [Required]
    public string Title { get; set; }
    [Required]
    public int UserId { get; set; }
}

and map the data to your models on the server, along with what ever validation is required. For example checking that the UserId exists and is valid.

[HttpPost]
public async Task<IActionResult> PostTask([FromBody] NewTaskDto data) {

    if(data != null) {
        validateUserId(data.UserId, ModelState);
    }

    if (!ModelState.IsValid) {
        return BadRequest(ModelState);
    }

    Models.Task task = MapDataToTask(data); //create Task and copy members

    await saveTask(task);

    return CreatedAtAction("GetTask", new { id = task.ID }, task);
}

That way the doc will only see the and report on the exposed members.

Upvotes: 1

Related Questions