Adding an existing service with OpenAPI v3 API document to the gateway
- Cloud service (control / data plane)
- On-premise service (data plane)
Import an OpenAPI specification
Use tinyurl.openapi.json
API Url suffix: tinyurl
Products: Unlimited
Security: OAuth 2
<policies>
<inbound>
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
<openid-config url="https://login.microsoftonline.com/your-tenant-guid/.well-known/openid-configuration" />
<required-claims>
<claim name="aud">
<value>00000002-0000-0000-c000-000000000000</value>
</claim>
</required-claims>
</validate-jwt>
<set-query-parameter name="api_token" exists-action="override">
<value>{{TinyUrlKey}}</value>
</set-query-parameter>
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
curl 'https://rfqapiservicey27itqpt4cf7q.azure-api.net/tinyurl/create' -H 'Ocp-Apim-Subscription-Key: your-key' -H 'Ocp-Apim-Trace: true' -H 'Content-Type: application/json' --data "@tinyurl.request.json" -H "Authorization: Bearer eyJ0e...."
ResponseBody:
{"data":{"domain":"tinyurl.com","alias":"y926fh2b","deleted":false,"archived":false,"analytics":{"enabled":true,"public":false},"tags":[],"created_at":"2022-04-25T08:27:11.000000Z","expires_at":null,"tiny_url":"https:\/\/tinyurl.com\/y926fh2b","url":"https:\/\/www.example.com\/my-really-long-link-that-I-need-to-shorten\/84378949"},"code":0,"errors":[]}
Use tinyurl.openapi.json
Display name: TinyURL OpenApi - Gateway
API Url suffix: tinyurl-gateway
Web service URL: http://192.0.0.0:9099
-> Url of On-Premise system
Products: Unlimited
Gateway: EvaluationGateway
<policies>
<inbound>
<base />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
<openid-config url="https://login.microsoftonline.com/your-tenant-guid/.well-known/openid-configuration" />
</validate-jwt>
<rewrite-uri template="/tinyurl.json" copy-unmatched-params="true" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
curl "Content-Type: application/json" -H "Ocp-Apim-Subscription-Key:your-key" -H "Ocp-Apim-Trace:true" --data "@tinyurl.request.json" --insecure https://localhost:443/tinyurl-gateway/create -H "Authorization: Bearer eyJ0e...."
ResponseBody:
{
"data": {
"domain": "tinyurl.com",
"alias": "2s3xj48u",
"deleted": false,
"archived": false,
"analytics": {
"enabled": true,
"public": false
},
"tags": [],
"created_at": "2022-04-25T09:38:15.000000Z",
"expires_at": null,
"tiny_url": "https://tinyurl.com/2s3xj48u",
"url": "https://www.planb.net"
},
"code": 0,
"errors": []
}
Adding an existing SOAP service with WSDL
- On-premise service (data plane)
Adding an existing SOAP service with WSDL.
Import SOAP API to API Management
The free online SOAP Number Conversion Service will be used for demonstration purpose.
Number Conversion Service - WSDL
Web service URL: http://192.0.0.0:9099
-> Url of On-Premise system
Products: Unlimited
Gateway: EvaluationGateway
API URL suffix: numbersoap-gateway
<policies>
<inbound>
<base />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
<openid-config url="https://login.microsoftonline.com/your-tenant-guid/.well-known/openid-configuration" />
</validate-jwt>
<rewrite-uri template="/numbers.xml" copy-unmatched-params="true" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Test original webservice:
curl -X POST -H "Content-Type: text/xml" -H 'SOAPAction: "NumberToWords"' --data-binary "@numberrequest.xml" https://www.dataaccess.com/webservicesserver/NumberConversion.wso
Test on-premise webservice:
curl -X POST -H "Content-Type: text/xml" -H 'SOAPAction: "NumberToWords"' -H "Ocp-Apim-Subscription-Key:your-key" -H "Ocp-Apim-Trace:true" --data-binary "@numberrequest.xml" --insecure https://localhost:443/numbersoap-gateway -H "Authorization: Bearer eyJ0e...."
ResponseBody:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<m:NumberToWordsResponse xmlns:m="http://www.dataaccess.com/webservicesserver/">
<m:NumberToWordsResult>forty two </m:NumberToWordsResult>
</m:NumberToWordsResponse>
</soap:Body>
</soap:Envelope>
Adding an existing SOAP service with WSDL.
Import SOAP API to API Management and convert to REST
The free online SOAP Number Conversion Service will be used for demonstration purpose.
Number Conversion Service - WSDL
Web service URL: http://192.0.0.0:9099
-> Url of On-Premise system
Products: Unlimited
Gateway: EvaluationGateway
API URL suffix: numberrest-gateway
<inbound>
<base />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
<openid-config url="https://login.microsoftonline.com/your-tenant-guid/.well-known/openid-configuration" />
</validate-jwt>
<rewrite-uri template="/numbers.xml" copy-unmatched-params="true" />
<!--
<rewrite-uri template="/webservicesserver/NumberConversion.wso" copy-unmatched-params="false" />
-->
Test on-premise webservice:
curl "Content-Type: application/json" -H "Ocp-Apim-Subscription-Key:your-key" -H "Ocp-Apim-Trace:true" --data "@numberrequest.json" --insecure https://localhost:443/numberrest-gateway/NumberToWords -H "Authorization: Bearer eyJ0e...."
ResponseBody:
{
"numberToWordsResponse":
{
"numberToWordsResult": "forty two "
}
}
- Create mocked endpoints with sample data
Operation /evaluation/hello
Mock policy returns example from OpenAPI:
policy:
<inbound>
<base />
<mock-response status-code="200" content-type="application/json" />
</inbound>
/hello:
get:
summary: hello
description: hello
operationId: hello
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/HelloGet200ApplicationJsonResponse'
example:
hello: world
components:
schemas:
HelloGet200ApplicationJsonResponse:
type: object
properties:
hello:
type: string
example: world
curl -X GET https://rfqapiservicey27itqpt4cf7q.azure-api.net/evaluation/hello -H "Ocp-Apim-Subscription-Key:your-key" -H "Ocp-Apim-Trace:true"
ResponseBody:
{
"hello": "world"
}
- Add roles/groups
- Integrate Azure AD
- Protect uses cases 1-4
Roles in API Management:
How to use Role-Based Access Control in Azure API Management
- API Management Service Contributor
- API Management Service Reader
- API Management Service Operator
Groups in API Management Developer Portal:
How to create and use groups to manage developer accounts in Azure API Management
- Administrators
- Developers
- Guests
Authorize developer accounts by using Azure Active Directory in Azure API Management
Execute corrected script for API permissions:
appReg.ps1
-> Publish Developer Portal
How to authorize developer accounts using OAuth 2.0 in Azure API Management
Register another application in Azure AD to represent a client application
However, what if someone calls your API without a token or with an invalid token? For example, if you call the API without the Authorization header, the call will still go through, since API Management does not validate the access token. It simply passes the Authorization header to the back-end API. Pre-authorize requests in API Management with the Validate JWT policy, by validating the access tokens of each incoming request. If a request does not have a valid token, API Management blocks it.
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
<openid-config url="https://login.microsoftonline.com/your-tenant-guid/.well-known/openid-configuration" />
</validate-jwt>
-> Publish Developer Portal
- Demo (capabilities)
- Transformation
- Group/Join multiple endpoints (from different services) to single API / OpenAPI 3 document for the consuming application/client
Overview of the developer portal
Developer Portal does not support transformation.
But in API Management APIs can use transformation policies:
API Management transformation policies
Developer Portal does not support grouping or joining multiple endpoints.
But in API Management APIs can combine results from different requests:
Policy - SendRequest
Policy - ReturnResponse
<policies>
<inbound>
<base />
<send-request mode="new" response-variable-name="helloResponse" timeout="120" ignore-error="false">
<set-url>@{
return context.Request.Url.ToString().Replace("hello-time", "hello");
}</set-url>
<set-method>GET</set-method>
<set-header name="Ocp-Apim-Subscription-Key" exists-action="override">
<value>{{EvaluationApiKey}}</value>
</set-header>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-header name="Ocp-Apim-Trace" exists-action="override">
<value>true</value>
</set-header>
</send-request>
<rewrite-uri template="/time" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<choose>
<when condition="@(((IResponse)context.Variables["helloResponse"]).StatusCode == 200 && context.Response.StatusCode == 200)">
<set-body>@{
var body = context.Response.Body.As<JObject>(true);
var helloWorldBody = ((IResponse)context.Variables["helloResponse"]).Body.As<JObject>();
body["hello"] = helloWorldBody["hello"];
return body.ToString();
}</set-body>
</when>
<otherwise>
<return-response>
<set-status code="400" reason="Bad request" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>@{
JObject response = new JObject();
response.Add("message", "error");
return response.ToString();
}</set-body>
</return-response>
</otherwise>
</choose>
</outbound>
<on-error>
<base />
</on-error>
</policies>
curl -X GET https://rfqapiservicey27itqpt4cf7q.azure-api.net/evaluation/hello-time -H "Ocp-Apim-Subscription-Key:your-key" -H "Ocp-Apim-Trace:true"
ResponseBody:
{
"time": "2022-04-26T12:22:32.8098341+00:00",
"hello": "world"
}
- Caching policies
API Management caching policies
<policies>
<inbound>
<base />
<return-response>
<set-status code="200" reason="OK" />
<set-body>@{
var message = new JObject();
message["time"] = DateTime.Now;
return message.ToString();
}</set-body>
</return-response>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
<policies>
<inbound>
<base />
<cache-lookup vary-by-developer="false" vary-by-developer-groups="false" must-revalidate="true" downstream-caching-type="public">
<vary-by-header>header</vary-by-header>
<vary-by-query-parameter>query</vary-by-query-parameter>
</cache-lookup>
<rewrite-uri template="/time" />
</inbound>
<backend>
<forward-request timeout="10" />
</backend>
<outbound>
<base />
<cache-store duration="10" cache-response="true" />
</outbound>
<on-error>
<base />
</on-error>
</policies>
curl -X GET https://rfqapiservicey27itqpt4cf7q.azure-api.net/evaluation/time-cached -H "Ocp-Apim-Subscription-Key:your-key" -H "Ocp-Apim-Trace:true"
ResponseBody:
{
"time": "2022-04-26T12:32:07.0748795+00:00"
}
{
"time": "2022-04-26T12:32:07.0748795+00:00"
}
{
"time": "2022-04-26T12:32:07.0748795+00:00"
}
- Create new endpoint in the API Gateway to collect data from multiple single services and merge them.
<inbound>
<base />
<mock-response status-code="200" content-type="application/json" />
</inbound>
Returns:
{
"hello": "world"
}
<policies>
<inbound>
<base />
<return-response>
<set-status code="200" reason="OK" />
<set-body>@{
var message = new JObject();
message["time"] = DateTime.Now;
return message.ToString();
}</set-body>
</return-response>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Returns:
{
"time": "2022-04-26T12:32:07.0748795+00:00"
}
- Requests /hello
- Forwards requests /time
- Merge responses in outbound section
<policies>
<inbound>
<base />
<send-request mode="new" response-variable-name="helloResponse" timeout="120" ignore-error="false">
<set-url>@{
return context.Request.Url.ToString().Replace("hello-time", "hello");
}</set-url>
<set-method>GET</set-method>
<set-header name="Ocp-Apim-Subscription-Key" exists-action="override">
<value>{{EvaluationApiKey}}</value>
</set-header>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-header name="Ocp-Apim-Trace" exists-action="override">
<value>true</value>
</set-header>
</send-request>
<rewrite-uri template="/time" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<choose>
<when condition="@(((IResponse)context.Variables["helloResponse"]).StatusCode == 200 && context.Response.StatusCode == 200)">
<set-body>@{
var body = context.Response.Body.As<JObject>(true);
var helloWorldBody = ((IResponse)context.Variables["helloResponse"]).Body.As<JObject>();
body["hello"] = helloWorldBody["hello"];
return body.ToString();
}</set-body>
</when>
<otherwise>
<return-response>
<set-status code="400" reason="Bad request" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>@{
JObject response = new JObject();
response.Add("message", "error");
return response.ToString();
}</set-body>
</return-response>
</otherwise>
</choose>
</outbound>
<on-error>
<base />
</on-error>
</policies>
curl -X GET https://rfqapiservicey27itqpt4cf7q.azure-api.net/evaluation/hello-time -H "Ocp-Apim-Subscription-Key:your-key" -H "Ocp-Apim-Trace:true"
ResponseBody:
{
"time": "2022-04-26T12:42:51.1558111+00:00",
"hello": "world"
}
- How to update the API Gateway (Version/security/patch/upgrade) for control & data plane
Patch and upgrade AKS worker nodes
AKS supports upgrading node images by using az aks nodepool upgrade, so you can keep up with the newest OS and runtime updates. To keep your agent node OS and runtime components patched, consider checking and applying node image upgrades bi-weekly, or automating the node image upgrade process. For more information about automating node image upgrades, see Node upgrade GitHub Actions.
Deploy an Azure API Management gateway on Azure Arc (preview)
API Gateway is available as image on Docker Hub.
docker pull mcr.microsoft.com/azure-api-management/gateway:latest
Similar to the nodes, container images should also be rebuilt and deployed every two weeks.
This can be automated in a scheduled CI/CD process.
List of Self-Hosted Gateway versions
API Management - Release notes
Release - self-hosted gateway: 1.2.5
Azure API Management Self-Hosted Gateway