DEV Community

Kenichiro Nakamura
Kenichiro Nakamura

Posted on

API Management : Customize throttling message

We can use rate-limit policies of Azure API Management to throttle API calls. See Advanced request throttling with Azure API Management for more detail.

In this article, I explain how we can customize the response message.

Default behavior

This is a sample policy which limits up to 5 calls per a minute.



<policies>
    <inbound>
        <rate-limit calls="5" renewal-period="60" />
        <base />
        <mock-response status-code="200" content-type="application/json" />
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
</policies>


Enter fullscreen mode Exit fullscreen mode

If we send 6th request in a minute, we can see the following message back from the service.

Image description

Change Response Header

rate-limit policy has many attributes to customize the behavior. See here for the list.

For example, we can add remaining-calls-header-name to let caller know how many more calls then can send before they get throttled. Following policy will add additional response header to include the number.



<rate-limit calls="5" renewal-period="60" remaining-calls-header-name="RemainingCalls"/>


Enter fullscreen mode Exit fullscreen mode

And this is the response header on my first call, which let me know I can do four more calls before throttled.

Image description

Following policy adds total calls number in response header.



<rate-limit calls="5" renewal-period="60" remaining-calls-header-name="RemainingCalls" total-calls-header-name="TotalCalls" />


Enter fullscreen mode Exit fullscreen mode

Image description

Customize the response

If we want to change the response message from default one, we can use on-error policy. We can use on-error policy to control error behavior, including 429 too many requests.

Simple example

This policy overrides behavior for RateLimitExceeded error. It still returns 429 status code, but message body is overwritten.



<policies>
    <inbound>
        <rate-limit calls="5" renewal-period="60" remaining-calls-header-name="RemainingCalls" total-calls-header-name="TotalCalls" />
        <base />
        <mock-response status-code="200" content-type="application/json" />
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <choose>
            <when condition="@(context.LastError.Reason == "RateLimitExceeded")">
                <return-response>
                    <set-status code="429" />
                    <set-header name="Content-Type" exists-action="override">
                        <value>application/json</value>
                    </set-header>
                    <set-body>{
                        "Don't call anymore"
                 }</set-body>
                </return-response>
            </when>
        </choose>
        <base />
    </on-error>
</policies>


Enter fullscreen mode Exit fullscreen mode

The response I see after 6th call is this.

Image description

Use LastError context

APIM provide error detail in context.LastError which we can use to enrich the message. Following example append error message to previous custom message.



<policies>
    <inbound>
        <rate-limit calls="5" renewal-period="60" remaining-calls-header-name="RemainingCalls" total-calls-header-name="TotalCalls" />
        <base />
        <mock-response status-code="200" content-type="application/json" />
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <choose>
            <when condition="@(context.LastError.Reason == "RateLimitExceeded")">
                <return-response>
                    <set-status code="429" />
                    <set-header name="Content-Type" exists-action="override">
                        <value>application/json</value>
                    </set-header>
                    <set-body>@{
                        return String.Format("Don't call anymore because you get {0}",
                        context.LastError.Message);
                    }</set-body>
                </return-response>
            </when>
        </choose>
        <base />
    </on-error>
</policies>


Enter fullscreen mode Exit fullscreen mode

The result looks like below.

Image description

Summary

I explain how we can use on-error to control error behavior. If you are interested in more detail about policy expression, see API Management policy expressions.

Top comments (0)