MVC 6

ASP.NET 5 Web API Put

As part of last week’s post on Aurelia’s Event Aggregator I needed a way to update existing contacts. This was a hole in my existing API and it was time to fill it.  See last week’s post for usage on the client side this post will only be covering the server side.

First off here is the code I added to the ContactsController in the API folder of my project. I thought the overview would be helpful a helpful context as we break down the different parts of the function.

[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, [FromBody] Contact updatedContact)
{
    if (updatedContact == null || updatedContact.Id != id)
    {
        return HttpBadRequest();
    }

    var contact = await GetContacts()
                        .Where(c => c.Id == id).FirstOrDefaultAsync();

    if (contact == null)
    {
        return HttpNotFound();
    }

    if (!ModelState.IsValid)
    {
        return new BadRequestObjectResult(ModelState);
    }

    _dbContext.Entry(contact).State = EntityState.Detached;
    _dbContext.Update(updatedContact);
    updatedContact.UserId = User.GetUserId();  
    await _dbContext.SaveChangesAsync();

    return CreatedAtRoute("GetById",
                          new
                          {
                              controller = "Contacts",
                              id = updatedContact.Id
                          },
                          updatedContact);
}

First off the Update function is decorated to only handle HttpPut actions with an ID. The function itself of course requires an ID. The FormBody attribute on the updatedContact parameter tells the framework to build a contact object from the request body.

[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, [FromBody] Contact updatedContact)

Next if the updatedContact was unable to be built from the request body or if the ID passed to the update function doesn’t match the ID in the updatedContact then a bad request status is returned.

if (updatedContact == null || updatedContact.Id != id)
{
    return HttpBadRequest();
}

Next I query the database for a matching contact. This query is limited to the current users so it should help prevent a contact for the wrong user being updated. If no matching contact is found then not found status is returned.

var contact = await GetContacts()
                    .Where(c => c.Id == id).FirstOrDefaultAsync();

if (contact == null)
{
    return HttpNotFound();
}

Next check to see if the mode state is valid and return bad request object result if it is not.

if (!ModelState.IsValid)
{
    return new BadRequestObjectResult(ModelState);
}

This next bit was a little bit of a challenge. Since entity framework was queried to see if the updated contact already exists I was getting an error when trying to use the DB context’s update function. One way to address this error would have been to update the contact pulled from the database with the values from the updated contact from the request body. If I had of gone this route something like automapper would have made the job fairly easy. Instead I used the DB context entry on the contact returned from the database to set its state to detached which tells the context to stop tracking that item. After that the context’s update function worked like a charm and I could save changes based on the updatedContact parameter.

_dbContext.Entry(contact).State = EntityState.Detached;
_dbContext.Update(updatedContact);
updatedContact.UserId = User.GetUserId();
await _dbContext.SaveChangesAsync();

Finally I returned the client the updated contact. This is the same thing I did for post and may or may not be the best thing to do on a put.

return CreatedAtRoute("GetById",
                      new
                      {
                          controller = "Contacts",
                          id = updatedContact.Id
                      },
                      updatedContact);

I am not sure how much of this is the “correct” way to handle a put. After seeing how some of this is handled when using razor with anti-forgery tokens and the ability to only allow certain fields to be updated this implementation seems lacking. This is a jumping off point, but I need to do more research on how to properly secure an API.

Start Aurelia from an ASP.NET 5 Controller

Last week was all about getting Aurelia up and running from inside of Visual Studio with an ASP.NET 5 project. In last week’s post I started Aurelia from an html page in the wwwroot folder. This week I am going to use a controller action to kick off Aurelia.

Start by modifying the default HomeController, or any controller of your choice, to add an Aurelia action that just returns a view.

public IActionResult Aurelia()
{
    return View();
}

Next right-click on the View/Home folder and add a new item.

AddNewItem

Select MVC View Page and enter a name that matches the above controller action, Aurelia.cshtml for this example.

AddNewItemDialog

The contents of Aurelia.cshtml are very close to the html page from last week except for a couple of items. The aurelia-app is on a div instead of the body and the script source has been changed to go up a folder level since the view will be running out of the Home folder but the scripts will still be in the root of the site when it is being served.

<div aurelia-app>
    <script src="../jspm_packages/system.js"></script>
    <script src="../config.js"></script>
    <script>
        System.import("aurelia-bootstrapper");
    </script>
</div>

Now change _Layout.cshtml in the Views/Shared folder to provide a link to the new Aurelia action. In this case it is being added to the navigation bar.

<div class="navbar-collapse collapse">
    <ul class="nav navbar-nav">
        <li><a asp-controller="Home" asp-action="Index">Home</a></li>
        <li><a asp-controller="Home" asp-action="Aurelia">Aurelia</a></li>
        <li><a asp-controller="Home" asp-action="About">About</a></li>
        <li><a asp-controller="Home" asp-action="Contact">Contact</a></li>
    </ul>
    @await Html.PartialAsync("_LoginPartial")
</div>

A couple of use cases for this might be to split a really large application into multiple sub-spas to keep user from having to load data for parts of the application they may use infrequently. I bet this same concept could be achieved with Aurelia itself, but with an existing application the approach above maybe an easier way to start.

ASP.NET 5 Getting and Storing User Identity

My contacts application has login options but does not currently tie data to a specific user. In this post I am going to fix that problem.  Add a new user ID property to the Contact class for storing the user’s ID.

public string UserId { get; set; }

The change to contact model needs to be applied to the database. To do this use the following dnx command to add a new entity framework migration.

dnx . ef migration add AddContactUser --context ContactsDbContext

At this point a run of the application will apply the migration to the database. This is assuming use of Database.AsRelational().ApplyMigrations() in the constructor of the appropriate data context, if not then dnx can be used to apply the migration.

The ContactsContoller now needs to require a logged in user as well as get the ID for the logged in user. The following two using statements are need to be added to support these operations.

using System.Security.Claims;
using Microsoft.AspNet.Authorization;

Microsoft.AspNet.Authorization provides access to the authorize attribute which is used to require an authorized used. The authorize attribute can be used a the  class or on individual functions. This example is at the class level.

[Authorize]
public class ContactsController : Controller

System.Security.Claims provides an extension method on the controller’s User property to get the current user’s ID. The following is the code from the index action for the ContactsController with the added filter for user ID on the contacts query.

public IActionResult Index(string filter)
{
    var contacts = from c in _db.Contacts
                   where c.UserId == User.GetUserId()
                   select c;

    if (!string.IsNullOrEmpty(filter))
    {
        contacts = contacts.Where(c => c.Name.Contains(filter) ||
                                       c.Address.Contains(filter) ||
                                       c.City.Contains(filter) ||
                                       c.Email.Contains(filter) ||
                                       c.Phone.Contains(filter) ||
                                       c.State.Contains(filter) ||
                                       c.ZipCode.Contains(filter));
    }

    if (Request?.Headers != null && 
        Request.Headers["X-Requested-With"] == "XMLHttpRequest")
    {
        return PartialView("_ContactList",contacts);
    }

    return View(contacts);
}

A similar change needs to be made on all the actions in the ContactsController. For example here is the user ID being used as part of the create action.

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(Contact contact)
{
    if (ModelState.IsValid)
    {
        contact.UserId = User.GetUserId();
        _db.Contacts.Add(contact);
        _db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(contact);
}

This method works fine but I am not sure if only using the User ID on the server-side is best practice or not. The other option would be to send the user ID to the client as a hidden field which would get returned when the user posted changes. It seems that forcing the User ID on the contact model on the server-side would prevent it from be tampered with, but this is a topic that I need to research further.

AJAX with ASP.NET 5

The next feature I wanted to handle moving my contacts application from ASP.NET 4 to ASP.NET 5 was not doing a full page refresh when applying a filter. For my implementation in ASP.NET 4 check out the Partial View and Partial View with AJAX posts.

For the partial view there is currently no menu choice but it is easy enough to create the view manually. In fact my _ContactList.cshtml looks exactly as it did in ASP.NET 4.

Rendering a partial view has changed slightly to use an async using await. The following is what replaced the contact list display in the contact index page.

<div id="contactList">
    @{
        await Html.RenderPartialAsync("_ContactList");
    }
</div>

At this point I was ready to add in AJAX to refresh my partial view when the user changes filters. In ASP.NET 4 there was the Ajax.BeginForm helper for making Ajax requests, but this does not currently exist in ASP.NET 5.

After a lot of searching I came across the jquery-ajax-unobtrusive github repo. This is the same library that was being used by Ajax.BeginForm ASP.NET 4 and it is available using Bower which ASP.NET 5 and Visual Studio 2015 have great support for.

Just by adding the following line to the dependencies section of my project’s bower.json the needed files were automatically download. The great thing about making edits in bower.json is that intellisense works!

"jquery-ajax-unobtrusive": "3.2.3"

The next step was to add the following line to the copy task in my project’s gulpfile.js.

"jquery-validation-unobtrusive": "jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"

I spent a good bit of time getting the gulp task to work. This is my first usage of gulp and at this point I still don’t know if there is an indicator that the copy failed other than the file the file not being at the specified location. My problem ended up being a typo of a dot instead of a dash.

With jquery-ajax-unobtrusive.js file in place the script needs to be include in _Layout.cshtml found in the Views\Shared folder. In _Layout.cshtml you will notice sections have been added for different environments. For the time being I have only change the scripts Development section with the following.

<script src="~/lib/jquery-ajax-unobtrusive/jquery.unobtrusive-ajax.js"></script>

The Staging and Production environment share the same set of script sources by default but can be separated if needed. The default setup also uses a CDN to pull scripts from by default with fallbacks to the local version. Before an actual deployment make sure to include jquery-ajax-unobtrusive.js in the Staging and Production environments.

Back in Contacts\Index.cshtml the filter form needs to change to the following.

@using (Html.BeginForm("Index", "Contacts", FormMethod.Post, new
{
    id = "filterButton",
    data_ajax = "true",
    data_ajax_method = "POST",
    data_ajax_mode = "replace",
    data_ajax_update = "#contactList"
}))
{
    <p>
        @Html.TextBox("Filter")
        <input type="submit" value="Filter"/>
    </p>
}

This is the standard Html.BeginForm with some added HTML attributes to support the Ajax call. The data_ajax* work because of jquery-ajax-unobtrusive. To get the values needed I ran the ASP.NET 4 version of my application and used view source to determine what values I needed. If you take the approach you will notice in the view source from the browser the data attributes will contain dashes which need to be changed to underscores for use in a razor view.

The last bit that needs to change is the index action of the contacts controller.

public IActionResult Index(string filter)
{
   var contacts = from c in _db.Contacts
                  select c;

   if (!string.IsNullOrEmpty(filter))
   {
     contacts = contacts.Where(c => c.Name.Contains(filter) ||
                                    c.Address.Contains(filter) ||
                                    c.City.Contains(filter) ||
                                    c.Email.Contains(filter) ||
                                    c.Phone.Contains(filter) ||
                                    c.State.Contains(filter) ||
                                    c.ZipCode.Contains(filter));
   }

   if (Request?.Headers != null && 
       Request.Headers["X-Requested-With"] == "XMLHttpRequest")
   {
     return PartialView("_ContactList",contacts);
   }

   return View(contacts);
}

The only gotcha here when moving from ASP.NET 4 is that the request object does not currently contain an IsAjaxRequest extension. Checking the X-Requested-With key of the request headers for XMLHttpRequest indicates an ajax request and triggers the return of the _ContactList partial view.

You should now have a working ajax request. It took me a while to get this all worked out I hope it saves you some time. Information on ASP.NET 5 can be sparce, but this will improve over time.