Create an Aurelia Application with ASP.NET Core 1.0, Part 3 - Web Services
In this third part of the series, we look at how to implement web services into Aurelia.
With our setup for the Aurelia project and the navigation behind us, we can focus on the web services part of the project.
Of course, since we are using an ASP.NET MVC Core project, we can easily add additional web services to the project without worrying about colliding with ASP.NET MVC.
ASP.NET MVC/Web API Changes
Since this is ASP.NET Core 1.0, I made some modifications to the data layer/web services since the last time we examined the optimization series.
While I want to focus on Aurellia, I feel these changes made to the back end are important enough to bring to one's attention since it is the new ASP.NET Core 1.0.
Rest assured, while I don't cover everything in detail with this post regarding ASP.NET MVC Core, I will post this on GitHub as a simple Aurelia project when I'm done (Keep watch on the right-hand side with What's New on my site for details).
Caching w/ Repositories
One of the changes made was the way we handle caching with our repositories.
Since everything is dependency injected into our MVC application, we had to reconstruct the CacheSessionRepository and CacheSpeakerRepository to use DI.
First, I had to add the MemoryCache, Configuration, and CodemashUnitOfWork to my Startup.cs so it can find the right objects to perform the DI.
Startup.cs
public void ConfigureServices(IServiceCollection services) { services.AddMemoryCache();
// Add framework services. services.AddMvc();
services.AddOptions();
services.Configure<CodemashConfiguration>(Configuration.GetSection("CodemashConfig"));
services.AddTransient<ICodemashUnitOfWork, CodemashUnitOfWork>(provider => new CodemashUnitOfWork( provider.GetService<IOptions<CodemashConfiguration>>(), provider.GetService<IMemoryCache>())); }
Once the DI was defined, we could inject the CodemashUnitOfWork into our controller with all of the parameters intact.
Controllers/SpeakerController.cs
[Route("api/[controller]")] public class SpeakerController : Controller { private ICodemashUnitOfWork UnitOfWork { get; set; }
public SpeakerController(ICodemashUnitOfWork uow) { UnitOfWork = uow; }
// GET: api/values [HttpGet] public JsonResult Get() { var list = JsonConvert.DeserializeObject<List<Speaker>>(JsonResources.PublicSpeakerDataModel);
// When we actually have a list of speakers, uncomment this line. // var list = UnitOfWork.SpeakerRepository.GetAllAsync();
return Json(list); } }
After testing this by typing localhost:xxxx/api/Speaker
, I didn't receive any data in json format.
It was empty.
Then I realized that it's about that time of the year when the Codemash committee is setting up the site for speakers and sessions so they clear out all of the previous speakers and sessions.
I thought I was losing my mind (even more).
So I doctored up some dummy data which can be found on Codemash Speakers/Sessions API.
At least we have some data to work with for this project.
For more information on caching in ASP.NET Core, check the Caching section over at Microsoft.
ASP.NET Core Configuration
As mentioned in another post, we need to adjust our "ConfigurationManager" for Core 1.0.
All we are changing is the CodemashConfiguration to reflect the old way we handle Configuration Settings.
Web API
The Web API is new for this application. Before, I used controllers with Views and Partial Views to return the data from the Web API. The funny thing is...not much has changed.
Since we have our new hotness (Aurelia), we can now call Web APIs from Aurelia to return the data.
I won't go into detail since it's easy to add an Api controller with attribute routing. I'll publish the GitHub project after everything is complete.
However, as mentioned above, it seems the Web API for Codemash is not available yet. Again, the example json data is over at the Codemash Speakers/Sessions API.
I placed the test json for speakers and sessions in a Resources file for testing purposes. When the Codemash API is available, I'll change the code so it works properly.
One last thing we need to do. In our Startup.cs, we need to uncomment the app.UseMvc() so our Web API can work.
Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug();
if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); } else { app.UseExceptionHandler("/Home/Error"); }
app.UseStaticFiles(); app.UseFileServer(new FileServerOptions { EnableDefaultFiles = true, EnableDirectoryBrowsing = false });
app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }
Setting up Web API Services
Everything should be in place for the backend. We aren't writing any data, only reading so this makes our application extremely easy to build.
Once we have the plumbing in place for web services, we can call our web services in the browser to confirm it's easy to access through Aurelia.
But how do we make a call to the service in Aurelia?
According to the docs, there are two types of clients:
aurelia-http-client
- A basic HttpClient based on XMLHttpRequest. It supports all HTTP verbs, JSONP and request cancellation.aurelia-fetch-client
- A more forward-looking HttpClient based on the Fetch specification. It supports all HTTP verbs and integrates with Service Workers, including Request/Response caching.
It''s recommended to use the fetch client instead of the http client because...they said so. They mentioned that this would be the preferred way to go. Moving forward, they mentioned that the http client has the ability to request a cancellation or display the progress of a download. These options are being considered for the future version of the fetch client.
Since we don't have a need for cancellation or displaying a download progress, we'll use fetch for now.
To install the fetch client, get to the root of your project and type:
jspm install aurelia-fetch-client
After that's installed, we can start building our web services for Aurelia.
Aurelia Simple Services
To access our web services in ASP.NET Web API, we need to build some Aurelia services (I know...it's always something, right?)
One easy way is to create a simple class for each service: One SpeakerWebService and one SessionsWebService.
wwwroot/services/SpeakerWebService.js
import {HttpClient} from 'aurelia-fetch-client'; import {inject} from 'aurelia-framework';
@inject(HttpClient) export class SpeakerWebService {
constructor(http) { this.http = http; }
getSpeakers() { return this.http.fetch('http://localhost:37432/api/Speaker') .then(response => response.json()) .catch(error => console.log(error)); } }
wwwroot/services/sessionWebService.js
import {HttpClient} from 'aurelia-fetch-client'; import {inject} from 'aurelia-framework';
@inject(HttpClient) export class SessionWebService {
constructor(http) { this.http = http; }
getSessions() { return this.http.fetch('http://localhost:37432/api/Session') .then(response => response.json()) .catch(error => console.log(error)); } }
At the top of each web service, we include the HttpClient from the aurelia fetch client and the inject for dependency injection.
The fetch method in the HttpClient is pretty simple. Fetch this url contents and then convert it into a Json object and return it. The same occurs with the SpeakerWebService.
To see if everything works, let's hook up a simple Speaker screen to our navigation.
We also need to modify our app.js which contains our menu (changes in bold).
wwwroot/app.js
export class App {
configureRouter(config, router) { config.title = 'Codemash App';
config.map([ { route: ['','codemash'], name: 'codemash', moduleId: './views/Codemash', nav: true, title:'Home'}, { route: 'speakers', name: 'speakers', moduleId: './views/Speakers', nav: true, title:'Speakers'} ]);
this.router = router; } }
Next, in the Views folder, create a speakers.html and speakers.js
wwwroot/views/speakers.html
<template> <h1>Speakers</h1> <p>We have ${SpeakerList.length} Speakers.</p> </template>
wwwroot/views/speakers.js
import {SpeakerWebService} from '../services/speakerWebService'; import {inject} from 'aurelia-framework'; @inject(SpeakerWebService) export class Speakers {
constructor(service) { this.service = service; }
activate() { this.service.getSpeakers() .then(data => { this.SpeakerList = data; }); } }
In the future, remember that your import at the top should match the same name of your class. It's case-sensitive! Same goes for your inject. It's case-sensitive as well and should be the same as your class name.
For each View, remember you have an activate to kick off any initialization. Once the activation is complete, you can do whatever you need to on the View.
I also snuck in a simple property called SpeakerList in the promise.
Any property created in the ViewModel can be bound in the view. In our particular View, we're counting the number of speakers.
Yeah, we have 2 speakers!
This means we can start building our Aurelia screens for next time.
Conclusion
Today, I demonstrated how to connect Web API REST-based web services to an Aurelia application. The amount of effort it took was a lot, but looking back on this section, it makes sense with the pieces that I missed (especially the Codemash API not being populated).
In the next post, we'll finish everything up with the screens. Since they are simply "templates," we shouldn't have any problems wiring everything and finishing our Aurelia application.
Did you understand everything? Post your comments or concerns below.