如果由于某种原因 SDK 提供的 Host 不够用,您可以创建自己的 REST Host。很少应该这样做,但它是特定方案的一个选项。本文假定您在项目中引用了最新的 REST SDK。
创建您的类
如 Hosts 一文所述,所有 Host 都继承 Limyee.Extensibility.Rest.Version1.RestHost。此类封装 REST 通信框架,同时为自定义 Host 提供成员以单独实现。你永远不应该尝试实现任何低于 RestHost 的抽象类。
对于此示例,我们将实现一个不使用 OAuth 的 Host,而是使用 API 密钥对用户进行身份验证。重要的是,OAuth 仍然是首选,但是对于某些实用程序方面,API 密钥更容易使用且可接受。如果要与网站或应用程序集成,则不应使用 API 密钥。您可以通过查看 Rest 身份验证主题来了解更多信息。
创建一个名为 APIKeyRESTHost 的新类,该类继承自 Limyee.Extensibility.Rest.Version1.RestHost。
namespace Samples { public class APIKeyRestHost : Limyee.Extensibility.Rest.Version1.RestHost { } }
如果在 Visual Studio 的类定义中右键单击 Limyee.Extensibility.Rest.Version1.RestHost 并选择"实现抽象类",您将看到它添加了 3 个成员。
namespace Samples { public class APIKeyRestHost : Limyee.Extensibility.Rest.Version1.RestHost { public override void ApplyAuthenticationToHostRequest(System.Net.HttpWebRequest request, bool forAccessingUser) { throw new NotImplementedException(); } public override string RootUrl { get { throw new NotImplementedException(); } } public override string Name { get { throw new NotImplementedException(); } } } }
ApplyAuthenticationToHostRequest: 应用于每个REST 请求的方法,用于添加相应的身份验证标头。这将详细讨论,但在这里我们只需要知道是能应用于登录用户、模拟用户和API密钥标头的请求。
RootUrl:指向您的平台的 URL,用于以后的所有请求。例如 http://mylimyeesite.com/。
名称:用于以友好的方式标识Host。在自定义 Host 中,除非实现者使用它,否则它没有特定的功能值。例如,默认 REST Host 在内存中存储多个 Host,可使用此属性来引用和查找特定实例。即使未使用,仍应返回值。
实现 API 密钥 Host
首先通过使用硬编码"APIKeyRestHost"作为属性返回值来添加名称。如前所述,我们不会在此实现中使用此字段,因此无需将其设置为动态。
public override string Name { get { return "APIKeyRestHost"; } }
获取所需信息
您还可以在 RootUrl 属性中将 URL 硬编码到您的平台,但通常这不是一个好的做法。特别是如果您计划将此工具用于多个平台或不同的环境。因此,我们建议动态收集这些信息。
此外,我们还需要获取此 Host 工作所需的一些其他信息:
管理 API 密钥:这是我们将使用的默认密钥。为简单起见,我们使用的是管理员帐户,但是对于此实现,此 API 密钥用户至少只需要模拟用户并在您的平台中查看配置文件权限。
管理员用户名:与上述管理员 API 密钥关联的用户名。
每次调用不模拟的 Host REST 时,我们的 Host 都会使用这些信息。要记住的一件事是,Host 的使用者可以在没有模拟的情况下进行任何调用(我们的例子中使用了管理员的 API 密钥),这意味着根据您提供给此 API 密钥用户的安全级别,您可能会暴露太多或太少。
由于这些都是特定于平台的,因此我们将它们作为构造函数的一部分传入,然后将 Url 值用于所需的 RootUrl 属性。
namespace Samples { public class APIKeyRestHost : Limyee.Extensibility.Rest.Version1.RestHost { private string _rootUrl = null; private string _adminAPIKey = null; private string _adminUserName = null; public APIKeyRestHost(string rootUrl, string adminUserName, string adminApiKey) { _rootUrl = rootUrl; _adminAPIKey = adminApiKey; _adminUserName = adminUserName; } public override void ApplyAuthenticationToHostRequest(System.Net.HttpWebRequest request, bool forAccessingUser) { } public override string RootUrl { get { return _rootUrl; } } public override string Name { get { return "APIKeyRestHost"; } } } }
添加身份验证标头
您需要了解 API 密钥身份验证,这可以在 REST 身份验证主题找到。
ApplyAuthenticationToHostRequest 是参与最多的成员。在这里,我们在构造函数中将收集的身份验证标头处理模拟。首先,我们必须始终将我们在构造函数中收集的 API 密钥信息应用于每个请求。请注意,HttpWebRequest 是作为此方法的参数提供给您。
public override void ApplyAuthenticationToHostRequest(System.Net.HttpWebRequest request, bool forAccessingUser) { var adminKey = String.Format("{0}:{1}", _adminAPIKey, _adminUserName); var adminKeyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(adminKey)); request.Headers.Add("Rest-User-Token", adminKeyBase64); }
接下来,我们使用 forAccessingUser 参数来确定是否需要模拟。如果它是 false,我们将不会添加任何其他信息。如果是 true,我们需要模拟。对于此示例,我们将使用 HttpContext 用户,并且此 Host 仅在用户登录时才有效。请注意,父类中内置了一个 GetCurrentHttpContext 方法获取 HttpContextBase 对象。
我们将从上下文中获取用户名,然后在平台中查找该用户名,以确保它存在,如果没有,则抛出异常。您还可以在此处实现自动创建用户的逻辑,但对于此示例,我们假设他们已经在平台用户群中。请记住将调用 GetToDynamic 的 enableImpersonation 参数设置为 false,否则您的应用程序可能陷入灾难的无限循环!
public override void ApplyAuthenticationToHostRequest(System.Net.HttpWebRequest request, bool forAccessingUser)
{
var adminKey = String.Format("{0}:{1}", _adminAPIKey, _adminUserName);
var adminKeyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(adminKey));
request.Headers.Add("Rest-User-Token", adminKeyBase64);
if (forAccessingUser)
{
var httpContext = this.GetCurrentHttpContext();
if (!httpContext.User.Identity.IsAuthenticated)
throw new AuthenticationException("This REST Host does not support anonymous access.");
var username = httpContext.User.Identity.Name;
var user = this.GetToDynamic(1, "users/{username}.json", false, new RestGetOptions()
{
PathParameters = { { "username", username } }
});
if (user == null)
throw new ApplicationException(string.Format("User '{0}' wasn't found. Create this user in your community first"));
}
}
如果找到用户,在模拟标头中使用用户名。
public override void ApplyAuthenticationToHostRequest(System.Net.HttpWebRequest request, bool forAccessingUser) { var adminKey = String.Format("{0}:{1}", _adminAPIKey, _adminUserName); var adminKeyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(adminKey)); request.Headers.Add("Rest-User-Token", adminKeyBase64); if (forAccessingUser) { var httpContext = this.GetCurrentHttpContext(); if (!httpContext.User.Identity.IsAuthenticated) throw new AuthenticationException("This REST Host does not support anonymous access."); var username = httpContext.User.Identity.Name; var user = this.GetToDynamic(1, "users/{username}.json", false, new RestGetOptions() { PathParameters = { { "username", username } } }); if (user == null) throw new ApplicationException(string.Format("User '{0}' wasn't found. Create this user in your community first")); //Apply impersonation request.Headers.Add("Rest-Impersonate-User", username); } }
以下是 Host 放在一起:
using System; using System.Collections.Generic; using System.Linq; using System.Security.Authentication; using System.Text; using System.Web; using Limyee.Extensibility.Rest.Version1; namespace Samples { public class APIKeyRestHost : Limyee.Extensibility.Rest.Version1.RestHost { private string _limyeeUrl = null; private string _adminAPIKey = null; private string _adminUserName = null; public APIKeyRestHost(string limyeeUrl, string adminUserName, string adminApiKey) { _limyeeUrl = limyeeUrl; _adminAPIKey = adminApiKey; _adminUserName = adminUserName; } public override void ApplyAuthenticationToHostRequest(System.Net.HttpWebRequest request, bool forAccessingUser) { var adminKey = String.Format("{0}:{1}", _adminAPIKey, _adminUserName); var adminKeyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(adminKey)); request.Headers.Add("Rest-User-Token", adminKeyBase64); if (forAccessingUser) { var httpContext = this.GetCurrentHttpContext(); if (!httpContext.User.Identity.IsAuthenticated) throw new AuthenticationException("This REST Host does not support anonymous access."); var username = httpContext.User.Identity.Name; var user = this.GetToDynamic(1, "users/{username}.json", false, new RestGetOptions() { PathParameters = { { "username", username } } }); if (user == null) throw new ApplicationException(string.Format("User '{0}' wasn't found. Create this user in your community first")); //NOTE: You could attempt to auto create the account using the admin API key, this host chooses not to. //Apply impersonation request.Headers.Add("Rest-Impersonate-User", username); } } public override string RootUrl { get { return _limyeeUrl; } } public override string Name { get { return "APIKeyRestHost"; } } } }
使用 Host
现在 Host 已经实现,下面构造我们的 API Key Host 的实例并调用 SDK Host REST:
var host = new APIKeyRestHost("http://mylimyeesite.com", "[Admin USername]", "[Admin API Key]"); //Call an API var user = host.GetToDynamic(1, "users/{username}.json", true,new RestGetOptions() { PathParameters = { { "username","pmason"} } });