发出请求取决于对 Host 的理解。此处描述的功能对于所有类型的 Host 都是通用的,但是如果不根据所使用的 Host 类型获取或实例化,则无法访问此功能。建议您在继续学习本主题之前,先查看有关 Host、客户端凭据 REST Host 和默认 REST Host的资料。
出于本主题的示例目的,我们将使用客户端凭据 REST Host。但是,请务必注意,一旦您获得了任何的 Host 实例,向 REST 发出请求的过程都是相同的。
请求类型
SDK 允许使用 REST API 支持的同类型请求。这些包括 GET,POST,PUT,DELETE 以及发起批处理和Widget渲染请求。您可以访问您的平台当前支持的任何 REST Url。如果您尝试向平台不支持的 URL 发出请求,例如,仅在较新版本中可用的 URL,则该请求将失败。提到这一点的重要原因是SDK本身不了解端点是什么或做什么,它只是将其视为一个 REST Url,它将使用适当的有效的负载去响应。
响应类型
这是 SDK 获得结果的地方。当涉及到平台 REST API 时,只有4种类型的响应。最常见的2种是XML或JSON。此外,某些端点可以返回原始二进制数据甚至 html 数据。SDK 可以处理所有这些问题,在某些情况下,可以帮助您将响应解析为更可用的格式。以下是 SDK 中可用的响应类型,以及何时或如何使用它们:
- String - 这适用于 JSON 或 XML 请求。在这种情况下,JSON 或 XML 的有效负载是以字符串形式返回。如果您使用的是基于 html 的端点(如小组件呈现端点),也可以使用此选项。
- Stream - 这实际上是返回响应流。它适用于所有类型的呼叫。如果有效负载是原始二进制数据,则特别需要它。而且,您可以将流读取为任何适当的形式。
- XElement - 这仅在 REST API 使用 XML 响应时可用。在这种情况下,XML 被读入一个易于使用的 .NET XElement 对象中。
- dynamic - 这只能与 JSON 响应类型一起使用,因为它是解析为动态对象的 JSON。动态对象实质上允许您像处理强类型对象一样处理响应,而无需实际创建一个。
对于每种请求类型,您都会找到每种响应类型的方法,但小组件渲染除外,它本质上是一个GET请求。
开始
如前所述,它是一个 Host,可让您访问进行 REST API。出于示例的目的,本主题使用客户端凭据 REST Host,但重要的是要重申本主题中所示的调用是相同的,而与 Host 无关,唯一的差异是 Host 的获取方式。如果要尝试这些示例中的任何一个,可以在 Visual Studio 中创建新的 Console 应用程序,然后安装 SDK,如此处所述。
加载 Host
在我们的示例中,我们有一个简单的方法来加载 Host,避免重复使用代码。如果您愿意,也可以以内联方式执行此操作。您还需要确保已设置了"客户端凭据"授权类型的私有 OAuth 客户端。URL可以是您想要的任何内容,它们与使用客户端凭据无关。如果您不确定如何执行此操作,请参阅 REST API 的身份验证主题的 OAuth 部分。
private static ClientCredentialsRestHost GetHost() { return new ClientCredentialsRestHost("admin" ,"http://mylimyeesite.com" , "[Your Client Id]" , "[Your Client Secret]"); }
如果您使用的是默认 REST Host,可以通过小幅调整来使用相同的模式,前提是您已正确设置它。 在此之后,所有的 REST 请求工作方式是完全相同。
private static Host GetHost() { return Host.Get("default"); }
构建请求
使用我们讨论过的响应类型发出不同的请求方式是通过调用 Host 上的特定方法。对于所有这些请求,命名模式是根据请求类型定制的。对于每个请求类型,都有一个用于不同响应类型的方法。
按给定的请求类型列表,基本上以下是可用的方法:
- Host.GetTo[Dynamic|String|Stream|XElement](...)
- Host.PostTo[Dynamic|String|Stream|XElement](...)
- Host.PutTo[Dynamic|String|Stream|XElement](...)
- Host.DeleteTo[Dynamic|String|Stream|XElement](...)
此外,它们每一个都有一个对应异步方法,我们将在后面更深入地介绍:
- Host.GetTo[Dynamic|String|Stream|XElement]Async(...)
- Host.PostTo[Dynamic|String|Stream|XElement]Async(...)
- Host.PutTo[Dynamic|String|Stream|XElement]Async(...)
- Host.DeleteTo[Dynamic|String|Stream|XElement]Async(...)
请求剖析
每个请求至少需要 2 条信息,但批处理请求除外,该请求将单独介绍。这些是:
- REST 版本 - 这是端点的版本。如果您手动发出请求,则它是 url (/api/v1/...) 的一部分。在 SDK 中,需要将此数字作为第一个参数。
- Url - 这是您尝试在没有 /api/v1/ 的情况下连接的实际端点。SDK 将自动使用第一个参数的版本号构建 URL 的这一部分。
鉴于此,如果我们想生成一个不需要任何参数的 GET 请求,它将如下所示:
var host = GetHost(); var response = host.GetToDynamic(1, "info.json");
模拟
每种请求方法都使您能够选择加入或退出模拟。但是,仅当您使用的 Host 支持模拟时,这才有效。虽然建议 Host 支持模拟,但可能不支持。您应该参考 Host 的文档,了解它如何处理模拟其他用户(如果有的话)。如果您不希望模拟,则应为 enableImpersonation 参数传入 false。enableImpersonation 参数是可选的,如果未提供,则默认为 true。
请参阅客户端凭据 REST Host 和默认 REST Host 上的主题,了解如何使用此参数以及它们如何支持模拟。
以下调用都将告诉 Host 使用模拟(如果支持):
var host = GetHost(); var response = host.GetToDynamic(1, "info.json")
或者
var host = GetHost(); var response = host.GetToDynamic(1, "info.json", true); //addition of "enableImpersonation" parameter
Options 对象
Options 对象是传递给每种类型请求的类,可以具有多个可选参数。这也是将要添加其他可选参数的地方。Options 本身也是可选的。
查询字符串参数
查询字符串参数是附加到 REST Url 后面的"?"符号之后的参数。它们是 GET 请求中最常见的,但在所有请求类型上都受支持。您不需要自己实际将它们添加到 url 中,而是在要调用的方法上使用 options 对象,找到一个属性来添加查询字符串参数。
下面的示例阐释了使用查询字符串参数的 GET 调用。POST、PUT和DELETE请求的模式也是相同的。
var host = GetHost(); var querystring = new NameValueCollection(); querystring.Add("pageSize","20"); querystring.Add("pageIndex","1"); var response = host.GetToDynamic(1, "users.json", true, new RestGetOptions() { QueryStringParameters = querystring });
PathParameters
与查询字符串参数一样,路径参数用于向请求添加动态数据,但与查询字符串参数不同,这些参数不会添加到任何内容中,而是替换值,通常用于替换 url 中的值。
替换值是用 {} 括起来的键名。例如,userid 键是以下 url 中的路径参数:
users/{userid}.json
路径参数可以位于 url 字符串中的任何位置。若要定义参数的值,请将其传递到选项对象的 PathParameters。路径参数支持所有 GET、POST、PUT 和 DELETE 请求。您可以定义自己的 path 参数,只要在 PathParameters 集合中提供该参数的值即可。
此示例在简单的 GET 中使用 PathParameters。
var host = GetHost(); var pathParameters = new NameValueCollection(); pathParameters.Add("userid", "2100"); var response = host.GetToDynamic(1, "users/{userid}.json", true, new RestGetOptions() { PathParameters = pathParameters });
PostParameters
与其他参数类型不同,PostParameters 被添加到请求正文中,而不是 url 的一部分。这些类型的参数仅受 POST 和 PUT 请求的支持。以下创建用户需要传递 PostParameters:
var host = GetHost(); var postParameters = new NameValueCollection(); postParameters.Add("Username","somerandomusername"); postParameters.Add("PrivateEmail", "somerandomusername@localhost.com"); postParameters.Add("Password","SuperComplexPassword"); var response = host.PostToDynamic(1, "users.json", true, new RestPostOptions() { PostParameters = postParameters });
自定义标头
如果由于某种原因需要应用程序自定义标头,则可以在 options 对象上指定它们。 这也会覆盖现有的标头。 这应该都支持所有请求 options 对象。
var customHeaders = new NameValueCollection() { {"X-App-Name","MyCustomApp"} }; dynamic response = host.GetToDynamic(1, "info.json", true, new RestGetOptions() { AdditionalHeaders = customHeaders });
处理响应
大多数响应类型都相当直接,处理它们并不是限定于此 SDK。下面是响应类型以及代码如何处理它们:
- XElement - 这为您提供了一个表示 XML 的 .NET XElement 对象。使用此元素,可以使用 LINQ to XML 处理数据。
- Stream - 在负载上的流有不同的表现。对于 XML 或 JSON,可以将流读入内存,然后将数据反序列化为应用程序创建的类。如果负载是文件,则可以将流读取到相应的文件中,并将其保存到磁盘。通常,您不会使用 stream 转换为字符串,因为该情况在以下一项 String 已经可用。
- String - 这最适合 Html 或 JSON 返回类型。您可以使用该字符串直接输出 html,也可以将其用于 JSON 序列化或操作。
最后一个可能最强大的响应是 dynamic,虽然它不是特定 SDK 的响应类型,但由于其相对较新,因此值得花费更多时间。它的缺点是,与强类型类不同,在编写代码时没有智能感知能力。
通过访问其属性,您可以像类一样与 dynamic 进行交互。但不同之处在于,这些属性实际上并未定义,而是在运行时动态绑定。
一个简单的例子是,如果你有一个名为"Foo"的普通旧对象,并且在代码中你调用了该属性"Name"。如果 Foo 对象没有名为"Name"的属性,则代码将无法编译。这是因为编译器在编译时会检查这些类型的错误。如果 Foo 是动态类型,并且您调用了 Foo.Name 则代码将编译而不会出现错误。这是因为在运行时之前,编译器会忽略此引用以进行动态操作。使用这个概念,我们不必为来自 REST API 的每个可能的响应编写一个类。我们只是简单地获取响应并将其转换为一种动态。这意味着您可以针对属性进行编码,即使代码尚不知道属性是否存在也是如此。只有在运行时评估其属性(如果该属性不存在)后,才会引发错误。
我们的动态绑定基于 JSON 响应的层次结构,因此您只能将动态响应用于 JSON 请求。查看此 JSON 响应(出于示例目的,此响应已缩短):
{ "InfoResult": { "SiteName": "sitename", "SiteDescription": "sitedescription" }, "AccessingUser": { "ContentId": "dd16cfa5-bde0-4aac-ae99-a0ff54df246e", "ContentTypeId": "920d87a5-ec73-4a9e-81aa-a985ff2f8088", "DisplayName": "displayname", "Username": "username", "Id": 6 }, "Errors": [ "string", "string" ] }
在此示例中,InfoResult 是动态对象的属性,Sitename 是 InfoResult 的属性。 同样,AccessingUser 是动态对象的属性,而 Username 是 Accessing User 的属性。
var host = GetHost(); var response = host.GetToDynamic(1, "info.json"); Console.WriteLine("Site Name:" + response.InfoResult.SiteName); Console.WriteLine("Accessing Username:" + response.AccessingUser.Username);
上传文件
SDK 还支持上传文件以用作其他内容的附件。SDK 上传的优点是能与 UI 中上传文件的方式类似。如果文件小于 15mb,则发送整个文件,否则将其分解为 15mb 块并单独传输。这将绕过请求限制,并通过将大文件切分到多个请求上来避免超时。与其他请求不同,这将为您提供一个强类型的 UploadedFileInfo 参数,以代替已经讨论过的其他类型的响应。
以下是以最基本形式上传文件的示例
using (var fileStream = new FileStream("c:\\test.png", FileMode.Open, FileAccess.Read)) { fileStream.Position = 0; Guid uploadContextId = Guid.NewGuid(); UploadedFile file = new UploadedFile(uploadContextId, "test.png", "image/png", fileStream); UploadedFileInfo response = host.UploadFile(file); if (!response.IsError) Console.WriteLine(response.UploadContext); else Console.WriteLine(response.Message); }
您还可以使用 UploadProgess 的 delegate 来跟踪每个块的进度,该操作将为您提供要与之交互的 FileUploadProgress 对象。 下面我们将打印完成百分比。
using (var fileStream = new FileStream("c:\\test.png", FileMode.Open, FileAccess.Read)) { fileStream.Position = 0; Guid uploadContextId = Guid.NewGuid(); UploadedFile file = new UploadedFile(uploadContextId, "test.png", "image/png", fileStream); UploadedFileInfo response = host.UploadFile(file,new RestFileOptions() { UploadProgress = (p) => { Console.WriteLine("Percent Complete:" + p.PercentComplete); } }); if (!response.IsError) Console.WriteLine(response.UploadContext); else Console.WriteLine(response.Message); }
您可以使用该文件的上下文 ID 创建论坛主题等内容附件:
using (var fileStream = new FileStream("c:\\test.png", FileMode.Open, FileAccess.Read)) { fileStream.Position = 0; Guid uploadContextId = Guid.NewGuid(); UploadedFile file = new UploadedFile(uploadContextId, "test.png", "image/png", fileStream); UploadedFileInfo fileInfo = host.UploadFile(file,new RestFileOptions() { UploadProgress = (p) => { Console.WriteLine("Percent Complete:" + p.PercentComplete); } }); f (!fileInfo.IsError) { var pathParameters = new NameValueCollection(); pathParameters.Add("forumid","2958"); var postParameters = new NameValueCollection(); postParameters.Add("Subject","This is a cool Forum"); postParameters.Add("Body","I think I will post a thread in it"); //Now add the file postParameters.Add("FileName", "test.png"); postParameters.Add("ContentType", "image/png"); postParameters.Add("FileUploadContext",fileInfo.UploadContext.ToString());//Use the upload context Id from our file upload dynamic threadResponse = host.PostToDynamic(2, "forums/{forumid}/threads.json", true, new RestPostOptions() { PathParameters = pathParameters, PostParameters = postParameters }); } }
批处理
批处理请求在单个调用中发出多个 REST 请求。这减少了开销,因为批处理请求是通过 HTTP 处理的,包含的请求是在服务器上执行的。SDK还使批处理请求更容易,因为您不必像直接对 REST 发出批处理请求那样了解请求格式。
批处理请求是请求对象的集合,这些对象在发送到平台之前转换为单个请求。批处理请求对象包含与单个请求方法类似的信息,但有几个例外。
- 默认的 APIVersion 将始终为 1,尽管有一个 APIVersion 属性来覆盖此值。
- 每个请求都必须在构造函数中指定一个序列,该序列基于零,因此您的第一个序列值应为 0。
- 您必须在 RestMethod 属性中指定请求是 GET,POST 等,因为批处理请求本身就是 POST。
- 在以前的请求中习惯的 QueryString 和 PostParameters 将折叠到一个名为 RequestParameters 的集合,并根据 RestMethod 使用 querystring 或 body 参数。路径参数是分开的,工作方式相同。
**序列始终是必需的,但仅在批处理请求设置为按顺序执行时才适用。否则,请求将在服务器上并行处理。
下面的示例在单个请求中并行执行 GET 和 POST 请求:
var batchRequestList = new List<BatchRequest>(); var userRequest = new BatchRequest("users/{username}.json", 0); userRequest.RestMethod = RestMethod.GET; serRequest.PathParameters = new NameValueCollection() { {"username","pmason"} }; batchRequestList.Add(userRequest); var threadRequest = new BatchRequest("forums/{forumid}/threads.json", 1); threadRequest.RestMethod = RestMethod.POST; threadRequest.PathParameters = new NameValueCollection() { {"forumid","1000"} }; threadRequest.RequestParameters = new NameValueCollection() { {"Subject","This is a great thread"}, {"Body","I love this forum so much!"} }; batchRequestList.Add(threadRequest); dynamic response = host.BatchRequestToDynamic(1, batchRequestList, true);
您可以强制按顺序运行它,这意味着每个请求将按其序列值的顺序运行,方法是添加 options 参数并设置 RunSequentially 标志:
dynamic response = host.BatchRequestToDynamic(1, batchRequestList, true,new BatchRequestOptions() { RunSequentially = true });
批处理响应类似于请求,在请求中,您将取回响应的集合。每个响应都有关于响应的元数据,包括了响应代码和 BatchResponse 属性,该属性是从请求端点返回的响应数据。此数据与端点的格式相同,因为它实际上是在单个请求中完成的。
{ "BatchResponses":[ { "Url": "api/v1/users/pmason.json", "StatusCode": 200, "Sequence": 0, "BatchResponse": { "User": { //This looks like the user SHOW response } } }, { "Url":"api/v1/forums/2958/threads.json", "StatusCode":200, "Sequence":1, "BatchResponse": { "Thread": { //This looks like the Thread Create response } } } ] }
现在,在 SDK 中,您不必使用原始响应。像其他请求一样,除了文件请求之外,它支持前面提到的所有响应类型。但是,了解结构确实有助于了解如何使用动态响应。因此,根据上面的请求,下面介绍您如何与响应进行交互,批响应是一个数组:
Console.WriteLine("User Request Response Code:" + response.BatchResponses[0].StatusCode); Console.WriteLine("User Id:" + response.BatchResponses[0].BatchResponse.User.Id); Console.WriteLine("Thread Create Request Response Code:" + response.BatchResponses[1].StatusCode); Console.WriteLine("Thread Id:" + response.BatchResponses[1].BatchResponse.Thread.Id);
需要注意的是,如果按顺序运行请求,则如果有 1 个请求失败,则会将整个请求视为失败,并且不会进一步处理。
使用异步
如果你的应用是使用异步编程,则可以使用 SDK 的异步方法。对于每个 REST 请求方法,都有一个相应的异步版本。若要了解有关异步模式的详细信息,请参阅:MSDN.
下面是一个返回用户 ID 的简单异步方法:
private async Task<int> GetUserId(string username) { var host = GetHost(); var pathParameters = new NameValueCollection() { {"username", username} }; dynamic response = await host.GetToDynamicAsync(1, "users/{username}.json", true, new RestGetOptions() { PathParameters = pathParameters }); return Task.FromResult(response.User.Id); }