1

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?

1
  • Then create a data transfer model that exposes only the data you want sent over the wire. Commented Aug 6, 2018 at 1:14

2 Answers 2

1

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.

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

Comments

0

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

Comments

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.