Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
ОБЛАСТЬ ПРИМЕНЕНИЯ: все уровни управления API
Политики, доступные в службе управления API Azure, позволяют выполнять множество полезных задач исключительно на основе входящих запросов, исходящих ответов и сведений о базовой конфигурации. Однако возможность взаимодействия с внешними службами управления из политик управления API предоставляет гораздо больше преимуществ.
В предыдущих статьях вы узнали, как взаимодействовать со службой Центров событий Azure для ведения журнала, мониторинга и аналитики. В этой статье вы узнаете о политиках, которые позволяют работать с любой внешней HTTP-службой. Эти политики можно использовать для запуска удаленных событий или для получения данных, которые определенным образом применяются для обработки исходного запроса и ответа.
Отправка однонаправленного запроса
Возможно, самым простым внешним взаимодействием является одноразовый стиль запроса, который позволяет внешней службе получать уведомления о каком-то важном событии. Политику choose потока управления можно использовать для обнаружения любого интересующего вас условия. Если условие выполняется, можно сделать внешний HTTP-запрос, используя политику отправки одностороннего запроса. Этот запрос может быть в системе обмена сообщениями, например Hipchat или Slack, или в API почты, например SendGrid или MailChimp, или для критически важных инцидентов поддержки, таких как PagerDuty. Все эти системы обмена сообщениями обладают простыми API-интерфейсами HTTP, которые могут быть вызваны.
Оповещения с использованием Slack
В следующем примере демонстрируется отправка сообщения в чат Slack, если код состояния HTTP-ответа больше или равен 500. Ошибка диапазона 500 указывает на проблему с внутренним API, которую клиент API не может устранить самостоятельно. Обычно это требует какого-либо вмешательства в части части управления API.
<choose>
<when condition="@(context.Response.StatusCode >= 500)">
<send-one-way-request mode="new">
<set-url>https://hooks.slack.com/services/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</set-url>
<set-method>POST</set-method>
<set-body>@{
return new JObject(
new JProperty("username","APIM Alert"),
new JProperty("icon_emoji", ":ghost:"),
new JProperty("text", String.Format("{0} {1}\nHost: {2}\n{3} {4}\n User: {5}",
context.Request.Method,
context.Request.Url.Path + context.Request.Url.QueryString,
context.Request.Url.Host,
context.Response.StatusCode,
context.Response.StatusReason,
context.User.Email
))
).ToString();
}</set-body>
</send-one-way-request>
</when>
</choose>
В системе Slack используется понятие входящих веб-привязок. При настройке входящего веб-перехватчика Slack создает специальный URL-адрес, который позволяет выполнять базовый запрос POST и передавать сообщение в канал Slack. Создаваемый текст JSON основан на формате, определенном системой Slack.
Достаточно ли хороша стратегия «выпустил и забыл»?
Использование запросов в стиле «пуск и забыть» связано с некоторыми компромиссами. Если по какой-то причине запрос завершается ошибкой, сообщение об ошибке не сообщается. В этой ситуации сложность вторичной системы отчетности о сбоях и дополнительная затратность на производительность из-за ожидания ответа не являются оправданными. В сценариях, где необходимо проверить ответ, лучше использовать политику отправки запросов .
Отправить запрос
Политика send-request позволяет использовать внешнюю службу для выполнения сложных действий по обработке и возврату данных в службу управления API для дальнейшей обработки политики.
Авторизация маркеров ссылок
Основная задача API управления состоит в защите серверных ресурсов. Если сервер авторизации, используемый вашим API, создает JSON Web Tokens (JWT) в рамках своего потока OAuth2, как это делает Microsoft Entra ID, можно использовать политику validate-jwt или политику validate-azure-ad-token для проверки действительности токена. Некоторые серверы авторизации создают то, что называются маркерами ссылок , которые не могут быть проверены без обратного вызова на сервер авторизации.
Стандартизованный самоанализ
В прошлом не существовало стандартизированного способа проверки референсного токена с сервером авторизации. Тем не менее, группа задач по разработке Интернета (IETF) недавно опубликовала предлагаемый стандарт RFC 7662 , который определяет, как сервер ресурсов может проверить допустимость токена.
Извлечение токена
Первым действием является извлечение маркера из заголовка авторизации. Значение заголовка должно быть отформатировано с помощью схемы авторизации Bearer — согласно RFC 6750 сначала указывается одиночный пробел, за которым следует маркер авторизации. К сожалению, существуют случаи, когда схема авторизации опускается. Чтобы учитывать это упущение при синтаксическом анализе, управление API разбивает значение заголовка на пробел и выбирает последнюю строку из возвращаемого массива строк. Этот метод предоставляет обходной путь для плохо отформатированных заголовков авторизации.
<set-variable name="token" value="@(context.Request.Headers.GetValueOrDefault("Authorization","scheme param").Split(' ').Last())" />
Выполнение запроса на проверку
После того как служба управления API получила маркер авторизации, она может выполнить запрос на проверку маркера. Согласно RFC 7662, этот процесс называется интроспекцией, и требуется, чтобы вы отправили HTML-форму в ресурс интроспекции с помощью POST. HTML-форма должна содержать по крайней мере пару "ключ-значение" с ключом token. Этот запрос на сервер авторизации также должен быть аутентифицирован, чтобы гарантировать, что вредоносные клиенты не могут искать действительные токены.
<send-request mode="new" response-variable-name="tokenstate" timeout="20" ignore-error="true">
<set-url>https://microsoft-apiappec990ad4c76641c6aea22f566efc5a4e.azurewebsites.net/introspection</set-url>
<set-method>POST</set-method>
<set-header name="Authorization" exists-action="override">
<value>basic dXNlcm5hbWU6cGFzc3dvcmQ=</value>
</set-header>
<set-header name="Content-Type" exists-action="override">
<value>application/x-www-form-urlencoded</value>
</set-header>
<set-body>@($"token={(string)context.Variables["token"]}")</set-body>
</send-request>
Проверка ответа
Атрибут response-variable-name используется для предоставления доступа к возвращаемого ответа. Имя, определенное в этом свойстве, можно использовать в качестве ключа к словарю context.Variables для доступа к объекту IResponse.
Из объекта ответа можно извлечь его тело, а RFC 7622 указывает, что управление API ожидает ответ в виде объекта JSON, который должен содержать, по крайней мере, свойство с именем active, являющееся логическим значением. Если active задано значение true, маркер считается допустимым.
Кроме того, если сервер авторизации не включает "active" поле, указывающее, является ли маркер допустимым, используйте средство http-клиента, например curl для определения свойств, заданных в допустимом токене. Например, если допустимый ответ маркера содержит свойство с именем "expires_in", проверьте, существует ли это имя свойства в ответе сервера авторизации таким образом:
<when condition="@(((IResponse)context.Variables["tokenstate"]).Body.As<JObject>().Property("expires_in") == null)">
Неудача при отправке отчета
Для обнаружения недопустимого маркера используется политика <choose>, и если маркер является недопустимым, возвращается ответ 401.
<choose>
<when condition="@((bool)((IResponse)context.Variables["tokenstate"]).Body.As<JObject>()["active"] == false)">
<return-response response-variable-name="existing response variable">
<set-status code="401" reason="Unauthorized" />
<set-header name="WWW-Authenticate" exists-action="override">
<value>Bearer error="invalid_token"</value>
</set-header>
</return-response>
</when>
</choose>
Как указано в RFC 6750, в котором описывается, как должны использоваться токены bearer, система управления API также возвращает заголовок ответа с кодом 401 WWW-Authenticate. WWW-Authenticate предназначен для предоставления клиенту сведений о создании надлежащим образом авторизованного запроса. Благодаря широкому спектру подходов к платформе OAuth2 трудно сообщить все необходимые сведения. К счастью, существуют возможности, позволяющие клиентам узнать о правильной авторизации запросов к серверу ресурсов.
Окончательное решение
В итоге вы получите следующую политику:
<inbound>
<!-- Extract Token from Authorization header parameter -->
<set-variable name="token" value="@(context.Request.Headers.GetValueOrDefault("Authorization","scheme param").Split(' ').Last())" />
<!-- Send request to Token Server to validate token (see RFC 7662) -->
<send-request mode="new" response-variable-name="tokenstate" timeout="20" ignore-error="true">
<set-url>https://microsoft-apiappec990ad4c76641c6aea22f566efc5a4e.azurewebsites.net/introspection</set-url>
<set-method>POST</set-method>
<set-header name="Authorization" exists-action="override">
<value>basic dXNlcm5hbWU6cGFzc3dvcmQ=</value>
</set-header>
<set-header name="Content-Type" exists-action="override">
<value>application/x-www-form-urlencoded</value>
</set-header>
<set-body>@($"token={(string)context.Variables["token"]}")</set-body>
</send-request>
<choose>
<!-- Check active property in response -->
<when condition="@((bool)((IResponse)context.Variables["tokenstate"]).Body.As<JObject>()["active"] == false)">
<!-- Return 401 Unauthorized with http-problem payload -->
<return-response response-variable-name="existing response variable">
<set-status code="401" reason="Unauthorized" />
<set-header name="WWW-Authenticate" exists-action="override">
<value>Bearer error="invalid_token"</value>
</set-header>
</return-response>
</when>
</choose>
<base />
</inbound>
Этот пример является лишь одним из многих, демонстрирующих, как send-request политику можно использовать для интеграции полезных внешних служб в процесс запросов и ответов, поступающих через службу управления API.
Структура ответа
Политику send-request можно использовать для повышения первичного запроса к серверной системе, как показано в предыдущем примере, или его можно использовать для полной замены внутреннего вызова. С помощью этой методики можно легко создавать сложные ресурсы, которые объединяются из нескольких разных систем.
Создание панели мониторинга
Иногда возникает потребность предоставить информацию из нескольких серверных систем, например, для использования в панели мониторинга. Ключевые показатели производительности (ключевые показатели эффективности) приходят из всех разных внутренних серверов, но вы предпочитаете не предоставлять прямой доступ к ним. Тем не менее, было бы хорошо, если бы все сведения можно было получить в одном запросе. Возможно, некоторые фоновые данные потребуется сначала разделить на части и немного очистить. Способность кэшировать составной ресурс будет полезным способом уменьшить нагрузку на серверную часть, так как у пользователей есть привычка нажимать клавишу F5, чтобы узнать, могут ли измениться их неэффективные метрики.
Подделка ресурса
Первым шагом в создании ресурса панели мониторинга является настройка новой операции на портале Azure. Эта операция плейсхолдера настраивает политику композиции для построения динамического ресурса.
Выполнение запросов
После создания операции можно настроить политику специально для этой операции.
Первым шагом является извлечение всех параметров запроса из входящего запроса, чтобы их можно было переслать в серверную часть. В этом примере на панели мониторинга сведения отображаются на основе определенного периода времени, поэтому здесь присутствуют параметры fromDate и toDate. С помощью политики set-variable можно извлечь сведения из URL-адреса запроса.
<set-variable name="fromDate" value="@(context.Request.Url.Query["fromDate"].Last())">
<set-variable name="toDate" value="@(context.Request.Url.Query["toDate"].Last())">
После получения этой информации можно выполнять запросы ко всем серверным системам. Каждый запрос создает новый URL-адрес со сведениями о параметрах, вызывает соответствующий сервер и сохраняет ответ в переменной контекста.
<send-request mode="new" response-variable-name="revenuedata" timeout="20" ignore-error="true">
<set-url>@($"https://accounting.acme.com/salesdata?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")</set-url>
<set-method>GET</set-method>
</send-request>
<send-request mode="new" response-variable-name="materialdata" timeout="20" ignore-error="true">
<set-url>@($"https://inventory.acme.com/materiallevels?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")</set-url>
<set-method>GET</set-method>
</send-request>
<send-request mode="new" response-variable-name="throughputdata" timeout="20" ignore-error="true">
<set-url>@($"https://production.acme.com/throughput?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")</set-url>
<set-method>GET</set-method>
</send-request>
<send-request mode="new" response-variable-name="accidentdata" timeout="20" ignore-error="true">
<set-url>@($"https://production.acme.com/accidentdata?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")</set-url>
<set-method>GET</set-method>
</send-request>
Управление API отправляет эти запросы последовательно.
Реагирование
Для формирования составного ответа можно использовать политику возврата ответа. Элемент set-body может применять выражение для создания нового JObject со всеми представлениями компонентов, внедренными в качестве свойств.
<return-response response-variable-name="existing response variable">
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>
@(new JObject(new JProperty("revenuedata",((IResponse)context.Variables["revenuedata"]).Body.As<JObject>()),
new JProperty("materialdata",((IResponse)context.Variables["materialdata"]).Body.As<JObject>()),
new JProperty("throughputdata",((IResponse)context.Variables["throughputdata"]).Body.As<JObject>()),
new JProperty("accidentdata",((IResponse)context.Variables["accidentdata"]).Body.As<JObject>())
).ToString())
</set-body>
</return-response>
Законченная политика выглядит следующим образом:
<policies>
<inbound>
<set-variable name="fromDate" value="@(context.Request.Url.Query["fromDate"].Last())">
<set-variable name="toDate" value="@(context.Request.Url.Query["toDate"].Last())">
<send-request mode="new" response-variable-name="revenuedata" timeout="20" ignore-error="true">
<set-url>@($"https://accounting.acme.com/salesdata?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
<set-method>GET</set-method>
</send-request>
<send-request mode="new" response-variable-name="materialdata" timeout="20" ignore-error="true">
<set-url>@($"https://inventory.acme.com/materiallevels?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
<set-method>GET</set-method>
</send-request>
<send-request mode="new" response-variable-name="throughputdata" timeout="20" ignore-error="true">
<set-url>@($"https://production.acme.com/throughput?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
<set-method>GET</set-method>
</send-request>
<send-request mode="new" response-variable-name="accidentdata" timeout="20" ignore-error="true">
<set-url>@($"https://production.acme.com/accidentdata?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
<set-method>GET</set-method>
</send-request>
<return-response response-variable-name="existing response variable">
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>
@(new JObject(new JProperty("revenuedata",((IResponse)context.Variables["revenuedata"]).Body.As<JObject>()),
new JProperty("materialdata",((IResponse)context.Variables["materialdata"]).Body.As<JObject>()),
new JProperty("throughputdata",((IResponse)context.Variables["throughputdata"]).Body.As<JObject>()),
new JProperty("accidentdata",((IResponse)context.Variables["accidentdata"]).Body.As<JObject>())
).ToString())
</set-body>
</return-response>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
</policies>
Итоги
Служба управления API Azure предоставляет гибкие политики, выборочно применяемые к HTTP-трафику, и позволяет формировать серверные службы. Если вы хотите усовершенствовать имеющийся шлюз API за счет функций оповещения, проверки или создать новые сложные ресурсы на основе нескольких серверных служб, send-request и связанные политики предоставят вам широкий диапазон новых возможностей.