Идея статьи возникла при решении задачи вызова WebApi, разработанных специалистами Заказчика. При анализе появилось три варианта решения данной задачи, которые мы и рассмотрим. В статье мы будем обращаться к WebApi DirectumRX, тем самым, затронем еще и особенности обращения к WebApi DirectumRX. Отмечу, что это могут быть любые другие REST API, любой другой системы.
Само решение WebApi DirectumRX представляют собой интеграционное API, которое использует протокол OData, которое является одним из готовых стандартов для создания RESTful API.
(Open Data Protocol (OData) – открытый веб-протокол для запроса и обновления данных. Он позволяет выполнять операции с ресурсами, используя в качестве запросов HTTP-команды, а также обмениваться данными в форматах JSON или XML).
Механизмы формирования запросов в .Net будем рассматривать на простейшем примере отправки POST запроса для создания новой должности. Предположим, что веб-сервисы развернуты на локальной машине по адресу 'http://localhost/WebApi/odata’. Если все корректно развернуто, то можно получить описание методов, выполнив GET запрос по адресу 'http://localhost/WebApi/odata/$metadata’ или просто открыв его в браузере.
1. Сформируем json-тело запроса:
JSON BODY:
{
"Name": "Специалист первой категории",
"Status": "Active"
}
2. Выполним POST запрос для создания должности 'http://localhost/WebApi/odata/JobTitle’.
3. Получим ответ от сервиса. Это может быть как успешное создание:
STATUS CODE: 201
STATUS DESCRIPTION: Created
JSON BODY:
{
"@odata.context": "http://localhost/WebApi/odata/$metadata#JobTitle/$entity",
"Id": 1,
"Name": "Специалист первой категории",
"Status": "Active"
}
так и ошибка:
STATUS CODE: 404
STATUS DESCRIPTION: Not Found
JSON BODY:
{
"error": {
"code": "",
"message": "No HTTP resource was found that matches the request URI 'http://localhost/WebApi/odata/JobTitle'."
}
}
При выполнении пунктов 1-3 используются инструменты разработчика браузера или вспомогательные программы, такие как Postman. В результате мы получили знание о структуре данных и правиле сериализации/десериализации этих данных в структурированные объекты.
4. Внутри приложения, которое будет подключаться к WebApi, создадим классы должности для сериализации тела запроса:
/// <summary>
/// Должность RX.
/// </summary>
class TestJobTitle
{
public int Id { get; set; }
public string Name { get; set; }
public string Status { get; set; }
}
и ошибки для десериализации полученного ответа:
/// <summary>
/// Содержимое ошибки.
/// </summary>
class ResponseError
{
public string code { get; set; }
public string message { get; set; }
}
/// <summary>
/// Ответ сервиса с ошибкой.
/// </summary>
class ResponseErrorHeader
{
public ResponseError error { get; set; }
}
5. Выполним обращение к веб-сервисам, используя один из интерфейсов .Net. Подробнее об этом в следующей части статьи.
На данном этапе мы уже можем «поиграться» с объектами, сериализовать/десериализвать их, отладить, посмотреть какие строки формируются при сериализации, сравнить их с ожидаемыми телами запроса и ответа. В примерах ниже используется сборка Newtonsoft.Json, которая позволяет в одну строчку кода сериализовать/десериализвать dynamic объекты.
Теперь рассмотрим особенности реализаций обращения к сервису через различные интерфейсы .Net. И сразу же сделаем все запросы асинхронными, т.к. методы могут вызываться как в фоновых процессах, так и в клиентских действиях.
Самый низкоуровневый способ реализации. Из-за чего реализация получается с большим количеством строк кода. В то же время предоставляет больше понимания/контроля над всеми действиями. Сборка доступна в среде разработки DirectumRX.
Для получения информации, специфичной для протокола HTTPS, используются два класса: HttpWebRequest и HttpWebResponse, которые наследуются соответственно от WebRequest и WebResponse.
Пример 1. Обращение с помощью System.Net.WebRequest и System.Net.WebResponse:
public Dictionary<int, string> CallWebRequest(string url, string userName, string password, string json)
{
var callResult = new Dictionary<int, string>();
// Формирование запроса.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ContentType = "application/json";
request.Method = "POST";
// Basic-Authorization: Авторизация по логину и паролю.
var byteArray = Encoding.ASCII.GetBytes(string.Format("{0}:{1}", userName, password));
request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(byteArray));
// Запись тела запроса.
using (Stream stream = request.GetRequestStream())
{
var encoder = new UTF8Encoding();
var resultByteArr = encoder.GetBytes(json);
stream.Write(resultByteArr, 0, resultByteArr.Length);
}
// Асинхронный запрос веб-сервиса.
var result = request.BeginGetResponse(null, null);
result.AsyncWaitHandle.WaitOne();
try
{
using (var response = request.EndGetResponse(result) as HttpWebResponse)
{
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
// Успешное создание записи должности.
string responseBody = streamReader.ReadToEnd();
var jobTitle = JsonConvert.DeserializeObject<TestJobTitle>(responseBody);
callResult = new Dictionary<int, string>() { { (int)response.StatusCode, string.Format("Создана запись {0}, с ИД {1}", jobTitle.Name, jobTitle.Id) } };
}
}
}
catch (WebException wex)
{
using (var response = wex.Response as HttpWebResponse)
{
using (StreamReader streamReader = new StreamReader(response.GetResponseStream(), true))
{
// Ошибка запроса.
string responseBody = streamReader.ReadToEnd();
var errorHeader = JsonConvert.DeserializeObject<ResponseErrorHeader>(responseBody);
callResult = new Dictionary<int, string>() { { (int)response.StatusCode, errorHeader.error.message } };
}
}
}
return callResult;
}
Абстракция над WebRequest и WebResponse для самых популярных действий. Меньше кода. Сборка доступна в среде разработки DirectumRX.
Пример 2. Обращение с помощью System.Net.WebClient:
public Dictionary<int, string> CallWebClient(string url, string userName, string password, string json)
{
var callResult = new Dictionary<int, string>();
using (var client = new WebClient())
{
// Basic-Authorization: Авторизация по логину и паролю.
var byteArray = Encoding.ASCII.GetBytes(string.Format("{0}:{1}", userName, password));
client.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(byteArray));
client.Headers.Add("Content-Type", "application/json");
client.Encoding = Encoding.UTF8;
try
{
// Асинхронный запрос веб-сервиса.
string responseBody = client.UploadString(url, "POST", json);
// Успешное создание записи должности.
var jobTitle = JsonConvert.DeserializeObject<TestJobTitle>(responseBody);
callResult = new Dictionary<int, string>() { { 202, string.Format("Создана запись {0}, с ИД {1}", jobTitle.Name, jobTitle.Id) } };
}
catch (WebException wex)
{
// Получить содержимое ответа.
using (var response = wex.Response as HttpWebResponse)
{
using (StreamReader streamReader = new StreamReader(response.GetResponseStream(), true))
{
// Ошибка запроса.
string responseBody = streamReader.ReadToEnd();
var errorHeader = JsonConvert.DeserializeObject<ResponseErrorHeader>(responseBody);
callResult = new Dictionary<int, string>() { { (int)response.StatusCode, errorHeader.error.message } };
}
}
}
}
return callResult;
}
Абстракция над HttpWebRequest и HttpWebResponse. По сути, HttpClient - своего рода фабрика запросов, которая значительно упрощает реализацию многих сценариев работы с HTTP. Сборка не доступна в среде разработки DirectumRX.
Пример 3. Обращение с помощью System.Net.Http.HttpClient:
public Dictionary<int, string> CallHttpClient(string url, string userName, string password, string json)
{
var callResult = new Dictionary<int, string>();
using (var client = new HttpClient())
{
// Basic-Authorization: Авторизация по логину и паролю.
var byteArray = Encoding.ASCII.GetBytes(string.Format("{0}:{1}", userName, password));
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
// Асинхронный запрос веб-сервиса.
var response = client.PostAsync(url, new StringContent(json, Encoding.UTF8, "application/json"));
var responseBody = response.Result.Content.ReadAsStringAsync().Result;
int statusCode = (int)response.Result.StatusCode;
if (statusCode >= 200 && statusCode < 300)
{
// Успешное создание записи должности.
var jobTitle = JsonConvert.DeserializeObject<TestJobTitle>(responseBody);
callResult = new Dictionary<int, string>() { { statusCode, string.Format("Создана запись {0}, с ИД {1}", jobTitle.Name, jobTitle.Id) } };
}
if (statusCode >= 400 && statusCode < 500)
{
// Ошибка запроса.
var errorHeader = JsonConvert.DeserializeObject<ResponseErrorHeader>(responseBody);
callResult = new Dictionary<int, string>() { { statusCode, errorHeader.error.message } };
}
}
return callResult;
}
Плюсы и минусы каждого способа зависят от решаемых задач. Варианты можно обсудить в комментариях к данной статье. Ниже приведена сравнительная таблица клиентских сборок, найденная на просторах интернета:
WebClient |
HttpClient |
Доступно в более старых версиях .Net |
Только .Net 4.5 |
Абстракция над WebRequest и WebResponse |
Абстракция над HttpWebRequest и HttpWebResponse |
Доступны синхронные и асинхронные методы |
Только асинхронные методы |
Предоставляет информацию о прогрессе загрузки |
Не предоставляет информацию о прогрессе загрузки |
Не использует повторно разрешенный DNS, cookie |
Может повторно использовать разрешенный DNS, cookie |
Для каждого конкурентного запроса необходимо создание экземпляра WebClient |
Нет необходимости создавать экземпляр HttpClient для каждого запроса |
Более сложная отладка запросов |
Простая отладка запросов |
Поддерживает FTP |
Не поддерживает FTP |
Приложения WinRT не могут использовать WebClient |
Можно использовать с WinRT |
Материала в интернете по ним не сказал бы что много, но достаточно. Каждый имеет свои «особенности». Например, в WebClient при успешном выполнении запроса невозможно получить код статуса ответа. В итоге, с большинством задач справляется HttpClient (при условии HTTP запросов), он наиболее простой в использовании и, в то же время мощный.
Подскажите пожалуйста, как включить Rest API в Directum?
У меня сейчас по http://localhost/WebApi/odata 404 ошибка
Андрей, добрый день!
Если при обращении к данной ссылке http://localhost/WebApi/odata/$metadata получаю ошибку следующего содержания:
Доступ к localhost запрещен
У вас нет прав для просмотра этой страницы.
HTTP ERROR 403
Можете посоветовать где глянуть настройки прав?
Добрый день! Получить или создать сущность понятно, вопрос как обновить уже имеющуюся по ИД? Можно на примере постмана, если есть такая возможность в штатной поставке сервиса.
Добрый день!
Подскажите, как работать с API, если Directum в облаке?
Павел, уточните 1) требуется из Directum обращаться к какому-то внешнему API или же внешняя система должна обратиться к API Directum? 2) какая разновидность облака имеется в виду (публичное, где только коробка, или же выделенное, где коробка + заказные доработки)?
Авторизуйтесь, чтобы написать комментарий