How Do I Configure API Management With Multiple Back Ends? - azure

I am hosting a service for clients where we have the same application deployed per client in Azure.
It is essentially this per client:
Application Gateway ->
App Service Environment ->
API Management ->
VM with Application back end ->
Remote Azure SQl Data
The first 3 layers increase the costs quite significantly.
Is it possible to configure it like this?
Application Gateway ->
App Service Environment ->
API Management ->
- Client 1
VM1 with Application back end ->
Remote Azure SQl Data1
- Client 2
VM2 with Application back end ->
Remote Azure SQl Data2
- Client 3
VM3 with Application back end ->
Remote Azure SQl Data3
i.e. the web layers route to the appropriate back end somehow
e.g. Maybe each client would access the web layer with a different URL.
http://client1.rest-application.azure.com
http://client2.rest-application.azure.com
but they all go through the same application gateway.

You need to use APIM policy to route the traffic to different backend pool based on the HTTP header information.
Here is the sample policy which routes the traffic based on the request body size.
The policy defined in this file demonstrates how to route requests based on the size of the message body.
Content-Length header contains the size of the message body.
256 KB, a limitation on message size in the Azure Service Bus.
he snippet checks if the message is smaller than 256000 bytes. If it's larger, request is routed somewhere else.
Copy the following snippet into the inbound section.
<policies>
<inbound>
<base/>
<set-variable name="bodySize" value="#(context.Request.Headers["Content-Length"][0])"/>
<choose>
<when condition="#(int.Parse(context.Variables.GetValueOrDefault<string>("bodySize"))<256000)">
<!-- let it pass through by doing nothing -->
</when>
<otherwise>
<rewrite-uri template="{{alternate-path-and-query}}"/>
<set-backend-service base-url="{{alternate-host}}"/>
</otherwise>
</choose>
<!-- In rare cases where Content-Length header is not present we'll have to read the body to get its length. -->
<!--
<choose>
<when condition="#(context.Request.Body.As<string>(preserveContent: true).Length<256000)">
</when>
<otherwise>
<rewrite-uri template=""/>
<set-backend-service base-url=""/>
</otherwise>
</choose>
-->
</inbound>
<backend>
<base/>
</backend>
<outbound>
<base/>
</outbound>
<on-error>
<base/>
</on-error>

Related

Why don't I see any results when I run my test on Azure API Management

I signed up for an API that retrieves a zip file and downloads it. For this, I received a bearer token.
I have created an apim instance and was looking to test the API I had subscribed to which downloads the file.
When I tested it, the apim instance kept loading and got stuck.
I tested this on Postman and it works perfectly. I got the "200 OK response", but still got weird characters like "�vt�" in my body, but that's probably because I'm retrieving a zip file. But why doesn't it work also within Azure's apim? Are there any extra policies I need to add?
Kind regards
You got the "200 OK response", but still facing the issue, you can try to troubleshoot by following approach:
1. If Backend section is taking most of the time (approx. 5 seconds), which means there is some slowness or long running operation is taking place at the backend.
Once you have isolated that the slowness is at the backend, you need to investigate the backend application code of the Web API application. For scenarios where you don't have access to the backend, you can implement caching at APIM level like below. Read about how you can implement caching policies to improve performance in Azure API Management.
<?xml version="1.0" encoding="UTF-8"?>
<policies>
<inbound>
<base />
<cache-lookup vary-by-developer="true" vary-by-developer-groups="true" must-revalidate="true" downstream-caching-type="public" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<cache-store duration="60" />
</outbound>
<on-error>
<base />
</on-error>
</policies>
You can refer to REST API service when called from azure APIM returning empty response body with Status code 200,
Azure API Management returning 200 status without response body, gzip Compression, and Azure API management gives 200 response code,

Can I call different backend on Azure API Management

I am trying to achieve that, when I call an APIM Endpoint based on the request headers It should different endpoint. for e.g when user call https://test.azure-api.net/testsvc-dev/api/test APIM should be able to send the request to https://testappv1:80/test or https://testappv2:80. right now I can see in the serviceURL, I can add only one. Is there any policy that I can use to do these kind of operations.
little more context: I am trying to access two different version of API on a single call. Instead of caller choosing which one to call,I am trying to make the APIM to decide based on the user.
You are probably looking for the set-backend-service policy.
Here an example from the Microsoft docs that changes the backend service based on a query parameter:
<policies>
<inbound>
<choose>
<when condition="#(context.Request.Url.Query.GetValueOrDefault("version") == "2013-05")">
<set-backend-service base-url="http://contoso.com/api/8.2/" />
</when>
<when condition="#(context.Request.Url.Query.GetValueOrDefault("version") == "2014-03")">
<set-backend-service base-url="http://contoso.com/api/9.1/" />
</when>
</choose>
<base />
</inbound>
<outbound>
<base />
</outbound>
</policies>
Certainly, you could adopt the sample and query for the desired headers ;-)

Azure API management - custom domain specific APIs?

I could successfully add the custom domain to the Azure API management and access the APIs.
I do understand that Azure API management supports multiple custom domains.
However all the APIs are accessible across the custom domains.
Is there any way to limit the APIs per custom domain? Eg: API1 will be available only for the domainX while API2 is accessible for domainY.
You could use an API level or Product level policy to return an Unauthorized status code when the Uri of the incoming call is on the wrong domain:
<policies>
<inbound>
<base />
<choose>
<when condition="#(context.Request.OriginalUrl.Host != "domainX")">
<return-response>
<set-status code="401" reason="Unauthorized"/>
</return-response>
</when>
</choose>
</inbound>
</policies>

How to setup 405 Method Not Allowed for each of the methods in APIs using azure API Management

In azure API Management how to setup the 405 (Method not allowed) policy. I am using azure API management APIs and adding different policies like jwt validation, IP filtering, rate limit and all. But I couldn't find a way for add the 405 method not allowed in APIM. I want to setup this for each of the methods. That means I want to block the incoming unrecognized method requests from APIM. (eg: Get instead of POST (Throws 405 method not allowed from APIM). Currently APIM passes the wrong method to backend and it returns the 404 from the application. Anyone know how we can block the wrong request from APIM side and returns 405 instead of passing it to backend and returns 404?.
You could use a Control Flow policy along with the Context Variable on the Inbound policy of each Method to intercept any requests that don't match the defined http method and then use a Set Status policy to return a 405. So for a GET method something along the lines of:
<policies>
<inbound>
<choose>
<when condition="#(context.Request.Method.ToString() != "GET")">
<return-response>
<set-status code="405" reason="No Content" />
</return-response>
</when>
</choose>
<base />
</inbound>
... rest of policies
</policies>
If you've got multiple methods with the same path you might need to apply this at the API level rather than the Method level and make the condition equals methods not in use rather than not equal to method in use
To set this at the API level and check against a collection of methods not in use create a policy along the lines of:
<policies>
<inbound>
<choose>
<when condition="#{
ICollection<string> disallowedMethods = new List<string>() { "POST", "PUT" };
return disallowedMethods.Contains(context.Request.Method.ToString());
}">
<return-response>
<set-status code="405" reason="No Content" />
</return-response>
</when>
</choose>
<base />
</inbound>
... rest of policies
</policies>
The http methods not in use in this example are POST and PUT but you can change the list to whatever applies in your use case.

How can you use Azure API Management policy to load balance calls to a backend service?

How can you use policy to load balance calls to a pair of backend services? (in this case a pair of Logic Apps in different regions)
I've read through the API Management policies and can see something around the control flow but I can't work out how to (a) test the back-end service is available, and then (b) change the call to the backup service if the primary is unavailable
<backend>
<forward-request/>
</backend>
One more way to achieve this could be that you can use the retry policy with set backend service or send-request
with something like
<backend>
<retry condition="#(context.Response.StatusCode == 400 || context.Response.StatusCode >= 500)" count="10" interval="10" max-interval="100" delta="10" first-fast-retry="false">
<choose>
<when condition="#(context.Response != null && (context.Response.StatusCode == 400 || context.Response.StatusCode >= 500)">
<set-backend-service base-url="http://echoapibackup.cloudapp.net/api" />
</when>
<otherwise>
<set-backend-service base-url="http://echoapi.cloudapp.net/api" />
</otherwise>
</choose>
<forward-request />
</retry>
</backend>
This will in case your primary backend returns an error, will keep retrying on your backup backend.
Look into using send-request policy. With it (and wait policy) you could make parallel calls to a couple of web services and return result from one that completes first. That would mean that you need to skip forward-request altogether as you'll be getting result data from these policies.
Or you could use send-request to test if certain backend is available and then use set-backend-service and/or rewrite-uri policies to change destination backend. In that case you'll be keeping forward-request.

Resources