Accessing context.Request.Body in APIM policy removes request body - azure-api-management

I'm trying to extract a specific attribute from the JSON body and add that as a header value. This should be straight forward as such:
<set-header name="x-bp-currency" exists-action="override" >
<value>#((string)context.Request.Body.As<JObject>()["currency"])</value>
</set-header>
But, I see that when I do this, the request-body gets removed before it is forwarded to the backend URL. I've managed to narrow it down that the context.Request.Body is causing the issue. If we add a hardcoded value, then the request-body is still sent to the backend.
Example:
This keeps the original request-body and forwards to the backend:
<inbound>
<base />
<set-header name="x-bp-currency" exists-action="override">
<value>test</value>
</set-header>
<set-backend-service base-url="https://webhook.site/xxxxx" />
</inbound>
This removes the request-body (content-length: 0).
<inbound>
<base />
<set-header name="x-bp-currency" exists-action="override" >
<value>#((string)context.Request.Body.As<JObject>()["currency"])</value>
</set-header>
<set-backend-service base-url="https://webhook.site/xxxxx" />
</inbound>
Even just adding the whole request-body to a variable causes the request-body to be removed (either as JObject or string):
<inbound>
<base />
<set-variable name="test" value="#(context.Request.Body.As<JObject>())" />
<set-backend-service base-url="https://webhook.site/xxxxx" />
</inbound>
or
<inbound>
<base />
<set-variable name="test" value="#(context.Request.Body.As<string>())" />
<set-backend-service base-url="https://webhook.site/xxxx" />
</inbound>

Not that good documentation around this, but if you access context.Request.Body, it cannot be accessed again further in the policy, and it seems that this is true for the request moving forward to the backend.
There was no good documentation on this, but I find it strange that my google searches did not see others with similar issues.
The way to specify preserveContent: true in the set-header is as such:
<inbound>
<base />
<set-header name="x-bp-currency" exists-action="override">
<value>#((string)context.Request.Body.As<JObject>(preserveContent: true)["currency"])</value>
</set-header>
<set-backend-service base-url="https://webhook.site/xxxxx" />
</inbound>

Related

What is Rewrite Html tag in web.config

I found this line of code in my web.config, and I cannot understand what does it exactly do for my website. What is the need of this in my web.config?
<rule name="RewriteHTML">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions> <action type="Rewrite" url="{R:1}.html" />
</rule>
You have a rule named "RewriteHTML" (that's up to you).
The match element is the pattern you want to match (regular expression). In your case, you match everything.
If you want to test Regex: https://regex101.com/
The action element tells what should be done with request that match the pattern. The type Rewrite tells that the request should be rewritten to another URL.
The conditions in this case tells to not match requests to files or directories.
So what happens is that you add ".html" to all your incoming requests.
For more clarification on {R:1} notation: IIS URL Rewrite {R:N} clarification

IIS - Remove trailing slash when directory

I want to remove trailing slashes from all requests from "/" to "", even when it's a directory. For instance:
Folder: http://bleh.local/sign-in/
File to be served: http://bleh.local/sign-in/index.html
Default document: index.html
I would like to redirect it to "http://bleh.local/sign-in"
Expected behavior: /sign-in/ redirected to /sign-in
Actual behavior: /sign-in redirected to /sign-in/
Web.config:
<rule name="RemoveTrailingSlashRule1" enabled="true" stopProcessing="true">
<match url="(.*)/$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Redirect" url="{R:1}" />
</rule>
applicationHost.config:
<sectionGroup name="rewrite">
<section name="globalRules" overrideModeDefault="Allow" allowDefinition="AppHostOnly" />
<section name="rules" overrideModeDefault="Allow" />
<section name="outboundRules" overrideModeDefault="Allow" />
<section name="providers" overrideModeDefault="Allow" />
<section name="rewriteMaps" overrideModeDefault="Allow" />
<section name="allowedServerVariables" overrideModeDefault="Allow" />
</sectionGroup>
With this setup, I get infinite redirects because I removed this instruction from the original setup:
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
I think my problems come from this: https://support.microsoft.com/en-ca/help/298408/iis-generates-courtesy-redirect-when-folder-without-trailing-slash-is
Any ideas?
Note: How can I configure IIS to serve directories without trailing slashes and always serve relative to root path? is not solving my problem. The backslashes are added consistently.
This is because of the <defaultDocument> in IIS.
<defaultDocument> rule is enabled in IIS by default which forces the page to be redirected to the url with trailing slash, if it did not find a file. domain/sign-in/
<rule name="RemoveTrailingSlashRule1" enabled="true" stopProcessing="true">
<match url="(.*)/$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Redirect" url="{R:1}" />
</rule>
Now this rule, matches the url with the trailing slash (and matches directory) and in turn redirects to the one without the trailing slash. domain/sign-in which creates a loop.
The solution is to add the rule<defaultDocument enabled="false"></defaultDocument>.
https://learn.microsoft.com/en-us/iis/configuration/system.webserver/defaultdocument/

Returning a custom response code for for serving static html when using IIS ApplicationInitialization remapManagedRequestsTo feature?

I'm currently using the ApplicationInitialization feature of IIS to warm up my ASP.NET application. I've set the attribute remapManagedRequestsTo to "warmup.html".
<applicationInitialization remapManagedRequestsTo="warmup.html" skipManagedModules="true" doAppInitAfterRestart="true" >
<add initializationPage="/home" />
<add initializationPage="/about-us" />
</applicationInitialization>
It's working well but I would like to return a custom status code when the content for Warmup.html is returned to the browser. This is so that when I run some smoke tests after deployment I get to know when the warm up has finished.
I've tried using URL Rewrite to change the status code from 200 to 555 to serve up warmup.html and it does change the status code but doesn't serve the content in warmup.html
<rewrite>
<rules>
<rule name="Change warm up status code" stopProcessing="true">
<match url="warmup.html" />
<action type="CustomResponse" statusCode="555" subStatusCode="0"/>
</rule>
</rules>
</rewrite>
Is there a way I can do both the serving of warmup.html's content AND return a custom status code of 555?
Finally found my answer in a blog post written by Morten Bock
Turns out I have to remove the two attributes remapManagedRequestsTo and skipManagedModules (default value of false) which leaves us with
<applicationInitialization doAppInitAfterRestart="true">
<add initializationPage="/home" />
<add initializationPage="/about-us" />
</applicationInitialization>
Then let the URL Rewrite module take over but we want to rewrite the response code when the Application Initialization is making the request marked by the server variable APP_WARMING_UP containing a value of 1. When this condition is met we can create a custom response as the action and pop the statusCode attribute with 555.
<rewrite>
<rules>
<rule name="WarmUp" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<conditions>
<add input="{APP_WARMING_UP}" pattern="1" />
</conditions>
<action type="CustomResponse" statusCode="555" statusReason="Site is warming up" statusDescription="Try again shortly" />
</rule>
</rules>
</rewrite>
Then to catch the status 555 as a custom error and direct the user to the friendly warm up page warmup.html
<system.webServer>
<httpErrors errorMode="Custom">
<error statusCode="555" path="warmup.html" responseMode="File" />
</httpErrors>
</system.webServer>

Relative paths not working as expected when using URL Rewrites

I'm doing a URL rewrite to a localhost site:
<rule name="test" enabled="true" stopProcessing="true">
<match url="demo/(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="Rewrite" url="http://localhost:7777/{R:1}" logRewrittenUrl="true" />
</rule>
Let's say the folder structure in localhost:7777 is:
/app/something/demo.css
and
/app/index.html
And the call made in the main site is:
http://www.domain.com/afolder/anotherfolder/demo/app/index.html
When I do a simple reference to a css file from index.html which sits in the localhost:7777 website like this:
<link rel="stylesheet" type="text/css" href="/app/something/demo.css">
I would expect the path to be: http://www.domain.com/afolder/anotherfolder/demo/app/something/demo.css.
But instead the path becomes:
http://www.domain.com/app/something/demo.css
Even if I try href="../demo.css" I would expect to see:
http://www.domain.com/afolder/anotherfolder/demo/app/demo.css
But no... I get: http://www.domain.com/afolder/demo.css
Why :( ?????

Internal navigation rejected: <allow-navigation not set> in Cordova on iOS

I have built an iOS app using Cordova. The app tries to load a web page e.g. http://yourdomain.example/home in index.html. But, the page stays white blank with error in console "Internal navigation rejected - <allow-navigation> not set for URL='http://yourdomain.example/home'".
I have already set <access origin="http://yourdomain.example/home" subdomains="true" /> and also tried by setting <allow-navigation> tag. But the page stays blank. am I missing anything else? Please guide.
After adding the following meta tag in index.html, the "internal navigation error" has gone, but the page is still white blank.
<meta http-equiv="Content-Security-Policy" content="default-src *; style-src 'self' 'unsafe-inline'; script src: 'self' 'unsafe-inline' 'unsafe-eval'">
I was having this problem and it turns out there are two config.xml files. The first one is in Xcode, but you have to edit the second one from the filesystem.
AppName/config.xml
AppName/platforms/ios/AppName/config.xml
I added
<allow-navigation href="*" />
to both of them and it worked. Using 6.3.0.
You have to add this line to your config.xml
<allow-navigation href="http://yourdomain.example/home" />
or this one if you want to allow the navigation to all the URLs on yourdomain.example
<allow-navigation href="http://yourdomain.example/*" />
Thanks for the pointer. The solution
<allow-navigation href="about:" />
caused a cordova prepare error for me. I need to use the following instead:
<allow-navigation href="about:*" />
It is a scheme used internally, just allow access/navigation to it:
<access origin="about:*" />
<allow-navigation href="about:" />
It is not recommended to use
<access origin="*" />
<allow-navigation href="*" />
as described in cordova-plugin-whitelist.
It's not recommend to open a url in your base web view. Use the cordova-plugin-inappbrowser and call the inappbrowser to open outer url:
function open_outer_url(url){
if(window.cordova && window.cordova.InAppBrowser){
window.cordova.InAppBrowser.open(url, "_blank", 'location=no');
}else{
window.open(url,'_blank');
}
}