Angular 6 应用程序, 使用 Cosmos DB 和 Web API 2





5.00/5 (6投票s)
我们将创建一个使用 Cosmos DB 和 Web API 2.0 的 Angular 6 应用程序。
- 此应用程序的 Web API 项目可从 Github (WebAPI2) 下载
- 此应用程序的 Angular 6 项目可从 Github (Angular6) 下载
引言
在本文中,我们将创建一个使用 Web API 2.0 的 Angular 6 应用程序。我们将看到所有四个 CRUD 操作并将数据保存到 Cosmos DB。为了测试目的,我们可以使用 Cosmos DB 模拟器并将数据保存到本地系统。成功完成测试后,我们还可以创建 Azure 中的 Cosmos DB 服务。
背景
对于 Cosmos DB 的新手来说,Azure Cosmos DB 是微软的全球分布式、多模型数据库。只需单击一个按钮,Azure Cosmos DB 即可让您跨任意数量的 Azure 地理区域弹性地独立扩展吞吐量和存储。它提供吞吐量、延迟、可用性和一致性保证以及全面的服务级别协议 (SLA),这是其他任何数据库服务都无法提供的。目前,Cosmos DB 支持五种不同的 API,如下所示。
- SQL
- MongoDB
- 图
- 表格
- Cassandra
实现
Web API 服务创建
使用 Visual Studio 创建一个名为 WebAPI4AngularCosmosDB
的新 Web API 2.0 项目。
我将使用 ASP.NET 4.5 模板。
下一步,我们将安装 Microsoft.Azure.DocumentDB
NuGet 包。
我们现在可以创建一个 DocumentDBRepository
类。这是 CosmosDB CRUD 操作的核心存储库。
DocumentDBRepository.cs
namespace WebAPI4AngularCosmosDB
{
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
public static class DocumentDBRepository<T> where T : class
{
private static readonly string DatabaseId = ConfigurationManager.AppSettings["database"];
private static readonly string CollectionId = ConfigurationManager.AppSettings["collection"];
private static DocumentClient client;
public static async Task<T> GetItemAsync(string id)
{
try
{
Document document = await client.ReadDocumentAsync
(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id));
return (T)(dynamic)document;
}
catch (DocumentClientException e)
{
if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return null;
}
else
{
throw;
}
}
}
public static async Task<IEnumerable<T>> GetItemsAsync()
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = -1 })
.AsDocumentQuery();
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
public static async Task<IEnumerable<T>> GetItemsAsync(Expression<Func<T, bool>> predicate)
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = -1 })
.Where(predicate)
.AsDocumentQuery();
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
public static async Task<T> GetSingleItemAsync(Expression<Func<T, bool>> predicate)
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = -1 })
.Where(predicate)
.AsDocumentQuery();
List<T> results = new List<T>();
results.AddRange(await query.ExecuteNextAsync<T>());
return results.SingleOrDefault();
}
public static async Task<Document> CreateItemAsync(T item)
{
return await client.CreateDocumentAsync
(UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId), item);
}
public static async Task<Document> UpdateItemAsync(string id, T item)
{
return await client.ReplaceDocumentAsync
(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id), item);
}
public static async Task DeleteItemAsync(string id)
{
await client.DeleteDocumentAsync
(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id));
}
public static void Initialize()
{
client = new DocumentClient(new Uri(ConfigurationManager.AppSettings["endpoint"]),
ConfigurationManager.AppSettings["authKey"]);
CreateDatabaseIfNotExistsAsync().Wait();
CreateCollectionIfNotExistsAsync().Wait();
}
private static async Task CreateDatabaseIfNotExistsAsync()
{
try
{
await client.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(DatabaseId));
}
catch (DocumentClientException e)
{
if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
await client.CreateDatabaseAsync(new Database { Id = DatabaseId });
}
else
{
throw;
}
}
}
private static async Task CreateCollectionIfNotExistsAsync()
{
try
{
await client.ReadDocumentCollectionAsync
(UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId));
}
catch (DocumentClientException e)
{
if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
await client.CreateDocumentCollectionAsync(
UriFactory.CreateDatabaseUri(DatabaseId),
new DocumentCollection { Id = CollectionId },
new RequestOptions { OfferThroughput = 1000 });
}
else
{
throw;
}
}
}
}
}
这是一个静态通用类。在此类中,我们有一个 Initialize
方法,它将从 Global.asax 类中的 Application_Start
方法调用。
应用程序启动时,将调用 DocumentDBRepository<Hero>.Initialize()
,如果 Cosmos DB 中尚不存在数据库和集合,它将创建一个。请注意,在首次运行时,我们的 Cosmos DB 中没有数据库和集合。
Cosmos DB 的终结点、密钥、数据库和集合名称必须存储在 Web.config 文件中。
<configuration>
<appSettings>
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="endpoint" value="https://:8081" />
<add key="authKey"
value="C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==" />
<add key="database" value="AngularHeroDB" />
<add key="collection" value="MyCollection1" />
</appSettings>......
请注意,在此应用程序中,我们使用的是 Cosmos DB 本地模拟器而不是真实的 Azure Cosmos DB 服务。这是为了测试目的。一旦测试完成,我们将把此配置更改为真实的 Cosmos DB 配置。
您可以从以下 URL 获取 Cosmos DB 本地模拟器
下载模拟器并将其安装到您的本地系统。安装成功后,您可以运行模拟器,它将显示在您的系统托盘中。
如果您单击“打开数据资源管理器”,本地 Cosmos DB 模拟器将在您的浏览器中打开。
在“资源管理器”按钮中,您可以看到目前我们的模拟器中没有可用的数据库。
现在,我们将在模型文件夹中为我们的 Web API 项目创建一个名为 Hero.cs 的模型。
Hero.cs
namespace WebAPI4AngularCosmosDB.Models
{
using Newtonsoft.Json;
public class Hero
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "uid")]
public string UId { get; set; }
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "saying")]
public string Saying { get; set; }
}
}
请注意,我们在该类中使用了 JsonProperty
属性,以便我们可以轻松地将 C# 属性转换为 json 属性。
现在,我们将通过依赖注入从 Global.asax
中的 Application_Start
方法调用 DocumentDBRepository
类。
Global.asax
namespace WebAPI4AngularCosmosDB
{
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using WebAPI4AngularCosmosDB.Models;
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
DocumentDBRepository<Hero>.Initialize();
}
}
}
请构建应用程序并使用本地 IIS 服务器运行它。
DocumentDBRepository<Hero>.Initialize()
将执行 Initialize
方法,并且下面的两个异步方法也将被执行。
CreateDatabaseIfNotExistsAsync().Wait();
CreateCollectionIfNotExistsAsync().Wait();
如果您查看 Cosmos DB 模拟器的数据资源管理器,您会看到我们的新数据库和集合已成功创建。
在我们的 Web.config 配置中,我们将数据库命名为 AngularHeroDB
,集合命名为 MyCollection1
。
我们可以创建我们的 HeroController.cs API 控制器并创建所有四个 CRUD 方法。所有这些方法都非常简单。通过这些方法,我们可以创建、编辑、更新和删除我们在 Angular 应用程序中使用的英雄。
HeroController.cs
namespace WebAPI4AngularCosmosDB.Controllers
{
using WebAPI4AngularCosmosDB.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Web.Http;
[RoutePrefix("api/Hero")]
public class HeroController : ApiController
{
[HttpGet]
public async Task<IEnumerable<Hero>> GetAsync()
{
IEnumerable<Hero> value = await DocumentDBRepository<Hero>.GetItemsAsync();
return value;
}
[HttpPost]
public async Task<Hero> CreateAsync([FromBody] Hero hero)
{
if (ModelState.IsValid)
{
await DocumentDBRepository<Hero>.CreateItemAsync(hero);
return hero;
}
return null;
}
public async Task<string> Delete(string uid)
{
try
{
Hero item = await DocumentDBRepository<Hero>.GetSingleItemAsync(d => d.UId == uid);
if (item == null)
{
return "Failed";
}
await DocumentDBRepository<Hero>.DeleteItemAsync(item.Id);
return "Success";
}
catch (Exception ex)
{
return ex.ToString();
}
}
public async Task<Hero> Put(string uid, [FromBody] Hero hero)
{
try
{
if (ModelState.IsValid)
{
Hero item = await DocumentDBRepository<Hero>.GetSingleItemAsync(d => d.UId == uid);
if (item == null)
{
return null;
}
hero.Id = item.Id;
await DocumentDBRepository<Hero>.UpdateItemAsync(item.Id, hero);
return hero;
}
return null; ;
}
catch (Exception ex)
{
return null;
}
}
}
}
如果需要,您可以从 POSTMAN 或任何其他 REST 客户端检查我们的 API 方法。
Angular 应用程序创建
使用 Angular CLI 创建新的 Angular 应用程序。
ng new Angular4WebAPICosmosDB
创建新项目需要一些时间,成功创建后,请在任何代码编辑器中打开该应用程序。我使用 Visual Studio Code 作为 IDE。
请注意,在此 Angular 应用程序中,我们使用的是 style.scss 文件而不是默认的 style.css 文件。您必须在 angular.json 文件中更改样式构建设置。
从 Github 下载完整的 Angular 6 源代码并运行 npm install
。
在运行 Angular 应用程序之前,我们需要在 Web API 中启用 CORS,以允许来自 Angular 应用程序的请求。为此,请使用 NuGet 安装 Microsoft.AspNet.WebApi.Cors
。
现在,在 WebApiConfig.cs 文件中的 Register
方法内添加以下代码。
namespace WebAPI4AngularCosmosDB
{
using System.Web.Http;
using System.Web.Http.Cors;
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
EnableCorsAttribute cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
运行 Web API 和 Angular 应用程序。
我们的 Angular 应用程序将加载并显示如下
您可以单击“添加新英雄”按钮并添加一些详细信息。
如果您查看 Cosmos DB 模拟器中的数据资源管理器,您会在我们的 MyCollection1
集合中看到一个文档,如下所示
您可以使用我们的 Angular 应用程序编辑和删除英雄。
我们使用了本地 Cosmos DB 模拟器进行测试。您甚至可以使用模拟器的“重置数据”按钮重置模拟器中的现有数据。
请选择 SQL 作为 API。
请创建服务,创建成功后,您可以在“密钥”窗格中查看密钥。
复制新的 URI 和主密钥,然后将其粘贴到我们的 Web.config 文件中,以替换本地 Cosmos DB 的旧密钥。
再次运行 Web API 应用程序,一段时间后,将创建新的 Azure Cosmos 数据库和集合,您可以在数据资源管理器窗格中查看数据。
如果您刷新正在运行的 Angular 应用程序,您会注意到不会显示任何英雄数据。因为我们已将连接从本地 Cosmos DB 更改为 Azure。
您可以再次添加一些数据并保存。请重新打开 Azure 中的数据资源管理器,并注意到我们的新数据将显示在资源管理器中,如下所示
祝您使用 Angular 6、Cosmos DB 和本地模拟器编码愉快。