Azure API Management

Policy voorbeelden

Terug

Plaats het fragment op de inbound policy van de DEV operatie of API. APIM roept de resolver altijd met GET aan.

`testId` komt standaard uit de query string en valt terug op `default`.

Resolver `problem+json` responses worden direct teruggegeven; gemockte errors zitten in de normale envelope.

Client ID en secret

APIM haalt eerst via client credentials een Entra token op en roept daarna de mock resolver aan.

Named Values

  • mock-api-base-url
  • mock-api-tenant-id
  • mock-api-client-id
  • mock-api-client-secret
  • mock-api-scope

Prerequisites

  • Maak een confidential client app registration voor APIM.
  • Zet de client secret als secret named value, bij voorkeur Key Vault backed.
  • Wijs de backend app role Mock.Read toe aan de client app service principal.
  • Het access token wordt gecached tot vlak voor de JWT expiration time, per tenant, client id en scope.
<policies>
  <inbound>
    <base />
    <set-variable name="mockApiName" value="@(context.Api.Name)" />
    <set-variable name="mockOperationId" value="@(context.Operation.Id)" />
    <set-variable name="mockMethod" value="@((context.Request.Method ?? &quot;GET&quot;).ToUpperInvariant())" />
    <set-variable name="mockTestId" value="@((string)context.Request.Url.Query.GetValueOrDefault(&quot;testId&quot;, &quot;default&quot;))" />
    <set-variable name="mockTokenCacheKey" value="@(&quot;mock-resolver-token-{{mock-api-tenant-id}}-{{mock-api-client-id}}-{{mock-api-scope}}&quot;)" />

    <cache-lookup-value key="@((string)context.Variables[&quot;mockTokenCacheKey&quot;])" variable-name="mockAccessToken" />
    <rate-limit-by-key calls="20" renewal-period="60" counter-key="@((string)context.Variables[&quot;mockTokenCacheKey&quot;])" />

    <choose>
      <when condition="@(!context.Variables.ContainsKey(&quot;mockAccessToken&quot;))">
        <send-request mode="new" response-variable-name="mockTokenResponse" timeout="10" ignore-error="false">
          <set-url>https://login.microsoftonline.com/{{mock-api-tenant-id}}/oauth2/v2.0/token</set-url>
          <set-method>POST</set-method>
          <set-header name="Content-Type" exists-action="override">
            <value>application/x-www-form-urlencoded</value>
          </set-header>
          <set-body>@{
            return "client_id=" + Uri.EscapeDataString("{{mock-api-client-id}}")
              + "&amp;client_secret=" + Uri.EscapeDataString("{{mock-api-client-secret}}")
              + "&amp;scope=" + Uri.EscapeDataString("{{mock-api-scope}}")
              + "&amp;grant_type=client_credentials";
          }</set-body>
        </send-request>
        <set-variable name="mockAccessToken" value="@(((IResponse)context.Variables[&quot;mockTokenResponse&quot;]).Body.As&lt;JObject&gt;()[&quot;access_token&quot;].ToString())" />
        <set-variable name="mockTokenCacheDuration" value="@{
          var jwt = ((string)context.Variables["mockAccessToken"]).AsJwt();
          if (jwt == null || !jwt.ExpirationTime.HasValue)
          {
            return 300;
          }

          var secondsUntilExpiry = (int)Math.Floor((jwt.ExpirationTime.Value.ToUniversalTime() - DateTime.UtcNow).TotalSeconds);
          return Math.Max(60, Math.Min(secondsUntilExpiry - 120, 3300));
        }" />
        <cache-store-value key="@((string)context.Variables[&quot;mockTokenCacheKey&quot;])" value="@((string)context.Variables[&quot;mockAccessToken&quot;])" duration="@((int)context.Variables[&quot;mockTokenCacheDuration&quot;])" />
      </when>
    </choose>

    <send-request mode="new" response-variable-name="mockResolverResponse" timeout="20" ignore-error="true">
      <set-url>@{
        var apiName = Uri.EscapeDataString((string)context.Variables["mockApiName"]);
        var operationId = Uri.EscapeDataString((string)context.Variables["mockOperationId"]);
        var method = Uri.EscapeDataString((string)context.Variables["mockMethod"]);
        var testId = Uri.EscapeDataString((string)context.Variables["mockTestId"]);
        return $"{{mock-api-base-url}}/api/mock-responses/{apiName}/{operationId}/{method}/{testId}";
      }</set-url>
      <set-method>GET</set-method>
      <set-header name="Authorization" exists-action="override">
        <value>@("Bearer " + (string)context.Variables["mockAccessToken"])</value>
      </set-header>
      <set-header name="Accept" exists-action="override">
        <value>application/json, application/problem+json</value>
      </set-header>
    </send-request>

    <choose>
      <when condition="@(((IResponse)context.Variables[&quot;mockResolverResponse&quot;]).StatusCode &gt;= 400)">
        <return-response response-variable-name="mockResolverResponse" />
      </when>
      <otherwise>
        <set-variable name="mockEnvelope" value="@(((IResponse)context.Variables[&quot;mockResolverResponse&quot;]).Body.As&lt;JObject&gt;())" />
        <return-response>
          <set-status code="@((int)((JObject)context.Variables[&quot;mockEnvelope&quot;])[&quot;response&quot;][&quot;statusCode&quot;])" />
          <set-header name="Content-Type" exists-action="override">
            <value>@((string)((JObject)context.Variables["mockEnvelope"])["response"]["contentType"])</value>
          </set-header>
          <set-header name="x-mock-source" exists-action="override">
            <value>@((string)((JObject)context.Variables["mockEnvelope"])["blobPath"])</value>
          </set-header>
          <set-header name="x-mock-default" exists-action="override">
            <value>@(((bool)((JObject)context.Variables["mockEnvelope"])["isDefault"]).ToString().ToLowerInvariant())</value>
          </set-header>
          <set-body>@{
            var envelope = (JObject)context.Variables["mockEnvelope"];
            var value = (string)envelope["response"]["payload"]["value"];
            return System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(value));
          }</set-body>
        </return-response>
      </otherwise>
    </choose>
  </inbound>
</policies>

APIM managed identity

APIM gebruikt de eigen managed identity om een token voor de backend API te krijgen.

Named Values

  • mock-api-base-url
  • mock-api-resource

Prerequisites

  • Enable system-assigned of user-assigned managed identity op APIM.
  • Wijs de backend app role Mock.Read toe aan de APIM managed identity service principal.
  • Gebruik bij user-assigned identity ook client-id op authentication-managed-identity.
<policies>
  <inbound>
    <base />
    <set-variable name="mockApiName" value="@(context.Api.Name)" />
    <set-variable name="mockOperationId" value="@(context.Operation.Id)" />
    <set-variable name="mockMethod" value="@((context.Request.Method ?? &quot;GET&quot;).ToUpperInvariant())" />
    <set-variable name="mockTestId" value="@((string)context.Request.Url.Query.GetValueOrDefault(&quot;testId&quot;, &quot;default&quot;))" />

    <send-request mode="new" response-variable-name="mockResolverResponse" timeout="20" ignore-error="true">
      <set-url>@{
        var apiName = Uri.EscapeDataString((string)context.Variables["mockApiName"]);
        var operationId = Uri.EscapeDataString((string)context.Variables["mockOperationId"]);
        var method = Uri.EscapeDataString((string)context.Variables["mockMethod"]);
        var testId = Uri.EscapeDataString((string)context.Variables["mockTestId"]);
        return $"{{mock-api-base-url}}/api/mock-responses/{apiName}/{operationId}/{method}/{testId}";
      }</set-url>
      <set-method>GET</set-method>
      <authentication-managed-identity resource="{{mock-api-resource}}" />
      <set-header name="Accept" exists-action="override">
        <value>application/json, application/problem+json</value>
      </set-header>
    </send-request>

    <choose>
      <when condition="@(((IResponse)context.Variables[&quot;mockResolverResponse&quot;]).StatusCode &gt;= 400)">
        <return-response response-variable-name="mockResolverResponse" />
      </when>
      <otherwise>
        <set-variable name="mockEnvelope" value="@(((IResponse)context.Variables[&quot;mockResolverResponse&quot;]).Body.As&lt;JObject&gt;())" />
        <return-response>
          <set-status code="@((int)((JObject)context.Variables[&quot;mockEnvelope&quot;])[&quot;response&quot;][&quot;statusCode&quot;])" />
          <set-header name="Content-Type" exists-action="override">
            <value>@((string)((JObject)context.Variables["mockEnvelope"])["response"]["contentType"])</value>
          </set-header>
          <set-header name="x-mock-source" exists-action="override">
            <value>@((string)((JObject)context.Variables["mockEnvelope"])["blobPath"])</value>
          </set-header>
          <set-header name="x-mock-default" exists-action="override">
            <value>@(((bool)((JObject)context.Variables["mockEnvelope"])["isDefault"]).ToString().ToLowerInvariant())</value>
          </set-header>
          <set-body>@{
            var envelope = (JObject)context.Variables["mockEnvelope"];
            var value = (string)envelope["response"]["payload"]["value"];
            return System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(value));
          }</set-body>
        </return-response>
      </otherwise>
    </choose>
  </inbound>
</policies>