Azure API Management é mais que um proxy. É mais que um gateway. É o APIM.
As APIs são a realidade de muitas empresas e é com esse recurso de arquitetura que muitos problemas são resolvidos, por exemplo, integrações, escala, segregação e resiliência. Com o rápido crescimento do número de APIs criadas em cada solução se faz necessário centralizar, administrar e documentar esses recursos. Nesse momento, entra em cena o Azure API Management - APIM!
O APIM é um API Gateway com orquestrações e integrações com os principais produtos do Azure como: Azure AD, Azure AD B2C, Key Vault e Application Insights.
Basicamente, colocamos o APIM na frente das nossas APIs ou das APIs de terceiros, desta forma, o APIM recebe as requisições, que passam por um pipeline (Fluxo de trabalho) e as redireciona para o backend, funcionando como um proxy ou um gateway convencional, mas não é só isso!!! o APIM é especializado em APIs, oferecendo uma série de funcionalidades que podem ser incorporadas na requisição, as quais são chamadas de "Políticas", e estas podem agregar resiliência, comportamentos, rastreamento, cache, logs e outros recursos.
Além dos recursos citados, temos o Portal do desenvolvedor que junto com Portal de gerenciamento do Azure fazem do Azure API management muito mais que um proxy, muito mais que um gateway. saiba mais
Os três Componentes Principais do APIM
O Azure API Management é uma solução completa que oferece esse mecanismo de Gateway além de dois outros componentes que vão ajudá-lo a distribuir, documentar e manter suas APIs, são eles;
- Portal do Desenvolvedor
- Gateway
- Portal do Azure
Portal do Desenvolvedor
Quem nunca passou pela sensação de estar criando uma API que já existia? Isso é mais comum do que parece e acontece porque não temos um ponto central de distribuição dessas APIs. O portal do desenvolvedor, é um site que além de conteúdo sobre seu programa de APIs, vai nos permitir encontrar todas as APIS disponíveis, entender como testá-las com informações técnicas de como fazer as chamadas além disso possui um mecanismo de autosserviço que permite que seus consumidores possam criar uma assinatura e começar a usar as APÌs sem necessidade de intervenções, chamados ou coisa do tipo.
Gateway
Esse é o coração do produto, ou melhor, cérebro. É nele que o direcionamento para o backend é feito, e nele que podemos criar comportamentos adicionais na requisição (políticas) que podem implementar um cache, uma validação de segurança, ou mesmo uma lógica que decide a qual backend a chamada deve ser realizada.
Além disso um recurso absolutamente incrível é que esse gateway pode ser hospedado em diferentes infraestruturas, outras nuvens, e até mesmo em servidores on-premisse. Isso diminui a latência entre o gateway e o backend, aumentando a resiliência e diminuindo o aprisionamento tecnológico.
Portal do Azure
O portal do Azure é onde a mágica acontece! É nele onde gerenciamos o produto como um todo de forma centralizada, e podemos fazer as importações e criações das nossas APIs, aplicar as diversas políticas, criar produtos, e fazer as configurações em geral do dia a dia de uma operação.
Acima começamos a utilizar o vocabulário do APIM. Então, para contextualizar, vamos explicar os principais conceitos. Eu chamo esses termos de as chaves do castelo (em homenagem ao Clóvis de Barros Filho), pois são de fato os conceitos que vão abrir as portas do entendimento do produto.
- Operações: são as funcionalidades propriamente ditas. Os serviços expostos na API por meio de alguma URI e verbo http
- APIs: são os conjuntos de operações
- Produtos: são conjuntos de APIs
- Escopos: são espaços delimitados de recursos onde aplicamos políticas. Por exemplo: todas as APIs, uma API, uma Operações ou um produto.
- Políticas: são Comportamentos adicionais aplicados à um determinado escopo mudando o comportamento original da requisição.
Mais sobre os escopos
Antes de começar é importante entender o conceito de escopo do APIM, cada política é aplicada em um escopo e pode ser herdara para escopos mais específicos por meio da política base
Os escopos existentes são;
- Produto: afeta todas as APIs do produto.
- Todas as APIs: afetam todos os recursos da API.
- API: afeta todas as operações (verbos) da API.
- Operação: afeta à operação
Por exemplo, temos um produto chamado TestPolicy, e, portanto, para criarmos políticas nesse escopo precisamos entrar nesse produto através do menu lateral.
No Escopo de todas as APIs. Normalmente, temos o forward-request nesse nível, essa política já nasce com a instancia e é responsável por entregar as requisições para o backend.
Escopo da API que engloba todo o conjunto de operações ou verbos HTTP de diferentes recursos.
Escopo da operação é onde de fato temos as funcionalidades finais, por exemplo os verbos de uma API REST.
Botando a mão na massa.
Acreditamos que com os conceitos acima, você já tenha tido uma ideia de como funciona o produto. A seguir, vamos mostrar alguns exemplos usados em campo por engenheiros Microsoft, e que foram muito uteis em situações do dia a dia. Você verá uma descrição de como será a API de backend que vamos usar, e na sequência vamos catalogando os exemplos de forma que fique fácil de consultar posteriormente.
Backend
https://sample-api-users.azurewebsites.net
Swagger
https://sample-api-users.azurewebsites.net/swagger/index.html
Swagger Json (OpenAPI)
https://sample-api-users.azurewebsites.net/swagger/v1/swagger.json
O APIM tem esses endpoints
O APIM mesmo em uma rede interna vai sempre possuir um IP público utilizado para serviço de gerenciamento, isso faz parte da infraestrutura do produto e não representa qualquer vulnerabilidade de segurança, para saber mais sobre isso clique aqui
Gateway URL
https://apimestudodev01.azure-api.net
Developer portal URL
https://apimestudodev01.developer.azure-api.net
Controller principal de backend
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sample.Controllers
{
[ApiController]
[Route("[controller]")]
public class RolesController : ControllerBase
{
private readonly ILogger<RolesController> _logger;
public RolesController(ILogger<RolesController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<Roles> Get()
{
return new List<Roles> { new Roles { name = "Adm" } };
}
}
}
Controller para teste da política de send-request
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
namespace Sample.Controllers
{
[ApiController]
[Route("[controller]")]
public class IntrospectionController : Controller
{
private readonly ILogger<IntrospectionController> _logger;
public IntrospectionController(ILogger<IntrospectionController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult Get()
{
return View();
}
[HttpPost]
public IActionResult Post()
{
return Ok(new
{
tokenGuid = Guid.NewGuid().ToString()
});
}
}
}
Importação e gerenciamento de APIS
A importação de novas APIs pode ser feita com qualquer arquivo no padrão OpenAPI, os famosos "Swaggers" bem conhecidos em API .net são um exemplo disso. Para isso, use o menu "New API" e escolha o item "OpenAPI" passando o endereço do arquivo json do Swagger acima.
Ao importar o *.json do Swagger, verifique se o backend foi configurado.
As APIs oferecem um menu lateral onde podemos importar para uma API existente novos endpoints utilizando diferentes arquivos do swagger OpenAPI, ou podemos usar o mesmo arquivo swagger original da API atualizando as operações e recursos disponíveis conforme a evolução do projeto.
Abaixo podemos ver uma API chamada de Seed.Api recebendo um incremento de um outro arquivo do swagger com outros endpoints da API Sample
O resultado manterá o que já existia, e adiciona novos endpoints conforme abaixo.
Além disso podemos exportar uma especificação OpenAPI de uma API e importar em outra, facilitando a composição de APIs com os endpoints que nós desejamos agrupar.
Annotations
Podemos decorar nossos métodos com atributos extras que permitem passar mais informações para o arquivo Json Open API gerado pelo Swagger. Um exemplo disso, é o pacote Swashbuckle.AspNetCore.Annotations.
[HttpGet]
[SwaggerOperation(OperationId ="Papeis",Description = "Lista Papeis", Summary = "Papeis")]
public IEnumerable<Role> Get(){ return new List<Role> { new Role { Name = "Adm" } };}
Observe abaixo na lista de operações uma operação de GET com a descrição de "papeis".
Primeiro exemplo batendo no backend.
Aqui vamos utilizar as informações fornecidas acima sobre nossa API. Dê uma olhada nesses códigos para entender melhor o que está acontecendo aqui nesse exemplo inicial.
A partir deste ponto, você terá fechado o entendimento macro da ferramenta e poderá se aprofundar nas políticas existentes, configurações de arquitetura para alta disponibilidade, e portal do desenvolvedor.
Perceba que temos a URL https://sample-api-users.azurewebsites.net/Roles configurada no pipeline do APIM, ou seja, é nessa API que vamos bater.
Chamada original pelo swagger
Use o Swagger para entender melhor essa API assim vai ser mais fácil entender o fluxo, saber o que o backend faz ajuda a entender o que as políticas fazem pois elas sempre complementam o comportamento das requisições originais
Usando a aba Test
Aqui vamos poder testar nossa API. Vamos poder fazer uma chamada HTTP no endpoint do APIM no gateway passado por todo o pipe line, e com a aba trace vamos ter informações detalhadas de cada etapa do processo.
Podemos usar a aba trace para entender o que aconteceu na chamada em cada etapa do fluxo do APIM
Frontend>>Inbound processing>>Backend>>Outbound processing>>Frontend. Nela podemos encontrar vários detalhes da requisição em cada uma das etapas do pipeline do APIM.
É muito útil para encontrar problemas.
Usando alguns headers, podemos obter as informações de trace no Postman, sem a necessidade de usar a aba test do APIM.
Ocp-Apim-Trace: true
Ocp-Apim-Subscription-Key: seu id de assinatura
O ID da assinatura pode ser obtido na aba Test caso você queira fazer um teste pelo Postman.
O resultado é esse Ocp-Apim-Trace-Location que pode ser acessado pelo browser.
Criando um Mock
Em diversas situações, ainda não temos um backend definido. Assim, podemos criar um Mock que simule esse backend, e darmos andamento nos testes das aplicações consumidoras do APIM.
Para isso, adicionamos a política de Mock ao processo de inbound do pipeline do APIM. Isso pode ser feito pela tela no botão "Add Policie" ou no código com o exemplo abaixo.
Algumas configurações precisam ser feitas pela tela clicando em "Editar" a primeira etapa do pipeline o frontend. Configuramos um retorno com código 200 e um Json mínimo para o Mock retornar. Observe o Código da política abaixo.
<policies>
<inbound>
<base />
<mock-response status-code="200" content-type="application/json" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Acessando uma chave do Key Vault em uma política
As Políticas nos dão grandes poderes podemos criar implementações muito elaboradas combinando políticas e expressões de código C#. Nesse cenário, é muito comum precisarmos gerenciar informações sensíveis ou mesmo informações que precisam ser centralizadas. Para isso temos o recurso de "named values" que tem integração direta com o Key Vault.
Observe abaixo que fazemos uso da variável assinatura na política set-header.
<policies>
<inbound>
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<set-header name="CustomHeader" exists-action="override">
<value>{{assinatura}}</value>
</set-header>
</outbound>
<on-error>
<base />
</on-error>
</policies>
Perceba que o código acima acessa a variável armazenada no named values ligada com key vault e a coloca em um header de saída chamado CustomHeader. Ou seja, podemos testar via Postman e ver o valor no header na resposta HTTP
Observe a imagem abaixo. Nela podemos ver a variável assinatura armazenada na ferramenta de named value. Porém, seu valor vem do Key Vault seedkeys.
Podemos adicionar novas variáveis e nesse momento associar com o Key Vault.
Acima, estamos selecionando uma chave do Key Vault seedKeys
Nota: para que as chaves possam ser listadas conforme a imagem acima é preciso configurar as permissões de acesso na sua instancia do Key Vault. Isso pode ser feito pelo menu "Access Police" da sua instancia do Key Vault e nele podemos configurar o nível de permissão para o APIM conforme abaixo.
Fazendo uma nova requisição pelo APIM
A ideia é fazer o APIM enviar um POST em nossa API, durante uma requisição de GET, para isso vamos usar uma política chamada send-request. Nesse exemplo vamos explorar um pouco mais as políticas básicas de entrada e saída, além de manipular as políticas com expressões C# obtendo informações do contexto da requisição.
Observe o código abaixo.
<policies>
<inbound>
<base />
<!-- Extract Token from Authorization header parameter -->
<set-variable name="token" value="@(context.Request.Headers.GetValueOrDefault("Authorization","scheme param").Split(' ').Last())" />
<send-request mode="new" response-variable-name="tokenResponse" timeout="20" ignore-error="true">
<set-url>https://sample-api-users.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>
<set-variable name="tokenGuid" value="@((string)((IResponse)context.Variables["tokenResponse"]).Body.As<JObject>()["tokenGuid"])" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<set-header name="custom-token" exists-action="override">
<value>@((string)context.Variables["tokenGuid"])</value>
</set-header>
</outbound>
<on-error>
<base />
</on-error>
</policies>
Essa Política obtém o token do objeto request.
<set-variable name="token" value="@(context.Request.Headers.GetValueOrDefault("Authorization","scheme param").Split(' ').Last())" />
Faz um post na API usando a política.
<send-request mode="new" response-variable-name="tokenResponse" timeout="20" ignore-error="true">
Armazena o response na variável tokenResponse e trata a variável tokenResponse convertendo para uma string e armazenando na variável tokenGuid.
<set-variable name="tokenGuid" value="@((string)((IResponse)context.Variables["tokenResponse"]).Body.As<JObject>()["tokenGuid"])" />
Por fim retorna em header customizado chamado custom-token.
<set-header name="custom-token" exists-action="override"> <value>@((string)context.Variables["tokenGuid"])</value> </set-header>
Dá para testar no postman também. Vamos ver o resultado mudando em cada requisição, pois a implementação gera um novo guid a cada solicitação.
Nota : Podemos perceber que é código C# usado para fazer as implementações o nome desse recurso é policy expressions, e podemos saber mais aqui policy expressions
jsonObject navegação
No backend
[HttpPost]
public IActionResult Post()
{return Ok(new
{
tokenGuid = Guid.NewGuid().ToString(),
attr = new {
value = "teste json object"
}
});}
No APIM
<set-variable name="tokenGuid" value="@((string)((IResponse)context.Variables["tokenResponse"]).Body.As<JObject>()["attr"]["value"])" />
Mudar o response retornando uma variável
<return-response>
<set-status code="200" reason="Success" />
<set-header name="custom-token" exists-action="override">
<value>@((string)context.Variables["tokenGuid"])</value>
</set-header>
<set-body>@((string)context.Variables["tokenGuid"])</set-body>
</return-response>
String teste json object veio da chamada feita pelo APIM com política send-request no endpoint https://sample-api-users.azurewebsites.net/introspection
Forward-request
Essa política é responsável por entregar as requisições para o backend.
Perceba que ela existe por padrão no escopo de todas as APIs.
<policies>
<inbound />
<backend>
<forward-request timeout="30" />
</backend>
<outbound />
<on-error />
</policies>
Essa política é responsável por muitas confusões. Quando à deletamos, o APIM ainda retorna status ok, mas não mais entrega a requisição para o backend. Para saber mais clique Aqui
Definir o serviço de back-end com política
A configuração do backend foi feita no momento da importação da API através da aba settings do ambiente de gerenciamento do APIM.
Podemos mudar isso utilizando a política set-backend-service.
<set-backend-service base-url="base URL of the backend service" />
Para usar Backends cadastrados no APIM use essa configuração.
<set-backend-service backend-id="WebApp_smartsecretary-api-v3" />
Os Backends podem ser cadastros no meu Backends. Os serviços de aplicação do Azure, presentes na mesma assinatura já ficam automaticamente disponíveis para o uso.
Depois podemos criar mecanismos de escolha que apontar para diferentes backends, de acordo com uma regra.
<choose>
<when condition="@(context.Request.Url.Query.GetValueOrDefault("version") == "v1")">
<set-backend-service base-url="http://contoso.com/api/8.2/" />
</when>
<when condition="@(context.Request.Url.Query.GetValueOrDefault("version") == "v2")">
<set-backend-service base-url="http://contoso.com/api/9.1/" />
</when>
</choose>
Podemos através do contexto da requisição definir um backend dinamicamente.
Url de Verificação
Essa url pode ser usada em ferramentas de verificação de aplicações essa chamada vai retornar um 200 quando o APIM estiver no rodando, é bastante útil pois não precisa ter nenhuma API rodando no APIM para podermos fazer esse teste.
Uma dica quando precisar configurar o Application gateway para manter o APIM em uma VNET privada vai perceber como esse endpoint é útil.
o path /status-0123456789abcdef é um endpoint de integridade padrão hospedado em todos os serviços de gerenciamento de API
Referencias
- How to set or edit Azure API Management policies | Microsoft Docs
- Azure API Management access restriction policies | Microsoft Docs
- Azure API Management advanced policies | Microsoft Docs
- Sample API management policy - Filter on IP Address when using Application Gateway - Azure API Management | Microsoft Docs
- Tutorial - Mock API responses in API Management - Azure portal | Microsoft Docs
- Adicionar uma API manualmente usando o portal do Azure | Microsoft Docs
- How to use named values in Azure API Management policies | Microsoft Docs
- Azure API Management advanced policies | Microsoft Docs
- Tutorial - Debug APIs in Azure API Management using request tracing | Microsoft Docs
- Azure API Management policy expressions | Microsoft Docs
- Azure API Management transformation policies | Microsoft Docs
- Add caching to improve performance in Azure API Management | Microsoft Docs
- IP addresses of Azure API Management service | Microsoft Docs
- Feature-based comparison of the Azure API Management tiers | Microsoft Docs