ASP.NET Core

Log Requests and Responses in ASP.NET Core

Note: An updated version of this post for ASP.NET Core 3 and above is available.

As part of trying to do some debugging, I needed a way to log the requests and responses. Writing a piece of middleware seemed to be a good way to handle this problem. It also turned out to be more complicated that I had expected to deal with the request and response bodies.

Middleware

In ASP.NET Core middleware are the components that make up the pipeline that handles request and responses for the application. Each piece of middleware called has the option to do some processing on the request before calling next piece of middleware in line. After execution returns from the call to the next middleware, there is an opportunity to do processing on the response.

The pipeline for an application is set in the Configure function of the Startup class. Run, Map and Use are the three types of middleware. Run should only be used to terminate the pipeline. Map is used for pipeline branching. Use seems to be the most common type of middleware that does some processing and call the next middleware in line. For more detail see the official docs.

Creating Middleware

Middleware can be implemented as a lambda directly in the Configure function, but more typically it is implemented as a class that is added to the pipeline using an extension method on IApplicationBuilder. This example will be going the class route.

This example is a piece of middleware that using ASP.NET Cores built-in logging to log requests and responses. Create a class called RequestResponseLoggingMiddleware.

The class will need a constructor that takes to arguments both will be provided by ASP.NET Core’s dependency injection system. The first is a RequestDelegate for the next piece of middleware to be called. The second is an ILoggerFactory which will be used to create a logger. The RequestDelegate is stored to the class level _next variable and the loggerFactory is used to create a logger which is stored to the class level _logger variable.

public class RequestResponseLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    public RequestResponseLoggingMiddleware(RequestDelegate next,
                                            ILoggerFactory loggerFactory)
    {
        _next = next;
        _logger = loggerFactory
                  .CreateLogger<RequestResponseLoggingMiddleware>();
    }
}

Add an Invoke function which is the function that will be called when your middleware is run by the pipeline. The following is the function that does nothing other than call the next middleware in the pipeline.

public async Task Invoke(HttpContext context)
{
     //code dealing with request

     await _next(context);

     //code dealing with response
}

Next, add a static class to simplify adding the middleware to the application’s pipeline. This is the same pattern the built-in middleware uses.

public static class RequestResponseLoggingMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestResponseLogging(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestResponseLoggingMiddleware>();
    }
}

Adding to the pipeline

To add the new middleware to the pipeline open the Startup to the Configure function and add the following line.

app.UseRequestResponseLogging();

Keep in mind that the order in which middleware is added can make a difference in how the application behaves. Since the middleware this post is dealing with is logging I have placed it near the begging of the pipeline just before app.UseStaticFiles().

Logging requests and responses

Now that the setup work for our new middleware is done we will come back to its Invoke function. As I stated above this ended up being more complicated that I expected, but thankfully I found this by Sul Aga which really helped me work through the issues I was having.

I created a couple of helper functions that we will look at first. The following is the function call to create the string that will be logged for a request.

private async Task<string> FormatRequest(HttpRequest request)
{
    var body = request.Body;
    request.EnableRewind();

    var buffer = new byte[Convert.ToInt32(request.ContentLength)];
    await request.Body.ReadAsync(buffer, 0, buffer.Length);
    var bodyAsText = Encoding.UTF8.GetString(buffer);
    request.Body = body;

    return $"{request.Scheme} {request.Host}{request.Path} {request.QueryString} {bodyAsText}";
}

The key to getting this function to work and allow reading of the request body was request.EnableRewind() which allows us to read from the beginning of the stream. The rest of the function is pretty straight forward.

The next function is used to get the string to that will be used to log the response body. This function looks simpler than it is and only works because of how it is called from the Invoke function.

private async Task<string> FormatResponse(HttpResponse response)
{
    response.Body.Seek(0, SeekOrigin.Begin);
    var text = await new StreamReader(response.Body).ReadToEndAsync(); 
    response.Body.Seek(0, SeekOrigin.Begin);

    return $"Response {text}";
}

Finally, the Invoke which does the logging and jumps through some hoops to allow the response body to be read.

public async Task Invoke(HttpContext context)
{
    _logger.LogInformation(await FormatRequest(context.Request));

    var originalBodyStream = context.Response.Body;

    using (var responseBody = new MemoryStream())
    {
        context.Response.Body = responseBody;

        await _next(context);

        _logger.LogInformation(await FormatResponse(context.Response));
        await responseBody.CopyToAsync(originalBodyStream);
    }
}

As you can see the trick to reading the response body is replacing the stream being used with a new MemoryStream and then copying the data back to the original body steam. This works and is a concept I found in Sul’s blog post. I don’t know how much the affect performance and would make sure to study how it scales or just avoid using it in production as much as possible.

Wrapping up

This entry didn’t turn out anything like I expected. I came into this looking to do a very simple post due to some time restrictions and it turned into something larger. I hope you find it helpful. I didn’t do a full repo for this week’s post, but you can a gist with the middleware classes here.

Log Requests and Responses in ASP.NET Core Read More »

Email with ASP.NET Core Using Mailgun

Sending emails from ASP.NET Core using Mailgun is a topic I covered in this post almost a year ago. The previous post was before ASP.NET Core hit 1.0 and I didn’t save or upload the code to GitHub. Based on the comments on the post I decided to redo the post using the current version of ASP.NET.

Starting point

For this post, I created a new ASP.NET Core Web Application targeting .NET Core using Individual User Accounts for authentication. The project before any changes for email can be found here.

UI and Controller change to support email

The project template comes with all the UI and controller functions need to support email, but they are commented out. The following is going to walk through uncommenting the proper code.

Account controller

Starting with the Register function the code to send a confirmation email needs to be uncommented and the existing call to _signInManager.SignInAsync should be commented out to keep a user from being signed in before their email address has been confirmed. The following is after the changes.

var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action("ConfirmEmail", "Account", 
                             new { userId = user.Id, code = code }, 
                             protocol: HttpContext.Request.Scheme);
await _emailSender.SendEmailAsync(model.Email, "Confirm your account",
   $"Please confirm your account by clicking this link: 
     <a href='{callbackUrl}'>link</a>");
//await _signInManager.SignInAsync(user, isPersistent: false);

Next, in the Login function add a check to verify a user’s account has been confirmed before allowing them to sign in. The new code starts with var user = await _userManager.FindByNameAsync(model.Email); the code above it is just to provide context.

 public async Task<IActionResult> Login(LoginViewModel model, 
                                        string returnUrl = null)
 {
      ViewData["ReturnUrl"] = returnUrl;
      if (ModelState.IsValid)
      {
          var user = await _userManager.FindByNameAsync(model.Email);
          if (user != null)
          {
              if (!await _userManager.IsEmailConfirmedAsync(user))
              {
                  ModelState.AddModelError(string.Empty, 
                              "You must have a confirmed email to log in.");
                  return View(model);
              }
          }

Finally, in the ForgotPassword function uncomment the following to enable sending the user a password reset link.

var code = await _userManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.Action("ResetPassword", "Account", 
                             new { userId = user.Id, code = code }, 
                             protocol: HttpContext.Request.Scheme);
await _emailSender.SendEmailAsync(model.Email, "Reset Password",
   $"Please reset your password by clicking here: <a          
     href='{callbackUrl}'>link</a>");
return View("ForgotPasswordConfirmation");
Forgot password view

To enabled the UI related to sending an email for a forgotten password open ForgotPassword.cshtml found in the Views/Account/ directory and uncomment the following.

<form asp-controller="Account" asp-action="ForgotPassword" 
      method="post" class="form-horizontal">
    <h4>Enter your email.</h4>
    <hr />
    <div asp-validation-summary="All" class="text-danger"></div>
    <div class="form-group">
        <label asp-for="Email" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="Email" class="form-control" />
            <span asp-validation-for="Email" class="text-danger"></span>
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
             <button type="submit" 
                     class="btn btn-default">Submit</button>
        </div>
    </div>
</form>

Warning for sites with existing users

The changes above will cause issues for any existing users since they will not have completed the email confirmation step keeping them from being able to log in or reset passwords. Manually marking existing users as confirmed can be done by updating the EmailConfirmed bit field to true in the AspNetUsers table.

Mailgun

Mailgun is an email service run by Rackspace that provides a simple API for sending emails. The free level of the service allows up to 10k emails to be sent a month.

You can sign up for an account here. Once logged in go to the Domains section.

Next, select your domain should only be one if you are on a new account. This will take you to a screen that looks like the following some of which will be needed to connect with the Mailgun API. I took the time to replace my information with a fake version so this screen shot could be referenced using the values from the screenshot for the rest of the post.

Configuration

Settings class

In order to hold and load Mailgun email related settings add a new EmailSettings class. In the sample project, this class can be found in the Configuration directory. The following is the full contents of the file.

public class EmailSettings
{
    public string ApiKey { get; set; }
    public string ApiBaseUri { get; set; }
    public string RequestUri { get; set; }
    public string From { get; set; }
}
User secrets introduction

User secrets is a concept in ASP.NET Core used to set configuration items and have them stored outside of the project so they will be excluded from version control. They are a great way to store private API key and related items which is why they will be used to store our Mailgun configuration items. I will be coving the basics here, but for a more detail explanation check out the official docs on the topic of app secrets.

Setting user secrets

In the Solution Explorer right-click on the project and select Manage User Secrets.

This will open the secrets.json file which will be used to store secrets related to the select project. Keep in mind this file is stored in your user directory in an unencrypted way so don’t view it as a secured store.

Based on the screenshot above from Mailgun’s domain detail page the json file would look like the following. The RequestUri is the only setting not pulled from the domain settings above and would just need fakesandbox replaced with the sandbox ID for your domain.

{
  "EmailSettings": {
    "ApiKey": "api:key-fakeapikey",
    "ApiBaseUri": "https://api.mailgun.net/v3/",
    "RequestUri": "fakesandbox.mailgun.org/messages",
    "From": "[email protected]"
  }
}
Loading user secrets in Startup

In the ConfigureServices function of the Startup class the EmailSettings section of our user secrets can be loaded and made available via the dependency injection system using the following line of code.

services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));

Not that user secrets are only meant to be used for development and for a production build of the applications the settings would need to be moved to a different location such as environment variables or Azure Key Vault.

Using Mailgun to send email

Not that the application has the email sending portion of the UI enabled the SendEmailAsync function of the AuthMessageSender class needs to be implemented. The class can be found in the MessageServices.cs file of the Services directory.

Injection of email settings

The first change needed is to add a class level variable to store email settings and to add a constructor that will allow the email setting to be injected.

private readonly EmailSettings _emailSettings;

public AuthMessageSender(IOptions<EmailSettings> emailOptions)
{
    _emailSettings = emailOptions.Value;
}
Sending an email

The body of the SendEmailAsync function is where the call to Mailgun’s API will be made using the email setting injected via the class’s constructor. The following is the full body of the function.

using (var client = new HttpClient { BaseAddress = 
                                     new Uri(_emailSettings.ApiBaseUri) })
{
    client.DefaultRequestHeaders.Authorization = 
      new AuthenticationHeaderValue("Basic",
    Convert.ToBase64String(Encoding.ASCII.GetBytes(_emailSettings.ApiKey)));

    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("from", _emailSettings.From),
        new KeyValuePair<string, string>("to", email),
        new KeyValuePair<string, string>("subject", subject),
        new KeyValuePair<string, string>("html", message)
    });

    await client.PostAsync(_emailSettings.RequestUri, 
                           content).ConfigureAwait(false);
}

The Mailgun API key is sent as an authentication header value with the rest of the parameters being sent via form URL encoded content. Finally, the Request URI from the email settings is used to send the post request to Mailgun.

If you just need to send a plain text instead of HTML the “html” key can be replaced with “text”.

Authorized recipients required for test domain

When using Mailgun with the default test domain note that only emails addressed to Authorized Recipients will be delivered. To add a recipient click Authorized Recipients button on Mailgun’s Domains page.

This will take you to the Authorized Recipients page where you can use the Invite New Recipient button to add a recipient.

Enter the email address you want to add and click the Send Invite button. After the email address is confirmed mail to that address will be delivered. Keep in mind this is only for test accounts and doesn’t have to be done when being used with a real domain.

Using logs to verify state of emails

During my testing, I wasn’t seeing emails come through and I thought something was wrong with my code, but it turned out that Mailgun was getting the request to send the mail they just hadn’t be processed yet. The Logs section of your Mailgun account is helpful for determining if they are getting your request to send an email or not.

As you can see in the screenshot the email request was accepted, but not yet delivered. It was 10 minutes later before the email was actually delivered. I am not sure if this delay is just for test domains or if would apply to live ones as well.

Other Email Options

Mailgun is obviously not the only option for sending emails. This post from Mashape lists 12 API providers. In addition SMTP is also an option which this post by Steve Gordon covers.

Wrapping up

The code finished code that goes with this post can be found here. Thank you to all the commenters on the original post for stepping in when I didn’t have all the answer or code available.

Email with ASP.NET Core Using Mailgun Read More »

Angular 2 Contact Creation and Post to an API

Expanding on this post which created a placeholder for creating new contacts this post will create an actual UI and post the newly created contact to an ASP.NET Core API. The code before any changes can be pulled using this release tag. Keep in mind all changes in this post take place in the Angular project if you are using the associated sample.

Contact service changes to all post

The contract service found in the contact.service.ts file of the ClientApp/app/components/contacts/ directory is used to encapsulate interaction with the associated ASP.NET Core API and prevent access to Angular’s Http library from being spread across the application. The first change is to expand the existing import of Angular’s Http library to include Headers and RequestOptions.

import { Http, Headers, RequestOptions } from '@angular/http';

Next, a save is added that makes a post request to the ASP.NET Core API with the body set to a JSON version of the contact to be added.

save(contact: Contact): Promise<Contact> {
   let headers = new Headers({ 'Content-Type': 'application/json' });
   let options = new RequestOptions({ headers: headers });

   return this.http.post(this.baseUrl, JSON.stringify(contact), options)
        .toPromise()
        .then(response => response.json())
        .then(contact => new Contact(contact))
        .catch(error => console.log(error));
}

The API will return the created contact with the ID now filled in which will be returned to the caller. As I have said before catching the error and just writing it to the console isn’t the proper way to handle errors and this should be done in a different manner for a production application.

Contact detail view model

The view model that backs the contact detail view needed a function to allow saving of a contact. The following code uses the contact service to save a contact and then replace its local contact with the new one returned from the API. Finally, the class level variable indicating if the view model is in create or detail mode is set to true which triggers the UI to change out of create mode.

save() {
     this.contactService.save(this.contact)
         .then(contact => this.contact = contact)
         .then(() => this.hasContactId = true);
}

You will see in the sample code that a second function was added for reset which is a quick way to reset the create UI.

reset() {
    this.contact = new Contact();
}

Contact model

The constructor of the contact class changed to make the data parameter optional to allow for the creation of an empty contact.

constructor(data?) {
    if (data == null) return;
    Object.assign(this, data);
}

Include Angular Forms

For the two-way binding needed on the contact detail page Angular forms will be used. To include them in the project open the app.module.ts file in the ClientApp/app/ directory. Add the following import.

import { FormsModule } from '@angular/forms';

Then add FormsModule to the imports array.

imports: [
     UniversalModule,
     FormsModule,
     RouterModule.forRoot([

Contact detail view

The following is the full contact view as it stands with all of the needed changes made. This will be followed by call outs of some of the important items.

 <h1>Contact Details</h1>
  <hr />
  <div *ngIf="hasContactId">
      <dl class="dl-horizontal">
          <dt>ID</dt>
          <dd>{{contact.id}}</dd>
         <dt>Name</dt>
         <dd>{{contact.name}}</dd>
         <dt>Address</dt>
         <dd>{{contact.getAddress()}}</dd>
         <dt>Phone</dt>
         <dd>{{contact.phone}}</dd>
         <dt>Email</dt>
          <dd>{{contact.email}}</dd>
      </dl>
  </div>
 <div *ngIf="!hasContactId">
     <div>
         <form role="form" class="form-horizontal">
             <div class="form-group">
                 <label class="col-sm-2 control-label">Name</label>
                 <div class="col-sm-10">
                     <input type="text" 
			    placeholder="name" 
			    class="form-control" 
                            [(ngModel)]="contact.name" 
			    name="name" />
                 </div>
             </div>
             <div class="form-group">
                 <label class="col-sm-2 control-label">Address</label>
                 <div class="col-sm-10">
                     <input type="text" 
			    placeholder="address" 
			    class="form-control" 
			    [(ngModel)]="contact.address" 
			    name="address" />
                 </div>
             </div>
             <div class="form-group">
                 <label class="col-sm-2 control-label">City</label>
                 <div class="col-sm-10">
                     <input type="text" 
			    placeholder="city" 
			    class="form-control" 
			    [(ngModel)]="contact.city" 
			    name="city" />
                 </div>
             </div>
             <div class="form-group">
                 <label class="col-sm-2 control-label">State</label>
                 <div class="col-sm-10">
                     <input type="text" 
			    placeholder="state" 
			    class="form-control" 
			    [(ngModel)]="contact.state" 
			    name="state" />
                 </div>
             </div>
             <div class="form-group">
                 <label class="col-sm-2 control-label">Zip</label>
                 <div class="col-sm-10">
                     <input type="text" 
			    placeholder="zip" 
			    class="form-control" 
			    [(ngModel)]="contact.postalCode" 
			    name="postalCode" />
                 </div>
             </div>
             <div class="form-group">
                 <label class="col-sm-2 control-label">Phone</label>
                 <div class="col-sm-10">
                     <input type="text" 
			    placeholder="phone" 
			    class="form-control" 
			    [(ngModel)]="contact.phone" 
			    name="phone" />
                 </div>
             </div>
             <div class="form-group">
                 <label class="col-sm-2 control-label">Email</label>
                 <div class="col-sm-10">
                     <input type="email" 
			    placeholder="email" 
			    class="form-control" 
			    [(ngModel)]="contact.email" 
			    name="email" />
                 </div>
             </div>
         </form>
     </div>
     <div class="text-center">
         <button class="btn btn-success btn-lg" 
		 (click)="save()">Save</button>
         <button class="btn btn-danger btn-lg" 
		 (click)="reset()">Reset</button>
     </div>
 </div>
  <a routerLink="/contact-list">Back to List</a>
  <hr />

All control of content rendering has been changed to use hasContactId.

Default view:
<div *ngIf="hasContactId">

Create view:
<div *ngIf="!hasContactId">

For the creation UI, the data is bound using Angular’s ngModel binding.

<input type="text" 
       placeholder="address" 
       class="form-control" 
       [(ngModel)]="contact.address" 
       name="address" />

If you have any issues make sure and check that you have the name attribute set to the property you are wanting to bind to.

The last thing to point out is the click handlers that are used to call the associate save and rest functions with the Save and Reset buttons are clicked.

<button class="btn btn-success btn-lg" 
        (click)="save()">Save</button>
<button class="btn btn-danger btn-lg" 
        (click)="reset()">Reset</button>

Wrapping up

Now the application has the ability to add contact not just view them which is one step closer to what would be needed for a real application. The finished code can be found here.

Angular 2 Contact Creation and Post to an API Read More »

Aurelia Contact Creation and Post to an API

Expanding on this post where a placeholder was added for contact creation the placeholder will be replaced with an actual UI. As part of the contact creation process, Aurelia’s fetch client will be used to make a post request to the ASP.NET API. The code at the starting point can be found here. If using the sample code keep in mind all the changes in this post takes place in the Aurelia project.

Contact service changes to allow post

In this project a service is used to keep all the Http bits isolated from the rest of the application. In the contactService.ts file found in the ClientApp/app/components/contacts/ directory a couple of changes need to be made. First the fetch import needs to expose json in addition to HttpClient.

import { HttpClient, json } from 'aurelia-fetch-client';

Then a save function is added that makes a post request to the ASP.NET API and return a new contact based on the response from the post request. The contact in the post response will contain the ID assigned by the API.

save(contact: Contact): Promise<Contact> {
    return this.http.fetch('',
        {
            method: 'post',
            body: json(contact)
        })
        .then(response => response.json())
        .then(contact => new Contact(contact))
        .catch(error => console.log(error));
}

Notice the usage of json to serialize the contact being create to JSON before sending to the server. Also, note that just logging an error to the console isn’t a best practice and should be handling in a different way in a production application.

Contact detail view model

The view model that backs the contact detail view needed a function to allow saving of a contact. The following code uses the contact service to save a contact and then replace its local contact with the new one returned from the API. Finally, the class level variable indicating if the view model is in create or detail mode is set to true which triggers the UI to change out of create mode.

save() {
     this.contactService.save(this.contact)
         .then(contact => this.contact = contact)
         .then(() => this.hasContactId = true);
}

You will see in the sample code that a second function was added for reset which is a quick way to reset the create UI.

reset() {
    this.contact = new Contact();
}

Contact model

The constructor of the contact class changed to make the data parameter optional to allow for the creation of an empty contact.

constructor(data?) {
    if (data == null) return;
    Object.assign(this, data);
}

Contact detail view

The view under when the most changes and the following is the entirety of the UI file which can be found in the contactDetail.html file. The code will be followed up with call outs for the important bits.

 <template>
      <h1>Contact Details</h1>
      <hr />
      <div if.bind="hasContactId">
          <dl class="dl-horizontal">
              <dt>ID</dt>
              <dd>${contact.id}</dd>
             <dt>Name</dt>
             <dd>${contact.name}</dd>
             <dt>Address</dt>
             <dd>${contact.getAddress()}</dd>
             <dt>Phone</dt>
             <dd>${contact.phone}</dd>
             <dt>Email</dt>
              <dd>${contact.email}</dd>
          </dl>
      </div>
     <div if.bind="!hasContactId">
         <div>
             <form role="form" class="form-horizontal">
                 <div class="form-group">
                     <label class="col-sm-2 control-label">Name</label>
                     <div class="col-sm-10">
                         <input type="text" 
                                placeholder="name" 
                                class="form-control" 
                                value.bind="contact.name" />
                     </div>
                 </div>
                 <div class="form-group">
                     <label class="col-sm-2 control-label">Address</label>
                     <div class="col-sm-10">
                         <input type="text" 
                                placeholder="address" 
                                class="form-control" 
                                value.bind="contact.address" />
                     </div>
                 </div>
                 <div class="form-group">
                     <label class="col-sm-2 control-label">City</label>
                     <div class="col-sm-10">
                         <input type="text" 
                                placeholder="city" 
                                class="form-control" 
                                value.bind="contact.city" />
                     </div>
                 </div>
                 <div class="form-group">
                     <label class="col-sm-2 control-label">State</label>
                     <div class="col-sm-10">
                         <input type="text" 
                                placeholder="state" 
                                class="form-control" 
                                value.bind="contact.state" />
                     </div>
                 </div>
                 <div class="form-group">
                     <label class="col-sm-2 control-label">Zip</label>
                     <div class="col-sm-10">
                         <input type="text" 
                                placeholder="zip" 
                                class="form-control" 
                                value.bind="contact.postalCode" />
                     </div>
                 </div>
                 <div class="form-group">
                     <label class="col-sm-2 control-label">Phone</label>
                     <div class="col-sm-10">
                         <input type="text" 
                                placeholder="phone" 
                                class="form-control" 
                                value.bind="contact.phone" />
                     </div>
                 </div>
                 <div class="form-group">
                     <label class="col-sm-2 control-label">Email</label>
                     <div class="col-sm-10">
                         <input type="email" 
                                placeholder="email" 
                                class="form-control" 
                                value.bind="contact.email" />
                     </div>
                 </div>
             </form>
         </div>
         <div class="text-center">
             <button class="btn btn-success btn-lg" 
                     click.delegate="save()">Save</button>
             <button class="btn btn-danger btn-lg" 
                     click.delegate="reset()">Reset</button>
         </div>
     </div>
     <div>
         <a route-href="route: contactlist">Back to List</a>
     </div>
      <hr />
  </template>

All control of content rendering has been changed to use hasContactId.

Default view:
<div if.bind="hasContactId">

Create view:
<div if.bind="!hasContactId">

For the creation UI, the data is bound using Aurelia’s value converters for more detail see the docs. The value converter is the value.bind bit.

<input type="text" 
       placeholder="name" 
       class="form-control" 
       value.bind="contact.name" />

The last thing to point out is the click delegates that are used to call the associate save and rest functions with the Save and Reset buttons are clicked.

<button class="btn btn-success btn-lg" 
        click.delegate="save()">Save</button>
<button class="btn btn-danger btn-lg" 
        click.delegate="reset()">Reset</button>

Wrapping up

The application now has the ability to add contact instead of only viewing existing contact which brings it close to a more realistic application. The code in its finished state can be found here.

The plan is to continue iterating on this application and moving the Aurelia and Angular 2 projects in parallel. I hope this is useful and if you have any specific features you would like to see implemented leave a comment.

Aurelia Contact Creation and Post to an API Read More »

Angular 2 Optional Route Parameter

This post expands on the Angular 2 post from a couple of weeks ago involving route links and parameters to include an “optional” route parameter. Using the use case from the same topic with Aurelia from last week’s post which is if a user ends up on the contact detail page with no contact ID they will be presented with the option to add a new contact. The starting point for the code can be found here. Keep in mind any changes in this post are taking place in the Angular project.

Route with an “optional” parameter

The reason for the quotes around optional is that with Angular’s current router I have found no way to make a route optional. As a work around two routes can be added that point to the same component. The following code is in the app.module.ts file of the ClientApp/app folder. The first handles calling the contact detail component without an ID and the second makes the call with an ID.

 { path: 'contact-detail', component: ContactDetailComponent },
 { path: 'contact-detail/:id', component: ContactDetailComponent },

Contact detail changes

The contact detail view model found in the contactdetail.component.ts file a class level property is needed to track of the contact detail component was called with an ID or not.

hasContactId: boolean;

Next, in the ngOnInit function has been changed to set the new property based on the route params having an ID set or not. If a contact ID is found then the details for that contact are loaded. The following is the full function.

ngOnInit(): void {
    var contactId: string;
 
    this.route.params
        .subscribe((params: Params) => contactId = params['id']);
 
   this.hasContactId = contactId != undefined;

   if (this.hasContactId) {
       this.contactService.getById(contactId)
           .then((contact: Contact) => this.contact = contact);
   }
}

In the associated view a placeholder for creating a new contact was added in the contactdetail.component.html file. This placeholder was added just above the link back to the contact list. This placeholder will only show if the detail components are loaded with no ID.

<h3 *ngIf="!hasContactId">
    Place holder for creating a new contact
</h3>

Add create link to the contact list

To finish a “Create New Contact” link is added to the contact list view found in the contactlist.component.html file which will call the contact detail component without a contact ID.

<a [routerLink]="['/contact-detail']">Create New Contact</a>

In the example solution, this link will show above the table of contacts.

Wrapping up

The finished code can be found here. If you have tried both Angular 2 and Aurelia leave a comment on how you think they compare and which you prefer.

Angular 2 Optional Route Parameter Read More »

Aurelia Optional Route Parameter

This post expands on the Aurelia post from a couple of weeks ago that involved router links and routing parameters to optional route parameters. The use case for this post is if a user ends up on the contact detail page with no contact ID they will be presented with the option to add a new contact. The starting point for the code can be found here. Keep in mind any changes in this post are taking place in the Aurelia project.

Route with optional parameter

Making a route parameter optional is as simple as adding a question mark to the end of the parameter name. The following is the before and after of the contact detail route found in the app.ts file in the ClientApp/app/components/app folder.

Before:
route: 'contact-detail/:id'

After:
route: 'contact-detail/:id?'

For more information on routing see the Aurelia docs.

Contact detail changes

In the contact detail view model which is in the contactDetail.ts file a class level property is added for if the component was activated with a contact ID or not.

hasContactId: boolean;

The activate function is changed to set the new class level variable to false if the function is called parms.id is falsy as well as to only pull contact details if it has a contact ID.

activate(parms, routeConfig) {
    this.hasContactId = parms.id;

    if (this.hasContactId) {
        return this.contactService.getById(parms.id)
            .then(contact => this.contact = contact);          
    }

    return null;

}

A placeholder for creating a new contact was added to the contact detail view found in contactDetail.html file. This placeholder was added just above the link back to the contact list. This placeholder will only show if the detail components are loaded with no ID.

<h3 if.bind="!hasContactId">
    Placeholder for creating a new contact
</h3>

Add create link to the contact list

Finally, add a link in the contactList.html file that sends the user to the contact detail view, but without sending a contact ID.

<a route-href="route: contactdetail">Create New Contact</a>

In the example code this was added before the table of contacts, but of course, it could be anywhere.

Wapping up

The code in it’s completed state can be found here. The same functionality using Angular 2 will be coming up next week.

Aurelia Optional Route Parameter Read More »

Angular 2 – Router links, click handlers, routing parameters

As with last week’s post this post is going to cover multiple topics related to while creating a contact detail page to go along with the existing contact list page that is part of the ASP.NET Basics repo, but this time using Angular 2 instead of Aurelia. The code before any changes can be found here. If you are using the sample application keep in mind all the changes in this post take place in the Angular project.

Creating a detail view and view model

Create contactdetail.component.html in ClientApp\app\components\contacts. This view that will be used to display all the details of a specific contact. It will also link back to the contact list. The following image shows the folder structure after the view and view model have been added.

View

The following is the full contents of the view.

<h1>Contact Details</h1>
<hr />
<div *ngIf="contact">
    <dl class="dl-horizontal">
        <dt>ID</dt>
        <dd>{{contact.id}}</dd>
        <dt>Name</dt>
        <dd>{{contact.name}}</dd>
        <dt>Address</dt>
        <dd>{{contact.getAddress()}}</dd>
        <dt>Phone</dt>
        <dd>{{contact.phone}}</dd>
        <dt>Email</dt>
        <dd>{{contact.email}}</dd>
    </dl>
</div>
<a routerLink="/contact-list">Back to List</a>
<hr />

*ngIf is adding/removing the associated div based on a contact being set or not.

{{value}} is Angular’s one-way binding syntax. For more details check out the docs.

<a routerLink=”/contact-list”> is using Angular to generate a link back to the contact list component.

View model

For the view model add contactdetail.component.ts in the ClientApp\app\components\contacts folder which is the same folder used for the view.

Make not of the imports needed to make this view model work. To start off Contact is needed to define what the definition of a contact is and ContactService is being used to load the data for a specific contact.

Angular’s core is imported to allow the view model to set as a component using @Component decorator as well as to all implementation of OnInit lifecycle hook.  Angular’s router is being used in the ngOnInit function to allow access the parameters of the route that caused the route to be triggered using this.route.params.

The switchMap operator from reactive extensions is used to map the id from the route parameters to a new observable that has the result of this.contactService.getById.

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router';
import 'rxjs/add/operator/switchMap';
import { Contact } from './contact';
import { ContactService } from './contact.service';

@Component({
    selector: 'contactdetail',
    template: require('./contactdetail.component.html'),
    providers: [ContactService]
})
export class ContactDetailComponent implements OnInit {
    contact: Contact;

    constructor(private route: ActivatedRoute,
                private router: Router,
                private contactService: ContactService) { }

    ngOnInit(): void {
        this.route.params
            .switchMap((params: Params) => 
                   this.contactService.getById(params['id']))
            .subscribe((contact :Contact) => this.contact = contact);
    }
}

Adding get by ID to the Contact Service

The existing ContactService doesn’t provide a function to get a contact by a specific ID so one needs to be added.

The following calls the API in the Contacts project and uses the result to create an instance of a  Contact as a promise which is returned to the caller.

getById(id: string): Promise<Contact> {
    return this.http.get(this.baseUrl + id)
        .toPromise()
        .then(response => response.json())
        .then(contact => new Contact(contact))
        .catch(error => console.log(error));
}

The base URL was also moved to a class level variable so that it could be shared.

Add a route with a parameter

To add the new contact detail to the list of routes that the application handles open app.module.ts in the ClientApp/app folder. First, add an import at the top of the file for the new component.

import { ContactDetailComponent } from './components/contacts/contactdetail.component';

Next, add the ContactDetailComponent to the declarations array of the @NgModule decorator.

declarations: [
    AppComponent,
    NavMenuComponent,
    CounterComponent,
    FetchDataComponent,
    ContactListComponent,
    ContactDetailComponent,
    HomeComponent
]

Finally, add the new route to the RouteModule in the imports section of the @NgModule decorator.

RouterModule.forRoot([
    { path: '', redirectTo: 'home', pathMatch: 'full' },
    { path: 'home', component: HomeComponent },
    { path: 'counter', component: CounterComponent },
    { path: 'fetch-data', component: FetchDataComponent },
    { path: 'contact-list', component: ContactListComponent },
    { path: 'contact-detail/:id', component: ContactDetailComponent },
    { path: '**', redirectTo: 'home' }
])

path is the pattern used to match URLs. In addition, parameters can be used in the form of :parameterName. The above route will handle requests for http://baseurl/contact-detail/{id} where {id} is an ID of a contact. As demonstrated above in the ngOnInit function of the view model route.params can be used to access route parameters.

component is used to locate the view/view model that goes with the route.

Integrating the detail view with the contact list

The contact list view and view model needed the following changes to support the contact detail page.

View model

A variable was added for the ID of the select contact was as well as a onSelect function which takes a contact and gets called when a contact is selected. The following is the fully contact list view model.

import { Component, OnInit } from '@angular/core';
import { Contact } from './contact';
import { ContactService } from './contact.service';

@Component({
    selector: 'contactlist',
    template: require('./contactlist.component.html'),
    providers: [ContactService]
})
export class ContactListComponent implements OnInit {
    contacts: Contact[];
    selectedContactId: number = null;

    constructor(private contactService: ContactService) { }

    ngOnInit(): void {
        this.contactService.getAll()
            .then(contacts => this.contacts = contacts);
    }

    onSelect(contact) {
        this.selectedContactId = contact.id;
    }
}

The changes to the view model were not required to add the contact detail page, but are used show how to set up a click handler the view side. In the future, the selected contact will come in handy when the list and details were shown at the same time.

View

The amount of data being displayed was reduced to just ID and name. A column was added with a link to the details page. The following is the full view.

<h1>Contact List</h1>

<p *ngIf="!contacts"><em>Loading...</em></p>

<table class="table" *ngIf="contacts">
    <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor="let contact of contacts">
            <td>{{contact.id}}</td>
            <td>{{contact.name}}</td>
            <td><a [routerLink]="['/contact-detail', contact.id]" (click)="onSelect(contact)">Details</a></td>
        </tr>
    </tbody>
</table>

(click)=”onSelect(contact)” will cause the onSelect function of the view model to be called with the related contact when the associated element is clicked.

[routerLink]=”[‘/contact-detail’, contact.id]” use the router to create a line to the contact details page passing the contact ID as a parameter.

As a reminder of how to use a route’s parameter here is the ngOnInit function from the contact detail view model.

ngOnInit(): void {
    this.route.params
        .switchMap((params: Params) => this.contactService.getById(params['id']))
        .subscribe((contact :Contact) => this.contact = contact);
}
 Wrapping up

As with the related Aurelia post, there are a lot of topics covered in this post, but they are all related to the process of adding a new page and route to the application.

The code including all the changes for this post can be found here.

 

Angular 2 – Router links, click handlers, routing parameters Read More »

Aurelia – Router links, click delegate, routing parameters

This post is going to cover multiple of topics that I hit while creating a contact detail page to go along with the contact list that is part of my ASP.NET Basics repo. The code before any changes can be found here. If you are following along with the sample application keep in mind all the changes in this post take place in the Aurelia project.

Creating a detail view and view model

Create contactDetail.html file inside of ClientApp\app\components\contacts. This is the view that will be used to display all the details of a specific contact as well as a link back to the contact list. The following image shows the folder structure with the view and view model already added.

View

The following is the full contents of the view. This will be followed up few a calls out of Aurelia specific things going on.

<template>
    <h1>Contact Details</h1>
    <hr />
    <div if.bind="contact">
        <dl class="dl-horizontal">
            <dt>ID</dt>
            <dd>${contact.id}</dd>
            <dt>Name</dt>
            <dd>${contact.name}</dd>
            <dt>Address</dt>
            <dd>${contact.getAddress()}</dd>
            <dt>Phone</dt>
            <dd>${contact.phone}</dd>
            <dt>Email</dt>
            <dd>${contact.email}</dd>
        </dl>
    </div>
    <a route-href="route: contactlist">Back to List</a>
    <hr />
</template>

if.bind will keep the contact details out of the DOM if the condition fails. In this case, if the contact is null.

${value} is one of Aurelia’s bind syntaxes. For more details check out their docs.

<a route-href=”route: contactlist”> is using Aurelia’s router to generate a link back to the contact list component.

View model

Next, create a contactDetail.ts file inside of ClientApp\app\components\contacts  which is the view model that goes with the view created above.

The view model gets an instance of the ContactService injected which is used to pull a contact’s detail information during the activate lifecycle hook. Notice the first parameter of the function (parms) which is where the route parameters are passed in.

import { inject } from 'aurelia-framework';
import { Contact } from './contact';
import { ContactService } from './contactService';

@inject(ContactService)
export class ContactDetail {
     contact: Contact;

    constructor(private contactService: ContactService) { }

    activate(parms, routeConfig) {
        return this.contactService.getById(parms.id)
            .then(contact => this.contact = contact);
    }
}

Adding get by ID to the Contact Service

The existing ContactService doesn’t provide a function to get a contact by a specific ID so one needs to be added.

The following calls the API in the Contacts project and uses the result to create an instance of a Contact as a promise which is returned to the caller.

getById(id: string): Promise<Contact> {
    return this.http.fetch(id)
        .then(response => response.json())
        .then(contact => new Contact(contact))
        .catch(error => console.log(error));
}

Add a route with a parameter

Next, the router needs to be made aware of the new contact details. Open app.ts inside of the ClientApp/app/components/app folder. This file contains all the routes for the application. The bit we are interested in is the config.map which is an array of routes the application handles. Contact details is a new route which means adding a new object to the config.map array.

{
 route: 'contact-detail/:id',
 name: 'contactdetail',
 moduleId: '../contacts/contactDetail',
 nav: false,
 title: 'Contact Detail'
}

The route is the pattern used to match URLs. In addition, parameters can be used in the form of :parameterName. The above route will handle requests for http://baseurl/contact-detail/{id} where {id} is an ID of a contact. If a route has a parameter it will be made available to the activate function via the parms parameter in the view model.

moduleId is used to locate the view/view model that goes with the route.

nav controls if the route will be included in the routers navigation model. This is used to build the menu in this application. Contact details shouldn’t show in the menu which is why nav is set to false. For more details on Aurelia’s router check out the docs.

Integrating the detail view with the contact list

The contact list view and view model needed changes to support the contact detail page.

View model

The change to the view model was the simplest. A variable for the ID of the select contact was added as well as a function that gets called when a contact is selected. The following is the fully contact list view model.

import { inject } from 'aurelia-framework';
import { Contact } from './contact';
import { ContactService } from './contactService';

@inject(ContactService)
export class ContactList {
    contacts: Contact[];
    selectedContactId: number = null;

    constructor(private contactService: ContactService) {}

    created() {
        this.contactService.getAll()
            .then(contacts => this.contacts = contacts);
    }

    select(contact) {
        this.selectedContactId = contact.id;
    }
}

The changes to the view model were not really required to add the contact detail page, but they are there to show how to setup a click delegate on the view side. In the future, the selected contact could come in handy if the list and details were shown at the same time.

View

On the view, the amount of data being displayed was reduced to show just contact ID and name. A column was added with a link to the details page that is now used to show the rest of information about a contact. The following is the full view.

<template>
    <h1>Contact List</h1>
    <p if.bind="!contacts"><em>Loading...</em></p>

    <table class="table" if.bind="contacts">
        <thead>
        <tr>
            <th>IDs</th>
            <th>Name</th>
            <th></th>
        </tr>
        </thead>
        <tbody>
        <tr repeat.for="contact of contacts" 
          class="${contact.id === selectedContactId ? 'active' : ''" }>
            <td>${contact.id}</td>
            <td>${contact.name}</td>
            <td><a route-href="route: contactdetail; params.bind: {id:contact.id}" click.delegate="select($contact)">
                  Details
                </a>
            </td>
        </tr>
        </tbody>
    </table>
    <hr />
</template>

click.delegate=”select($contact)” will cause the select function of the view model to be called when the associated element clicked and passes the relevant contact object. The docs go into more depth on when to use delegates vs triggers.

The details link contains a lot of concepts. route-href is binding the anchor to Aurelia’s router.

route: contactdetail is telling the router which route to load.

params.bind: {id:contact.id} is telling the link to pass the contact’s ID to through the router to the view model that is being loaded. The following is the activate function of the contact detail view model as a reminder of the parameter’s usage.

activate(parms, routeConfig) {
    return this.contactService.getById(parms.id)
        .then(contact => this.contact = contact);
}

Wrapping up

This post cover a lot of topics, but they were all things I had to review in the course of adding contact details. My hope is this post will shortcut your own research and get you back to your task at hand. The finished code can be found here.

Leave a comment with any thoughts and/or questions.

Aurelia – Router links, click delegate, routing parameters Read More »

Swagger and Swashbuckle with ASP.NET Core API

This post is going to walk through adding Swagger to an existing ASP.NET Core API application using Swashbuckle. The starting point for the code can be found here.

What is Swagger?

Swagger is a specification on documentation an API. As I am sure we all know API documentation tends to get out of date fast and a lot of times is a low priority.  Swagger aims to help solve that problem using a format that is both human and machine readable which can be maintained in either JSON or YAML and can be auto generated using a tool like Swashbuckle. Check out this post by the Swagger team for the full introduction.

What is Swashbuckle?

Swashbuckle provides auto generation of Swagger 2.0, swagger-ui integration, etc. The project takes all the pain out of getting going with Swagger as well as providing tools and hooks for using and customizing Swagger related items. The full description can be found here.

Adding Swashbuckle to the project

There are lots of ways to get a new package into an ASP.NET Core application and the following covers the NuGet UI, Package Manager Console and Project.json. Pick one of them to use.

NuGet UI

Right click on the project Swashbuckle is going to be added to, Contacts in the case of the sample code, and select Manage NuGet Packages.

swashbuckleprojectmenu

Select the Browse tab, check the Include prerelease checkbox and search for Swashbuckle. Prerelease is need to get the version that works with ASP.NET Core.

swashbucklenuget

Finally click the Install button and work though any confirmation dialog screens that might show.

Package manager console

From the package manager console run Install-Package Swashbuckle -Pre.

Project.json

Open porject.json and in the dependencies section add “Swashbuckle”: “6.0.0-beta902”.

Add and configure Swashbuckle

In the ConfigureServices function of the Startup class add the following. I added it as the end, but placement shouldn’t matter.

services.AddSwaggerGen();

Next in the Configure function after app.UseMvc add the following.

app.UseSwagger();
app.UseSwaggerUi();

The first line enable serving of the Swagger JSON endpoint and the second enables the swagger-ui.

Test it out

Running the application will now provide two new routes one or each of the items added to the Configure function above.

The first is http://localhost:13322/swagger/v1/swagger.json (your base URL may differ if not using the sample procject) and it exposes the Swagger compliant JSON.

The second URL is http://localhost:13322/swagger/ui and it provides a very readable view of the documented API along with examples and options to try the API out. The following as an example of what the current version outputs.

swaggeruiexample

Wrapping up

Swashbuckle make it easy to add Swagger to a project. I feel that it also provides a huge value for anyone trying to consume an API. It is of course not a magic bullet and communication with your API consumers about API changes will still be critical.

Microsoft’s docs has a great walk through which can be found here. It does more in-depth on customizing your setup and as far as modifying the look of the UI.

The code for this post in it’s finished state can be found here.

Swagger and Swashbuckle with ASP.NET Core API Read More »

Angular 2 with an ASP.NET Core API

This week’s post is going to take the Angular 2 application from a couple of weeks ago and add the same functionality currently present in the Aurelia application found the ASP.NET Core Basic repo. This release is the starting point for the solution used in this post.

Starting point overview

When you download a copy of the repo you will find an ASP.NET Core solution that contains three projects. The Angular project is where this post will be focused.

The Contacts project has a set of razor views and a controller to go with them that support standard CRUD operations, which at the moment is the best way to get contact information in the database. It also contains the ContactsApiController which will be the controller used to feed contacts to the Angular 2 and Aurelia applications.

Multiple startup projects in Visual Studio

In order to properly test the functionality that will be covered here both the Contacts project and the Angular project will need to be running at the same time. Visual Studio provides a way to handle this. The Multiple startup projects in Visual Studio section of this post walks through the steps of setting up multiple startup project. The walk through is for the Aurelia project, but the same steps can be applied to the Angular project.

Model

Create a contacts directory inside of ClientApp/app/components/ of the Angular project. Next create a contact.ts file to the contacts directory. This file will be the model of a contact in the system. If you read the Aurelia version of this post you will noticed that this model is more fully defined since this project is using TypeScript the more fully defiled model provides more type safety. The following is the contests file.

export class Contact {
    id: number;
    name: string;
    address: string;
    city: string;
    state: string;
    postalCode: string;
    phone: string;
    email: string;

    constructor(data) {
        Object.assign(this, data);
    }

    getAddress() {
        return `${this.address} ${this.city}, ${this.state} ${this.postalCode}`;
    }

}

Service

To isolate HTTP access the application will use a service to encapsulate access to the ASP.NET Core API. For the service create a contact.service.ts file in the contacts directory of the Angular project. The following is the code for the service.

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { Contact } from './contact';

@Injectable()
export class ContactService {

    constructor(private http: Http) {
    } 

    getAll(): Promise<Contact[]> {
        return this.http.get('http://localhost:13322/api/contactsApi/')
            .toPromise()
            .then(response => response.json())
            .then(contacts => Array.from(contacts, c => new Contact(c)))
            .catch(error => console.log(error));
    }
}

This class uses Angular’s HTTP client to access the API and download a list of contacts. Angular’s HTTP client uses reactive extensions and returns an observable. In this case we don’t need an observable so for this service the observable is being converted to a promise. Then from there the response from the API is being converted an array of type contact.

Also make note of the Injectable decorator which tells Angular 2 the class should be available for dependency injection.

View Model

The next step is to create a view model to support the view that will be used to display the contacts download from the API. Add a file named contactlist.component.ts to the contacts directory of the Angular project. The following is the full contents of the view model file. This will be followed by a breakdown of the file in order to highlight some parts of the file.

import { Component, OnInit } from '@angular/core';
import { Contact } from './contact';
import { ContactService } from './contact.service';

@Component({
    selector: 'contactlist',
    template: require('./contactlist.component.html'),
    providers: [ContactService]
})
export class ContactListComponent implements OnInit {
    contacts: Contact[];

    constructor(private contactService: ContactService) { }

    ngOnInit(): void {
        this.contactService.getAll()
            .then(contacts => this.contacts = contacts);
    }
}

The import statements are pulling in a couple parts of the Angular 2 framework in addition to the contact model and contact service created above.

Next is a component decorator which marks the class as an Angular component and provides a method to set metadata about the class.

@Component({
    selector: 'contactlist',
    template: require('./contactlist.component.html'),
    providers: [ContactService]
})

The selector property sets the identifier for the class to be used in templates. The template property sets the view that should be used with the view model. In this case it is requiring in another file, but it could also contain the actual template that should be used to render the component. An alternate is to use templateUrl to point to an external file containing a template. The final property used in this example is the providers  property which is a list of providers that the framework needs to be made available to the component, in this case the ContractService. For more information on the component decorator check out the Angular docs.

The next thing of note on this class is that it implements OnInit.

export class ContactListComponent implements OnInit

OnInit is one of Angular’s lifecycle hooks, see the docs for the rest of the available hooks. OnInit is called once after component creation and runs the ngOnInit function which in the case of this class is being used to get a list of contacts from the ContactService.

View

For the view create a contactlist.component.html in the contacts directory of the Angular project. This is the file that the veiw model created above is bound with to display the contact data retrieved from the API. The following is the complete contents of the view file.

<ul>
    <li *ngFor="let contact of contacts">
        <h4>{{contact.name}}</h4>
        <p>{{contact.getAddress()}}</p>
    </li>
</ul>

The second line repeats the li tag for each contact in the contacts array of the view model class. {{expression}} is Angular’s syntax for one way data binding. {{contact.name}} does a one way binding to the name property of the current contact in the *ngFor loop. For more details on the different options available for data binding see the docs.

Add menu

The final piece is to add an item to the menu from with the contact list can be accessed. Open app.module.ts in the ClientApp/app/components/ directory. Add an imports for the ContactListComponent.

import { ContactListComponent } from './components/contacts/contactlist.component';

Next add a new path to the RouterModule. The third from the bottom is the line that was added for the contact list.

RouterModule.forRoot([
    { path: '', redirectTo: 'home', pathMatch: 'full' },
    { path: 'home', component: HomeComponent },
    { path: 'counter', component: CounterComponent },
    { path: 'fetch-data', component: FetchDataComponent },
    { path: 'contact-list', component: ContactListComponent },
    { path: '**', redirectTo: 'home' }
])

Finally open the navmenu.component.html file in the ClientApp/app/components/navmenu/  directory. Add a new li for the contact list matching the following.

<li [routerLinkActive]="['link-active']">
    <a [routerLink]="['/contact-list']">
        <span class='glyphicon glyphicon-list-alt'></span> Contact List
    </a>
</li>

Wrapping up

That is all it takes to consume some data from an ASP.NET Core API and use it in an Angular 2 application. I can’t stress enough how easy working with in the structure provided by JavaScriptServices helped in getting this project up and going quickly.

The completed code that goes along with this post can be found here. Also note that the Aurelia project has be redone as well also based on JavaScriptServices and TypeScript so that the applications will be easier to compare.

Angular 2 with an ASP.NET Core API Read More »