【发布时间】:2019-05-07 13:43:34
【问题描述】:
我有一个想要与 Cosmosdb 交谈的 Xamarin Forms 移动客户端 直接,我不想依赖 - 并且有开销 of - 整个 DocumentDb SDK。
由于我使用的是不受信任的客户端,因此我使用
resource tokens验证。一切都是分区的。出于测试目的,我已经复制了我正在尝试做的事情 REST SQL 和 DocumentClient 调用。
我已通过发出
Get成功检索到单个文档 使用 REST 和资源令牌调用。这也适用于 DocumentClient 方法。到目前为止,一切都很好。
当我尝试实际执行
query时,使用 DocumentClient 和资源令牌。使用完全相同的查询和完全相同的资源标记 REST 调用产生
Forbidden结果。The permission mode provided in the authorization token doesn't provide sufficient permissions我在某处读到(现在找不到)你需要一位大师 使用 REST 调用进行查询的令牌。
在我发布一堆代码并编写它之前,我正在体验 预期的行为还是我实际上应该能够使用 REST 进行查询 来电?
提前致谢。
** 更新 #2 与 GITHUB 存储库的链接**
https://github.com/nhwilly/DocumentClientVsRest.git
使用代码示例更新
using System;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Newtonsoft.Json;
namespace DocClientVsRestCallTest
{
/// <summary>
/// The purpose of this console app is to determine why I can't get a REST call
/// to work on a read only resource token for Azure CosmosDb. A direct comparison
/// using identical paths and tokens should work. I have an issue for sure, but I
/// can't find it. :(
///
/// To run this, you need to have already created a partitioned CosmosDb collection.
/// </summary>
class Program
{
public static string dbServicePath = $"https://[YOUR-COSMOS-ACCOUNT-NAME].documents.azure.com";
public static string databaseId = "[YOUR-DATABASE-ID]";
public static string collectionId = "[YOUR-COLLECTION-ID]";
public static string datetime = DateTime.UtcNow.ToString("R");
public static string version = "2018-06-18";
public static string resourceId = $"dbs/{databaseId}/colls/{collectionId}";
public static string urlPath = $"{dbServicePath}/{resourceId}/docs";
public static string partitionKey = $"TestPartition";
public static string documentId = $"TestDocumentId";
public static string queryString = $"select * from c where c.id='{documentId}' and c.partitionId ='{partitionKey}'";
public static string userId = $"TestUser";
public static string permissionToken = string.Empty;
// the master key is supplied to create permission tokens and simulate the server only.
public static string masterKey = $"[YOUR-MASTER-KEY]";
static void Main(string[] args)
{
Debug.WriteLine("Starting...");
// let's make sure we get a readonly token for the user/partition in question.
permissionToken =
Task.Run(async () => await GetPermissionToken()).GetAwaiter().GetResult();
QueryUsingSdk();
Task.Run(async () => await QueryUsingRest()).ConfigureAwait(false);
Task.Run(async ()=> await CleanUp()).ConfigureAwait(false);
Console.WriteLine("finished...");
Console.ReadKey();
}
static async Task QueryUsingRest()
{
Uri uri = new Uri(urlPath);
HttpClient client = new HttpClient();
var encodedToken =
HttpUtility.UrlEncode(permissionToken);
string partitionAsJsonArray =
JsonConvert.SerializeObject(new[] { partitionKey });
client.DefaultRequestHeaders.Add("x-ms-date", datetime);
client.DefaultRequestHeaders.Add("x-ms-documentdb-isquery", "True");
client.DefaultRequestHeaders.Add("x-ms-documentdb-query-enablecrosspartition", "False");
client.DefaultRequestHeaders.Add("x-ms-documentdb-query-iscontinuationexpected", "False");
client.DefaultRequestHeaders.Add("x-ms-documentdb-partitionkey", partitionAsJsonArray);
client.DefaultRequestHeaders.Add("authorization", encodedToken);
client.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
client.DefaultRequestHeaders.Add("x-ms-version", version);
client.DefaultRequestHeaders.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content =
new StringContent(JsonConvert.SerializeObject(new { query = queryString }), Encoding.UTF8, "application/query+json");
HttpResponseMessage response =
await client.PostAsync(urlPath, content).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
await DisplayErrorMessage(response).ConfigureAwait(false);
}
else
{
Debug.WriteLine($"Success {response.StatusCode}!");
var jsonString =
await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
static void QueryUsingSdk()
{
var docClient =
new DocumentClient(new Uri(dbServicePath), permissionToken);
var feedOptions =
new FeedOptions { PartitionKey = new PartitionKey(partitionKey) };
var result =
docClient
.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri(databaseId, collectionId), queryString,
feedOptions)
.ToList().First();
Debug.WriteLine($"SDK result: {result}");
}
/// <summary>
/// This method simulates what would happen on the server during an authenticated
/// request. The token (and other permission info) would be returned to the client.
/// </summary>
/// <returns></returns>
static async Task<string> GetPermissionToken()
{
string token = string.Empty;
try
{
var docClient =
new DocumentClient(new Uri(dbServicePath), masterKey);
var userUri =
UriFactory.CreateUserUri(databaseId, userId);
// delete the user if it exists...
try
{
await docClient.DeleteUserAsync(userUri).ConfigureAwait(false);
}
catch (Exception e)
{
Debug.WriteLine($"Delete user error: {e.Message}");
}
// create the user
var dbUri =
UriFactory.CreateDatabaseUri(databaseId);
await docClient.CreateUserAsync(dbUri, new User { Id = userId }).ConfigureAwait(false);
// create the permission
var link =
await docClient
.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(databaseId, collectionId))
.ConfigureAwait(false);
var resourceLink =
link.Resource.SelfLink;
var permission =
new Permission
{
Id = partitionKey,
PermissionMode = PermissionMode.Read,
ResourceLink = resourceLink,
ResourcePartitionKey = new PartitionKey(partitionKey)
};
await docClient.CreatePermissionAsync(userUri, permission).ConfigureAwait(false);
// now create a document that should be returned when we do the query
var doc = new { id = documentId, partitionId = partitionKey, message = "Sample document for testing" };
try
{
await docClient.DeleteDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId,
documentId), new RequestOptions { PartitionKey = new PartitionKey(partitionKey) }).ConfigureAwait(false);
}
catch (Exception e)
{
Debug.WriteLine($"Test document not found to delete - this is normal.");
}
try
{
var document = await docClient
.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(databaseId, collectionId), doc)
.ConfigureAwait(false);
}
catch (Exception e)
{
Debug.WriteLine($"Create document message: {e.Message}");
}
// now read the permission back as it would happen on the server
var result = await docClient.ReadPermissionFeedAsync(userUri).ConfigureAwait(false);
if (result.Count > 0)
{
token = result.First(c => c.Id == partitionKey).Token;
}
}
catch (Exception ex)
{
Debug.WriteLine($"Create and get permission failed: {ex.Message}");
}
if (string.IsNullOrEmpty(token))
{
Debug.WriteLine("Did not find token");
}
return token;
}
static async Task CleanUp()
{
var docClient =
new DocumentClient(new Uri(dbServicePath), masterKey);
var doc = new { id = documentId, partitionId = partitionKey, message = "Sample document for testing" };
try
{
await docClient.DeleteDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId,
documentId), new RequestOptions { PartitionKey = new PartitionKey(partitionKey) }).ConfigureAwait(false);
}
catch (Exception e)
{
Debug.WriteLine($"Delete document message: {e.Message}");
}
}
static async Task DisplayErrorMessage(HttpResponseMessage response)
{
var messageDefinition =
new
{
code = "",
message = ""
};
var jsonString =
await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var message =
JsonConvert.DeserializeAnonymousType(jsonString, messageDefinition);
Debug.WriteLine($"Failed with {response.StatusCode} : {message.message}");
}
}
}
【问题讨论】:
标签: azure xamarin.forms azure-cosmosdb