10.3: Add Inbox and Outbox Service Methods

Adding MessageParams Class

We'll add a new class to the .Models project called MessageParams.cs.

This will hold paging information about our messages as well as the MessageContainer - whether it is an 'Inbox', 'Outbox', or 'Unread'. We'll set them to 'Unread' as default.

As we work through this, we'll have to be careful with the UserId.

  • If the MessageContainer is 'Unread' - we're requesting messages for the recipient so the UserId will equal the RecipientId.

  • If the MessageContaineris 'Outbox' - we'll match the UserId with the SenderId.

public class MessageParams
{
    public int PageNumber { get; set; } = 1;
    private const int  MaxPageSize = 50;
    private int pageSize = 10;

    public int PageSize
    {
        get { return pageSize; }
        set { pageSize = (value > MaxPageSize) ? MaxPageSize : value; }
    }

    public int UserId { get; set; }
    public string MessageContainer { get; set; } = "Unread";
}

Implement GetMessagesForUser() Method

First, we'll update the method signature in the IMessageService() interface. Before, we didn't have the MessageParams class we needed for the parameter:

Task<PagedList<MessageToReturn>> GetMessagesForUser(MessageParams messageParams);

Next, in MessageService.cs - we'll implement the GetMessagesForUser() method:

public async Task<PagedList<MessageToReturn>> GetMessagesForUser(MessageParams messageParams)
{
    var messages = _context.Messages                                                                    //  1.
                        .Include(u => u.Sender).ThenInclude(p => p.Photos)
                        .Include(u => u.Recipient).ThenInclude(p => p.Photos)
                        .AsQueryable();

    switch (messageParams.MessageContainer)
    {
        case "Inbox":                                                                                   //  2a.
            messages = messages.Where(u => u.RecipientId == messageParams.UserId &&
                u.RecipientDeleted == false);
            break;
        case "Outbox":                                                                                  //  2b.
            messages = messages.Where(u => u.SenderId == messageParams.UserId &&
                u.SenderDeleted == false);
            break;
        default:    // "Unread"                                                                         //  2c.
            messages = messages.Where(u => u.RecipientId == messageParams.UserId &&
            u.RecipientDeleted == false &&
            u.IsRead == false);
            break;
    }

    var messagesToReturn = messages                                                                     //  3.
                            .Select(u => new MessageToReturn
                            {
                                Id = u.Id,
                                SenderId = u.SenderId,
                                SenderKnownAs = u.Sender.KnownAs,
                                SenderPhotoUrl = u.Sender.Photos.FirstOrDefault(p => p.IsMain).Url,
                                RecipientId = u.RecipientId,
                                RecipientKnownAs = u.Recipient.KnownAs,
                                RecipientPhotoUrl = u.Recipient.Photos.FirstOrDefault(p => p.IsMain).Url,
                                Content = u.Content,
                                IsRead = u.IsRead,
                                DateRead = u.DateRead,
                                DateSent = u.DateSent
                            })
                            .OrderByDescending(d => d.DateSent);

    return await PagedList<MessageToReturn>                                                             //  4.
                    .CreateAsync(messagesToReturn, messageParams.PageNumber, messageParams.PageSize);
}

There is a lot going on in this method. We'll try to break it down again.

  1. We're querying the database for messages. We need to include both the recipient and the photos for each of them.

  2. A switch statement to check what the values for the message parameters being passed are.

    • If it's 'Inbox' - we grab the messages matching where the UserId matches the recipient Id and where the recipient hasn't deleted them.

    • If it's 'Outbox' - we grab the messages matching where the UserId matches the sender Id and where the sender hasn't deleted them.

    • If it's the default ('Unread') - we grab the messages matching where the UserId matches the recipient Id, where the recipient hasn't deleted them, and where the IsRead property is false.

  3. We're shaping the messages into a MessageToReturn DTO. If we didn't do that - then all of the information about the Users and the Photos would be included in what we send back. That's far too much to send back - and, while these mappings can be a bit of a headache (particularly to grab the photo Urls) - it's less of a headache to do it once here than have to handle it in Angular on the frontend.

  4. Finally, we return a PagedList of MessageToReturns.

Add GetMessagesForUser() Controller Method

In MessagesController.cs - we'll add a new GET method called GetMessagesForUser:

[HttpGet]
public async Task<IActionResult> GetMessagesForUser(int userId, MessageParams messageParams)
{
    if (userId != int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value))
        return Unauthorized();

    var messages = await _messageService.GetMessagesForUser(messageParams);

    Response.AddPagination(
            messages.CurrentPage,
            messages.PageSize,
            messages.TotalCount,
            messages.TotalPages);

    return Ok(messages);
}

Testing in Postman

Now, let's test in Postman. You may need to check your database if you don't remember what messages you've already sent - you can can practice in Postman and send some more.

Since I already have a token for the sender of the last message - I'll start out by getting the Outbox for that User by sending a GET request to:

http://localhost:5000/api/users/{userId}/messages?MessageContainer=Outbox

(remember to update your UserId).

You should get back a 200 OK response:

You can also check the headers in the response to ensure the pagination is set up correctly:

We also should be able to login and get a token for the recipient and check the Unread:

And the Inbox:

We currently aren't setting the 'isRead' property to true when a user views their message - so, for now these will be the same.

Last updated