Jarosław Kończak Jarosław Kończak - 4 months ago 6
C# Question

Should I have two different model classes to read and update object in Web API?

Let say I have a API that sends simple messages to users. To send it I would use something like:

POST {
Content: "Message here",
To: "foo@bar.com"
} api/messages


Now I want to read all sent messages but with user who send it (I had identity in cookie) and send time (assigned automatically). Again it seems to be simple:

GET api/messages


and I will get:

[{
Content: "Message here",
To: "foo@bar.com",
From: "user1",
Time: "0001-01-01T00:00:00"
}]


From API controller side I will have two methods:

public class MessagesController : ApiController
{
[HttpPost, Route("Messages")]
public HttpResponseMessage Post([FromBody] Message message)
{
messageRepository.Create(message);
return Request.CreateResponse(HttpStatusCode.Created, "Message was send.");
}

[HttpGet, Route("Messages")]
public HttpResponseMessage Get()
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(JsonConvert.SerializeObject(messageRepository.GetMessages()))
};
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
return response;
}
}


My problem is that the object what is send is different from object what i get. So I think I can't use same Message class. Why should I do then:


  • Should I have two different classes here to do it proper way? (I will have more to code and i don't know is it correct from REST point of view)

  • Should I use one class but ignore From and Time properties if user will send it to me?

  • Should I told my user to send me From and Time but send null here?


Answer

Think of the objects being sent over the wire as DTOs (Data Transfer Objects). It is usually advised that you should only send what is necessary over the wire.

So your client should only construct what the api expects. If From and Time is data that the client does not need to provide then it shouldn't.

public class PostMessageDto {
    public string Content { get; set; }
    public string To {get; set; }
}

From a security perspective you don't want to leak more information than is necessary for the client. If your entities are exposing more information than you want exposed you should create a model to pass only the details you want sent to the client.

public class MessagesController : ApiController {
    //POST api/messages
    [HttpPost, Route("Messages")]
    public HttpResponseMessage Post([FromBody] PostMessageDto message) {
        var entity = new Message {
            Content = message.Content,
            To = message.To
        };
        messageRepository.Create(entity);
        return Request.CreateResponse(HttpStatusCode.Created, "Message was send.");
    }

    //GET api/messages    
    [HttpGet, Route("Messages")]
    public HttpResponseMessage Get() {
        var entities = messageRepository.GetMessages();

        //..you can put code here that creates the data you want returned.
        var responseData= entities.Select(x => new {
            Content = x.Content,
            To = x.To,
            From = x.From,
            Time = x.Time
        });

        var response = Request.CreateResponse(HttpStatusCode.OK, responseData);
        response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
        return response;
    }
}