如何使用适用于 Azure 移动应用的 ASP.NET Framework SDK

注意

此产品已停用。 有关使用 .NET 8 或更高版本的项目的替换,请参阅 Community Toolkit Datasync 库

本主题介绍如何在关键的 Azure 应用服务移动应用方案中使用 .NET 后端服务器 SDK。 Azure 移动应用 SDK 可帮助你从 ASP.NET 应用程序中使用移动客户端。

警告

本文介绍 v4.2.0 库版本的信息,该版本被 v5.0.0 库取代。 有关最新信息,请参阅 最新版本的文章

创建 Azure 移动应用 ASP.NET 框架后端

可以使用 Visual Studio 2019 创建 ASP.NET 框架应用。

  • 选择 ASP.NET Web 应用程序(.NET Framework) 模板。 如果在查找此模板时遇到问题,请选择 C#所有平台,以及 Web
  • 为应用程序选择名称和位置后,选择 Web API 项目模板。 将安装应用程序的基本服务的正确集合。

下载并初始化 SDK

SDK 在 NuGet.org上提供,并提供开始使用 Azure 移动应用所需的基本功能。 安装包:

  1. 右键单击项目,然后选择 管理 NuGet 包...
  2. 在“浏览”选项卡中,在搜索框中输入 Microsoft.Azure.Mobile.Server,然后按 Enter。
  3. 选择 Microsoft.Azure.Mobile.Server.Quickstart 包。
  4. 单击 安装
  5. 按照提示完成安装。

重复此过程以安装 Microsoft.Owin.Host.SystemWeb

注意

不要更新用作依赖项的包,例如 Newtonsoft.JSONSystem.IdentityModel.Jwt。 在许多情况下,这些包的 API 已更改,现在与适用于 ASP.NET Framework 的 Azure 移动应用不兼容。

初始化服务器项目

Azure 移动应用服务器项目初始化与其他 ASP.NET 框架项目类似;通过包括 OWIN Startup 类。 添加 OWIN Startup 类:

  1. 右键单击项目,然后选择“添加新项”

  2. 选择 Web>常规,然后选择 OWIN 启动类 模板。

  3. 输入名称 Startup.cs 作为启动名称。

  4. Startup.cs 文件的内容应类似于以下代码:

    using Microsoft.Azure.Mobile.Server.Config;
    using Microsoft.Owin;
    using Owin;
    using System.Web.Http;
    
    [assembly: OwinStartup(typeof(WebApplication1.Startup))]
    namespace WebApplication1
    {
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                HttpConfiguration config = new HttpConfiguration();
                new MobileAppConfiguration()
                    // no added features
                    .ApplyTo(config);
                app.UseWebApi(config);
            }
        }
    }
    

    OwinStartup、命名空间和类名称将有所不同,具体取决于项目。 具体而言,应替换 Configuration() 方法的内容,并相应地调整 using 指令。

若要启用单个功能,必须在 MobileAppConfiguration 对象上调用扩展方法,然后才能调用 ApplyTo。 例如,以下代码将默认路由添加到初始化期间具有属性 [MobileAppController] 的所有 API 控制器:

new MobileAppConfiguration()
    .MapApiControllers()
    .ApplyTo(config);

以下设置被视为“正常”用法,它使表和 API 控制器都能够使用 Entity Framework 访问 SQL 服务。

new MobileAppConfiguration()
    .AddMobileAppHomeController()
    .MapApiControllers()
    .AddTables(
        new MobileAppTableConfiguration()
            .MapTableControllers()
            .AddEntityFramework()
    )
    .MapLegacyCrossDomainController()
    .ApplyTo(config);

使用的扩展方法包括:

  • AddMobileAppHomeController() 提供默认的 Azure 移动应用主页。
  • MapApiControllers() 为使用 [MobileAppController] 属性修饰的 WebAPI 控制器提供自定义 API 功能。
  • AddTables() 提供 /tables 终结点到表控制器的映射。
  • AddTablesWithEntityFramework() 是使用基于实体框架的控制器映射 /tables 终结点的简短方法。
  • MapLegacyCrossDomainController() 为本地开发提供标准 CORS 标头。

SDK 扩展

以下基于 NuGet 的扩展包提供了应用程序可以使用的各种移动功能。 使用 MobileAppConfiguration 对象在初始化期间启用扩展。

发布服务器项目

本部分介绍如何从 Visual Studio 发布 .NET 后端项目。 可通过其他方法发布应用程序。 有关详细信息,请参阅 Azure 应用服务文档

  1. 在 Visual Studio 中,重新生成项目以还原 NuGet 包。
  2. 在解决方案资源管理器中,右键单击项目,单击 发布
  3. 如果以前尚未发布此项目,将配置发布。
    • 为目标选择 Azure
    • 为特定目标选择 Azure 应用服务(Windows)。
    • 选择要部署到的应用服务实例。 如果没有,请使用 + 创建一个。
    • 单击 完成
  4. 如果以前未链接 SQL 数据库,请单击 SQL 数据库旁边的 配置
    • 选择 azure SQL 数据库
    • 选择数据库。 如果没有一个或想要使用不同的数据库,请单击 + 创建新的数据库和服务器。
    • 输入 MS_TableConnectionString 作为数据库连接字符串名称。 在提供的框中填写用户名和密码。
    • 单击 完成
  5. 单击 发布

发布到 Azure 需要一些时间。 有关详细信息,请参阅 Visual Studio 文档

定义表控制器

定义表控制器以向移动客户端公开 SQL 表。 配置表控制器需要三个步骤:

  1. 创建数据传输对象 (DTO) 类。
  2. 在 Mobile DbContext 类中配置表引用。
  3. 创建表控制器。

数据传输对象 (DTO) 是从 EntityData继承的纯 C# 对象。 例如:

public class TodoItem : EntityData
{
    public string Text { get; set; }
    public bool Complete {get; set;}
}

DTO 用于定义 SQL 数据库中的表。 若要创建数据库条目,请将 DbSet<> 属性添加到所使用的 DbContext

public class MobileServiceContext : DbContext
{
    private const string connectionStringName = "Name=MS_TableConnectionString";

    public MobileServiceContext() : base(connectionStringName)
    {

    }

    public DbSet<TodoItem> TodoItems { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Add(
            new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>(
                "ServiceColumnTable", (property, attributes) => attributes.Single().ColumnType.ToString()));
    }
}

最后,创建新的控制器:

  1. 右键单击 Controllers 文件夹。

  2. 选择 Web API>Web API 2 控制器 - 空

  3. 输入控制器的名称。

  4. 将新控制器的内容替换为以下代码:

    public class TodoItemController : TableController<TodoItem>
    {
        protected override void Initialize(HttpControllerContext controllerContext)
        {
            base.Initialize(controllerContext);
            ZUMOAPPNAMEContext context = new ZUMOAPPNAMEContext();
            DomainManager = new EntityDomainManager<TodoItem>(context, Request);
        }
    
        // GET tables/TodoItem
        public IQueryable<TodoItem> GetAllTodoItems()
        {
            return Query();
        }
    
        // GET tables/TodoItem/48D68C86-6EA6-4C25-AA33-223FC9A27959
        public SingleResult<TodoItem> GetTodoItem(string id)
        {
            return Lookup(id);
        }
    
        // PATCH tables/TodoItem/48D68C86-6EA6-4C25-AA33-223FC9A27959
        public Task<TodoItem> PatchTodoItem(string id, Delta<TodoItem> patch)
        {
            return UpdateAsync(id, patch);
        }
    
        // POST tables/TodoItem
        public async Task<IHttpActionResult> PostTodoItem(TodoItem item)
        {
            TodoItem current = await InsertAsync(item);
            return CreatedAtRoute("Tables", new { id = current.Id }, current);
        }
    
        // DELETE tables/TodoItem/48D68C86-6EA6-4C25-AA33-223FC9A27959
        public Task DeleteTodoItem(string id)
        {
            return DeleteAsync(id);
        }
    }
    

调整表分页大小

默认情况下,Azure 移动应用为每个请求返回 50 条记录。 分页可确保客户端不会将其 UI 线程和服务器绑定太久,确保良好的用户体验。 若要更改表分页大小,请使用 EnableQuery 属性调整服务器端“允许的查询大小”,增加服务器端“允许的查询大小”和客户端页面大小:

[EnableQuery(PageSize = 500)]

确保 PageSize 与客户端请求的大小相同或更大。 有关更改客户端页面大小的详细信息,请参阅特定的客户端 HOWTO 文档。

定义自定义 API 控制器

自定义 API 控制器通过公开终结点为移动应用后端提供最基本的功能。 可以使用 [MobileAppController] 属性注册特定于移动的 API 控制器。 MobileAppController 属性注册路由、设置移动应用 JSON 序列化程序,并启用客户端版本检查。

自定义 API 控制器的内容包括:

[MobileAppController]
public class CustomAPIController : ApiController
{
    // Content here
}

使用 MobileAppController 属性配置后,可以采用与任何其他 Web API 相同的方式定义自定义 API。

使用身份验证

Azure 移动应用使用应用服务身份验证/授权来保护移动后端。 本部分介绍如何在 .NET 后端服务器项目中执行以下与身份验证相关的任务:

将身份验证添加到服务器项目

可以通过扩展 MobileAppConfiguration 对象并配置 OWIN 中间件,将身份验证添加到服务器项目。

  1. 在 Visual Studio 中,安装 Microsoft.Azure.Mobile.Server.Authentication 包。

  2. Startup.cs 项目文件中,在 Configuration 方法的开头添加以下代码行:

    app.UseAppServiceAuthentication(config);
    

    此 OWIN 中间件组件验证关联应用服务网关颁发的令牌。

  3. [Authorize] 属性添加到需要身份验证的任何控制器或方法。

对应用程序使用自定义身份验证

重要

若要启用自定义身份验证,必须先启用应用服务身份验证,而无需在 Azure 门户中为应用服务选择提供程序。 这将在托管时启用 WEBSITE_AUTH_SIGNING_KEY 环境变量。

如果不想使用某个应用服务身份验证/授权提供程序,则可以实现自己的登录系统。 安装 Microsoft.Azure.Mobile.Server.Login 包,以帮助生成身份验证令牌。 提供自己的代码来验证用户凭据。 例如,可以检查数据库中的加盐密码和哈希密码。 在下面的示例中,isValidAssertion() 方法(在其他位置定义)负责这些检查。

通过创建 ApiController 并公开 registerlogin 操作来公开自定义身份验证。 客户端应使用自定义 UI 从用户收集信息。 然后,使用标准 HTTP POST 调用将信息提交到 API。 服务器验证断言后,将使用 AppServiceLoginHandler.CreateToken() 方法颁发令牌。 ApiController 不应 使用 [MobileAppController] 属性。

示例 login 操作:

public IHttpActionResult Post([FromBody] JObject assertion)
{
    if (isValidAssertion(assertion)) // user-defined function, checks against a database
    {
        JwtSecurityToken token = AppServiceLoginHandler.CreateToken(new Claim[] { new Claim(JwtRegisteredClaimNames.Sub, assertion["username"]) },
            mySigningKey,
            myAppURL,
            myAppURL,
            TimeSpan.FromHours(24) );
        return Ok(new LoginResult()
        {
            AuthenticationToken = token.RawData,
            User = new LoginResultUser() { UserId = userName.ToString() }
        });
    }
    else // user assertion was not valid
    {
        return this.Request.CreateUnauthorizedResponse();
    }
}

在前面的示例中,LoginResultLoginResultUser 是公开所需属性的可序列化对象。 客户端需要以 JSON 对象的形式返回登录响应:

{
    "authenticationToken": "<token>",
    "user": {
        "userId": "<userId>"
    }
}

AppServiceLoginHandler.CreateToken() 方法包括 访问群体颁发者 参数。 这两个参数都设置为使用 HTTPS 方案的应用程序根的 URL。 同样,应将 secretKey 设置为应用程序的签名密钥的值。 请勿在客户端中分发签名密钥,因为它可用于挖掘密钥和模拟用户。 通过引用 WEBSITE_AUTH_SIGNING_KEY 环境变量,可以在应用服务中托管时获取签名密钥。 如果需要在本地调试上下文中,请按照 本地调试中的说明使用身份验证 部分检索密钥并将其存储为应用程序设置。

颁发的令牌还可能包括其他声明和到期日期。 至少,颁发的令牌必须包含使用者()声明。

可以通过重载身份验证路由来支持标准客户端 loginAsync() 方法。 如果客户端调用 client.loginAsync('custom'); 登录,则必须 /.auth/login/custom路由。 可以使用 MapHttpRoute()为自定义身份验证控制器设置路由:

config.Routes.MapHttpRoute("custom", ".auth/login/custom", new { controller = "CustomAuth" });

提示

使用 loginAsync() 方法可确保身份验证令牌附加到对服务的每个后续调用。

检索经过身份验证的用户信息

当应用服务对用户进行身份验证时,可以访问 .NET 后端代码中分配的用户 ID 和其他信息。 用户信息可用于在后端做出授权决策。 以下代码获取与请求关联的用户 ID:

// Get the SID of the current user.
var claimsPrincipal = this.User as ClaimsPrincipal;
string sid = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier).Value;

SID 派生自提供程序特定的用户 ID,对于给定用户和登录提供程序是静态的。 对于无效的身份验证令牌,SID 为 null。

应用服务还允许你从登录提供程序请求特定声明。 每个标识提供者都可以使用标识提供者 SDK 提供更多信息。 例如,可以使用 Facebook 图形 API 获取好友信息。 可以在 Azure 门户的提供程序边栏选项卡中指定请求的声明。 某些声明需要使用标识提供者进行更多配置。

以下代码调用 GetAppServiceIdentityAsync 扩展方法来获取登录凭据,其中包括针对 Facebook Graph API 发出请求所需的访问令牌:

// Get the credentials for the logged-in user.
var credentials = await this.User.GetAppServiceIdentityAsync<FacebookCredentials>(this.Request);

if (credentials.Provider == "Facebook")
{
    // Create a query string with the Facebook access token.
    var fbRequestUrl = "https://graph.facebook.com/me/feed?access_token="
        + credentials.AccessToken;

    // Create an HttpClient request.
    var client = new System.Net.Http.HttpClient();

    // Request the current user info from Facebook.
    var resp = await client.GetAsync(fbRequestUrl);
    resp.EnsureSuccessStatusCode();

    // Do something here with the Facebook user information.
    var fbInfo = await resp.Content.ReadAsStringAsync();
}

System.Security.Principal 添加 using 语句以提供 GetAppServiceIdentityAsync 扩展方法。

限制授权用户的数据访问

在上一部分中,我们演示了如何检索经过身份验证的用户的用户 ID。 可以根据此值限制对数据和其他资源的访问。 例如,将 userId 列添加到表并按用户 ID 筛选查询结果是一种将返回的数据限制为仅授权用户的简单方法。 以下代码仅在 SID 与 TodoItem 表的 UserId 列中的值匹配时返回数据行:

// Get the SID of the current user.
var claimsPrincipal = this.User as ClaimsPrincipal;
string sid = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier).Value;

// Only return data rows that belong to the current user.
return Query().Where(t => t.UserId == sid);

Query() 方法返回可由 LINQ 操作以处理筛选的 IQueryable

调试和排查 .NET Server SDK 问题

Azure 应用服务为 ASP.NET 应用程序提供了多种调试和故障排除技术:

伐木

可以使用标准 ASP.NET 跟踪写入来写入应用服务诊断日志。 在写入日志之前,必须在 Azure 移动应用后端启用诊断。

若要启用诊断并写入日志,请执行以下操作:

  1. 按照 启用应用程序日志记录(Windows)中的步骤操作。

  2. 在代码文件中添加以下 using 语句:

    using System.Web.Http.Tracing;
    
  3. 创建跟踪编写器以从 .NET 后端写入诊断日志,如下所示:

    ITraceWriter traceWriter = this.Configuration.Services.GetTraceWriter();
    traceWriter.Info("Hello, World");
    
  4. 重新发布服务器项目,并访问 Azure 移动应用后端以使用日志记录执行代码路径。

  5. 下载并评估日志,如 Access 日志文件中所述。

使用身份验证进行本地调试

可以在本地运行应用程序,在将更改发布到云之前对其进行测试。 对于大多数 Azure 移动应用后端,在 Visual Studio 中按 F5。 但是,使用身份验证时,还有其他一些注意事项。

必须配置了应用服务身份验证/授权的基于云的移动应用,并且客户端必须具有指定为备用登录主机的云终结点。 有关所需的特定步骤,请参阅客户端平台的文档。

确保移动后端已安装 Microsoft.Azure.Mobile.Server.Authentication。 然后,在应用程序的 OWIN 启动类中,在 MobileAppConfiguration 应用于 HttpConfiguration后添加以下内容:

app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions()
{
    SigningKey = ConfigurationManager.AppSettings["authSigningKey"],
    ValidAudiences = new[] { ConfigurationManager.AppSettings["authAudience"] },
    ValidIssuers = new[] { ConfigurationManager.AppSettings["authIssuer"] },
    TokenHandler = config.GetAppServiceTokenHandler()
});

在前面的示例中,应使用 HTTPS 方案将 authAudienceauthIssuer 应用程序 Web.config 设置配置为应用程序根目录的 URL。 同样,应将 authSigningKey 设置为应用程序的签名密钥的值。

若要获取签名密钥,请执行以下操作:

  1. azure 门户 中导航到应用
  2. 单击“工具”>Kudu>Go
  3. 在 Kudu 管理站点中,单击 环境
  4. 查找 WEBSITE_AUTH_SIGNING_KEY的值。

在本地应用程序配置中使用 authSigningKey 参数的签名密钥。移动后端现在已准备好在本地运行时验证令牌,客户端从基于云的终结点获取令牌。