How to update Azure API Management named values in a batch? - azure-api-management

We have existing production services which are based on this policy sample which acquires a bearer token via client grant flow. However we want to switch the client application settings to a new app, which requires updating 4 named values. How can this be done with high throughput traffic volumes?
We have updated named value pairs as documented and we've looked at the REST API. There is only 1-by-1 update of values. We applied this on lower volume environments with no problems. However when we applied this on production environments we have outages with the expression unable to parse the value, which results in a null and caused an outage.
This was the policy, copied from the portal.
<choose>
<when condition="#(!context.Variables.ContainsKey("access_token"))">
<send-request ignore-error="true" timeout="20" response-variable-name="response" mode="new">
<set-url>{{authorizationServer}}</set-url>
<set-method id="apim-generated-policy">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={{clientid}}&resource={{scope}}&client_secret={{clientsecret}}&grant_type=client_credentials";}</set-body>
</send-request>
<set-variable name="responseJson" value="#(((IResponse)context.Variables["response"]).Body.As<JObject>())" />
<set-variable name="access_token" value="#("Bearer " + (String)((JObject)context.Variables["responseJson"])["access_token"])" />
<set-variable name="expires_in" value="#((int)((JObject)context.Variables["responseJson"])["expires_in"] - 20)" />
<cache-store-value key="access_token" value="#((string)context.Variables["access_token"])" duration="#(((int)context.Variables["expires_in"]))" />
</when>
</choose>
<set-header name="Authorization" exists-action="override">
<value>#((string)context.Variables["access_token"])</value>
</set-header>
"Elapsed": 109,
"Source": "set-variable[3]",
"Reason": null,
"Message": "Expression evaluation failed. Value cannot be null.\r\nParameter name: value\r\n at Newtonsoft.Json.Linq.JToken.EnsureValue(JToken value)\r\n at Newtonsoft.Json.Linq.JToken.op_Explicit(JToken value)",
"Scope": "product",
"Section": "inbound",
"Path": "choose\\when[1]",
"PolicyId": "",
"TransportErrorCode": 0,
"HttpErrorCode": 0
}
I suspected that the issue was related to one of these variables being null but our logs were unclear as to which variable.

There is no API to anatomically update multiple named value properties. There are two approaches that come to mind. You could store all your values in a single named value encoding them into a single string and parsing out inside policy.
Or you could have two sets of named values and a fifth one that controls which one to use. This way you'll be free to update your passive set freely and only activate it later.

Related

Validate request parameters in Azure APIM

I define a full-name API that takes in First Name and Last Name and combine them to return the Full Name:
https://my-org.azure-api.net/my-app/full-name?firstName=John&lastName=Smith
# Returns: John Smith
In Azure APIM, I set both firstName and lastName parameters as "Required" in the API Schema.
In the Inbound policy, I want to validate that both are present in the request. I found the validate-parameters policy which appears to do just that:
<inbound>
<validate-parameters specified-parameter-action="prevent"
unspecified-parameter-action="detect"
errors-variable-name="validationErrors" />
</inbound>
To my surprise, this does absolution nothing! I have to run a manual check on each param (this code is so verbose):
<inbound>
<choose>
<when condition="#( context.Request.OriginalUrl.Query.GetValueOrDefault("firstName") == null )">
<return-response>
<set-status code="400" reason="Bad Request" />
<set-header name="Context-Type" exists-action="override">
<value>text/plain</value>
</set-header>
<set-body>'firstName' is required</set-body>
</return-response>
</when>
<when condition="#( context.Request.OriginalUrl.Query.GetValueOrDefault("lastName") == null )">
<return-response>
<set-status code="400" reason="Bad Request" />
<set-header name="Context-Type" exists-action="override">
<value>text/plain</value>
</set-header>
<set-body>'lastName' is required</set-body>
</return-response>
</when>
<otherwise>
</otherwise>
</choose>
</inbound>
Is there any more convenient way to validate the request against the API's schema in Azure APIM?
We can find the document shows us we need to use api-version which later than 2021-01-01-preview to create the api in APIM, then the policy can work.
So if your api in APIM was created in past, you need to create it again. I checked the create api operation on azure portal, it has been updated to using api-version=2021-01-01-preview shown as below screenshot.
You can also request the api to create it by yourself.

Cannot get params over to rewrite url properly in Azure Api Management

I'm trying to make apim take rewrite...
https://apimpath/customers/1234
to
https://backendpath/api/customers/1234
It fails. Tried a few things but they all don't seem to recognize customerId. What am i missing?
------Update-------
Tried some of the suggestions below and it didn't help. Here is the new ui showing the same message
Tried adding it through the api with several variations of this...
PUT {{url}}/apis/customers/operations/GET?api-version=2016-7-7
{
"name": "GET",
"method": "GET",
"urlTemplate": "/{customerId}",
"templateParameters": [
{"name": "customerId", "type": "string"}
],
"policies": null
}
Which added the operation successfully...
PUT {{url}}/apis/customers/operations/GET/policy/?api-version=2016-7-7
<policies>
<inbound>
<base/>
<rewrite-uri template="/api/customers/{customerId}"/>
</inbound>
<outbound>
<base/>
</outbound>
</policies>
But the request above failed with this result...
{
"error": {
"code": "ValidationError",
"message": "One or more fields contain incorrect values:",
"details": [
{
"code": "ValidationError",
"target": "rewrite-uri",
"message": "Error in element 'rewrite-uri' on line 4, column 10: Only parameters specified in the original URL template can be used in the rewrite template. Original template is: /*"
}
]
}
}
This seems to be a bug. I can recreate this as well.
If you create the operation with a parameter, this is saved somewhere.
Changing it doesn't seem to work, because
The correct order is to create the operation directly with the right parameter name. Then add the backend rewrite rule.
If you take a look at the screenshot, the original operation you created was "/*" and not "/{customerId}"
I tested this in the new UI/Portal.
I created an operation "/{id}",
Added the rewrite "/api/{id}"
Tested it and it worked
renamed the operation to "/{customerId}"
encountered below error:
One or more fields contain incorrect values:
Error in element 'rewrite-uri' on line 16, column 10: Only parameters specified in the original URL template can be used in the rewrite template. Original template is: /{id}

API Managment unable to cast response body as string

In Azure API Management, when the response going back to the client is a 500, I wish to check the body of the response to see if it matches "Some text". I need to do this so that I may change the body of the response to contain some more helpful text in this particular scenario.
The following <outbound> section of my policy is accepted by the API Management console, but when I test and get a 500, API Management generates an error -
Expression evaluation failed. Unable to cast object of type 'Microsoft.WindowsAzure.ApiManagement.Proxy.Gateway.MessageBody' to type 'System.String'.
I'm guessing this is my fault, but does anybody know how I can amend the ploicy so that it does not generate an error? To clarify, the error is being generated by this line - ((string)(object)context.Response.Body == "Some text").
<outbound>
<choose>
<when condition="#((context.Response.StatusCode == 500) && ((string)(object)context.Response.Body == "Some text"))">
<set-status code="500" reason="Internal Server Error" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>
{
"statusCode": "500",
"Message": "Some different, more helpful text."
}
</set-body>
</when>
</choose>
</outbound>
Update
I've discovered that context.Response.Body is of type IMessageBody. There seems to be woefully little documentation around this type, and the only reference I can find comes under <set-body> in the Transformation Policies API management documentation.
The troube is, the example that MS havd documented produces an exception when I try and save my policy -
<set-body>
#{
JObject inBody = context.Request.Body.As<JObject>();
if (inBody.attribute == <tag>) {
inBody[0] = 'm';
}
return inBody.ToString();
}
</set-body>
Property or indexer 'string.this[int]' cannot be assigned to -- it is read only
Try context.Request.Body.As<string>(). Method As currently supports following types as generic argument value:
byte[]
string
JToken
JObject
JArray
XNode
XElement
XDocument
Mind that if you try to call .As<JObject> over response that does not contain valid JSON you would get an exception, same applies to other types as well.

use nlog to write to rest service

been at this for some time now,
i have a rest service that takes some parameter that i send via the query string. other then that i need to send via the request body some fields.
the format needs to be json.
here is a successful request made from postman for example:
{
"TransactionLogId": "6e6279a3-22d9-458d-b1c9-9b03a81556be",
"CreatedDate": "2015-08-17T15:05:50.0143866Z",
"LogType": "Info",
"TransactionCode": "2831b7bc-9fc8-424d-857a-182397a5eb11",
"ServiceName": "ESBService.WCF",
"ServiceId": "8664e362-f63d-4d10-8a23-3b86b9f22cc7",
"Servers": "******************",
"Context": "EmployersSite",
"RequestIPs": "**************",
"UserId": "258a8c83-3f18-40d6-aea7-986dc0d97656",
"ActivityTime": "2015-08-17T15:05:50.0143866Z",
"LogSubType": "Info.RequestBegin",
"Title": "RequestBegin",
"Details": "",
"RequestData": "{\r\n \"Filters\": {\r\n \"Mode\": \"Automatic\",\r\n \"Type\": \"Applicant\"\r\n }\r\n}",
"EntityClass": "SavedSearch",
"EntityId": "",
"Methods": "SavedSearch_Search; SetSession",
"SourceFilePath": "*******************\\PortalService.svc.cs",
"SourceLineNum": 1025,
"RunTime": 0.015
}
here is my config. lets say that all i want right now is just to send the exception message in details field.
here is my config:
<nlog throwExceptions="true" internalLogFile="c:\\Data\\Logs\\IES\\nlog_debug.txt" internalLogLevel="Warn">
<variable name="LogBaseFolder" value="c:\\Data\\Logs\\tester" />
<targets>
<target type='WebService'
name='ws'
url='somesvc.vc/TransactionLog/Create?Context=Portal&UserToken=a441b37f-3403-43fd-8f58-d1da3024133a'
protocol='HttpPost'
encoding='UTF-8'>
<parameter name='details' type='System.String' layout=" ${message}"/>
</target>
</targets>
<rules>
<logger name="*" writeTo="ws" />
</rules>
</nlog>
and my code:
Log.Site.Error("erorrrr")
but the request never makes it it get error that the body is not right:
message=Unexpected character encountered while parsing value: M. Path
'', line 0, position 0.
this is killing me could realy use some help. thanks
don't worry it's actually something newbies encounters a lot and there is a simple solution for that! – so REST your mind :)
So basically, when you pass the JSON back in the respond you need to Super-Serialize the object prior the response.Write(), do
response.super(Object name_Object)
What it does it handles "deeper" encapsulation processes ('DIYA Encapsulation') which includes nested objects (methods and members)
But without the core functions (constructor, destructor..)
Hope it helps, and good luck.
BTW, Recommend you to read more about classes, nest objects, what is a constructor etc.
It will help you further more as you develop as a developer! Cheers :)

WCF WebFaultException ExceptionDetail

I'm creating a WCF service that returns data in JSON Format. I'm trying to figure out how to best handle exceptions and I'm trying to use the WebFaultException class to return an exception detail message in the response, which can later be outputted to the user.
A simple Test of this method I am trying is as follows
The WCF Service method
<WebInvoke(Method:="POST",
ResponseFormat:=WebMessageFormat.Json)>
<OperationContract()>
Public Function Test() As Object
Throw New WebFaultException(Of String)("Message Details", Net.HttpStatusCode.NotFound)
End Function
From what I found searching for answers to this questions, you should give the service a behaviorconfiguartion which sets includeExceptionDetailInFaults to true.
My Web.Config
<service name="WebserviceExceptionTest.Service" behaviorConfiguration="behavior">
<endpoint address="" behaviorConfiguration="WebserviceExceptionTest.ServiceAspNetAjaxBehavior"
binding="webHttpBinding" contract="WebserviceExceptionTest.Service" />
</service>
<serviceBehaviors>
<behavior name="behavior">
<serviceDebug includeExceptionDetailInFaults="True"/>
</behavior>
Unfortunately, this appears to not do the trick for me and the response still does not include the exception detail, the JSON string looks like this:
{"ExceptionDetail":null,"ExceptionType":null,"Message":"Not Found","StackTrace":null}
Does anyone have any idea of what it is I am doing wrong, or am I just entirely on the wrong path? Thanks!
Edit
The Response I'm getting is always "500 Internal server error" Even though I'd expect it to get a 400 not found. The Error message does contain the "No Content" though.
Set your automaticFormatSelectionEnabled to be false, defaultOutgoingResponseFormat to Json (I believe it even ignores ResponseFormat:=WebMessageFormat.Json)
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="false" defaultOutgoingResponseFormat ="Json" />
http://msdn.microsoft.com/en-us/library/system.servicemodel.description.webhttpendpoint.automaticformatselectionenabled.aspx