#azure #azure-blob-storage #http-status-code-403
Вопрос:
Я работаю с хранилищем больших двоичных объектов Azure и C#. Мне нужно удалить большой двоичный объект с помощью API REST, но я получаю ошибку 403 Forbidden. Я использую ту же функцию для создания заголовка аутентификации для создания нового большого двоичного объекта, но она работает для put, но не для удаления. Это мой код:
public bool DeleteBlob(string containerName, string fileName)
{
string blobName = fileName;
string method = "DELETE";
string requestUri = $"https://{_accountName}.blob.core.windows.net/{containerName}/{blobName}";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri);
string now = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
request.Method = method;
request.Headers.Add("x-ms-version", "2020-10-02");
request.Headers.Add("x-ms-date", now);
request.Headers.Add("x-ms-delete-snapshots", "include");
request.Headers.Add("Authorization", AuthorizationHeader("DELETE", request, containerName, blobName));
bool result = false;
using (HttpWebResponse resp = (HttpWebResponse)request.GetResponse())
{
if (resp.StatusCode != HttpStatusCode.Accepted amp;amp; resp.StatusCode != HttpStatusCode.OK)
{
throw new Exception(resp.StatusDescription);
}
result = true;
}
return resu<
}
private string AuthorizationHeader(string method, HttpWebRequest request, string containerName, string blobName)
{
string urlResource = $"/{_accountName}/{containerName}/{blobName}";
string stringToSign = $"{method}nnn{request.ContentLength}nn{request.ContentType}nnnnnnn{GetCanonicalizedHeaders(request)}{GetCanonicalizedResource(request.RequestUri, _accountName)}";
HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(_accountKey));
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
return string.Format("{0} {1}:{2}", "SharedKey", _accountName, signature);
}
private string GetAuthorizationHeader(string method, HttpWebRequest request, string now, string containerName, string blobName)
{
string urlResource = $"/{_accountName}/{containerName}/{blobName}";
var canonicalizedStringToBuild = string.Format("{0}n{1}", now, $"/{_accountName}/" urlResource);
string signature;
using (var hmac = new HMACSHA256(Convert.FromBase64String(_accountKey)))
{
byte[] dataToHmac = Encoding.UTF8.GetBytes(canonicalizedStringToBuild);
signature = Convert.ToBase64String(hmac.ComputeHash(dataToHmac));
}
return string.Format($"{_accountName}:" signature);
}
private string GetCanonicalizedHeaders(HttpWebRequest request)
{
ArrayList headerNameList = new ArrayList();
StringBuilder sb = new StringBuilder();
foreach (string headerName in request.Headers.Keys)
{
if (headerName.ToLowerInvariant().StartsWith("x-ms-", StringComparison.Ordinal))
{
headerNameList.Add(headerName.ToLowerInvariant());
}
}
headerNameList.Sort();
foreach (string headerName in headerNameList)
{
StringBuilder builder = new StringBuilder(headerName);
string separator = ":";
foreach (string headerValue in GetHeaderValues(request.Headers, headerName))
{
string trimmedValue = headerValue.Replace("rn", String.Empty);
builder.Append(separator);
builder.Append(trimmedValue);
separator = ",";
}
sb.Append(builder.ToString());
sb.Append("n");
}
return sb.ToString();
}
private string GetCanonicalizedResource(Uri address, string accountName)
{
StringBuilder str = new StringBuilder();
StringBuilder builder = new StringBuilder("/");
builder.Append(accountName);
builder.Append(address.AbsolutePath);
str.Append(builder.ToString());
NameValueCollection values2 = new NameValueCollection();
if (address.Query.Length > 0)
{
string query = address.Query.Remove(0, 1);
var queryParts = query.Split('amp;');
foreach (var queryPart in queryParts)
{
var parts = queryPart.Split('=');
values2.Add(parts[0], parts[1]);
}
ArrayList list2 = new ArrayList(values2.AllKeys);
list2.Sort();
foreach (string str3 in list2)
{
StringBuilder builder3 = new StringBuilder(string.Empty);
builder3.Append(str3);
builder3.Append(":");
builder3.Append(values2[str3]);
str.Append("n");
str.Append(builder3.ToString());
}
}
return str.ToString();
}
Кто-нибудь может сказать мне, что я делаю не так? Как я уже отмечал, для создания нового большого двоичного объекта все работает нормально, но не для удаления. Я не знаю, отличается ли заголовок аутентификации для удаления (я знаю, что имя метода другое). Я не нашел много информации об этом.
Танкс.
Комментарии:
1. Есть ли причина, по которой вы пишете код для использования REST API вместо использования .Net SDK?
2. Привет, Гаурав. Спасибо за ваш ответ. Да, есть причина: я должен добавить эту функциональность в действие пользовательского рабочего процесса Dynamics 365, и невозможно добавить внешние сборки в проект C# кода. Поэтому мне нужно использовать API REST. Это системное ограничение.
Ответ №1:
Причина, по которой ваша операция удаления завершается 403
ошибкой, заключается stringToSign
в том, что вы назначаете длину содержимого 0
, в то время как это должна быть пустая строка.
Пожалуйста, измените свой AuthorizationHeader
код метода на код, указанный ниже, и ваша операция удаления большого двоичного объекта должна работать просто отлично:
private string AuthorizationHeader(string method, HttpWebRequest request, string containerName, string blobName)
{
string urlResource = $"/{_accountName}/{containerName}/{blobName}";
var contentLength = request.ContentLength == 0 ? "" : request.ContentLength.ToString();
string stringToSign = $"{method}nnn{contentLength}nn{request.ContentType}nnnnnnn{GetCanonicalizedHeaders(request)}{GetCanonicalizedResource(request.RequestUri, _accountName)}";
HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(_accountKey));
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
return string.Format("{0} {1}:{2}", "SharedKey", _accountName, signature);
}
То, что я сделал выше, — это установил значение длины содержимого в пустую строку, если длина содержимого равна 0.
Из этого link
(курсив мой):
В текущей версии поле Длина содержимого должно быть пустой строкой, если длина содержимого запроса равна нулю. В версии 2014-02-14 и более ранних версиях длина содержимого была включена, даже если она равна нулю.