skip to Main Content

I want to rate lime based on the mailTo attribute from the request body.

Here is the APIM policy

<rate-limit-by-key calls="5" 
    renewal-period="10" 
    counter-key="@(context.Request.Body.As<JObject>()["mailTo"].ToString())" />

Here is the request body

{
  "mailTo": "[email protected]"
}

This work fine for direct call to backend, but getting below error while calling to APIM

{
    "errors": {
        "": [
            "A non-empty request body is required."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "00-......-3be881755d918044-00"
}`
<rate-limit-by-key calls="5" 
    renewal-period="10" 
    counter-key="@(context.Request.Body.As<JObject>()["mailTo"].ToString())" />

2

Answers


  1. I have used the same policy in below format and it worked as expected

    Policy

    <policies>
    <inbound>
    <base  />
    <rate-limit-by-key  calls="5"  renewal-period="10"  counter-key="@(context.Request.Body.As<JObject>()["mailTo"].ToString())"  />
    </inbound>
    <backend>
    <base  />
    </backend>
    <outbound>
    <base  />
    </outbound>
    <on-error>
    <base  />
    </on-error>
    </policies>
    

    Test Result

    enter image description here

    enter image description here

    Trace

    enter image description here

    Login or Signup to reply.
  2. I guess there’s much more logic in the policy.

    This simple policy does not produce the mentioned error:

    <policies>
        <inbound>
            <base />
            <rate-limit-by-key calls="5" renewal-period="10" counter-key="@(context.Request.Body.As<JObject>()["mailTo"].ToString())" />
            <return-response>
                <set-status code="200" reason="Ok" />
                <set-body>{    
        "message": "It's fine!"           
    }</set-body>
            </return-response>
        </inbound>
        <backend>
            <base />
        </backend>
        <outbound>
            <base />
        </outbound>
        <on-error>
            <base />
        </on-error>
    </policies>
    

    enter image description here

    enter image description here

    But I guess that the request body is used at least twice in the policy.
    Therefore you have to set the parameter preserveContent to true:

    context.Request.Body.As<JObject>(true)["mailTo"].ToString()

    https://learn.microsoft.com/en-us/azure/api-management/api-management-policy-expressions

    To avoid that and have the method operate on a copy of the body stream, set the preserveContent parameter to true

    Policy which uses the request twice:

    <policies>
        <inbound>
            <base />
            <rate-limit-by-key calls="5" renewal-period="10" counter-key="@(context.Request.Body.As<JObject>(true)["mailTo"].ToString())" />
            <set-variable name="email" value="@(context.Request.Body.As<JObject>(true)["mailTo"]?.ToString().ToLower())" />
            <return-response>
                <set-body template="none">@{
                    var jsonResponse = new JObject(); 
                    jsonResponse.Add(new JProperty("message", (string)context.Variables["email"])); 
                    return jsonResponse.ToString(); 
                }</set-body>
            </return-response>
        </inbound>
        <backend>
            <base />
        </backend>
        <outbound>
            <base />
        </outbound>
        <on-error>
            <base />
        </on-error>
    </policies>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search