Let's setup the basics:
I'm using Google Api Gateway with differents backends like Google Cloud Function.
First, I was parsing the req paramters with a switch statement on a header containing the original request url. (Very messy but working)
So I decided to use an express app instead for my cloud function.
But here is the thing: my functions always receive / from the gateway and generate raging errors like CANNOT GET / when my path is https://mygateway/api/subservice/action
So my question is: can I change the handling of the express app to parse my header containing the original request url and not the default path url?
Here is a part of my config:
{
"swagger": "2.0",
"info": {
"title": "my API",
"version": "1.0.0"
},
"basePath": "/api",
"host": "mygateway.[REGION].gateway.dev",
"schemes": [
"https"
],
"paths": {
"/subservice/action": {
"get": {
"x-google-backend": {
"address": "https://[REGION]-[ProjectID].cloudfunctions.net/[mycloudfunction]"
},
"security": [
{
"jwt_security": []
}
],
I found on this question something similar that guided my search of the response possible duplicate here
According Google's explanation of path translation when we use x-google-backend, the backend will only receive the basic request. we have to define with the parameter path_translation the behaviour we expect. In my case, I want to receive the same path so i use APPEND_PATH_TO_ADDRESS
Related
Whenever a new release pipeline is ran in Azure DevOps, the URL Is changed.. currently my ARM template has a hard-coded URL which can be annoying to keep on adding in manually.
"cors": {
"allowedOrigins": [
"[concat('https://',parameters('storage_account_name'),'.z10.web.core.windows.net')]"
}
The only thing that changes is the 10 part in the z10 so essentially i want it to be something like
[concat('https://',parameters('storage_account_name'),'.z', '*', '.web.core.windows.net')] I dont know if something like that is valid but essentially its so that the cors policy will accept the URL regardless of the z number.
Basically speaking this is not possible, because of the CORS standard (see docs).
which allows only for exact origins, wildcard, or null.
For instance, ARM for Azure Storage is also following this pattern allowing you to put a list of exact origins or a wildcard (see ARM docs)
However, if you know your website name, in your ARM you can receive the full host and use it in your CORS:
"[reference(resourceId('Microsoft.Web/sites', parameters('SiteName')), '2018-02-01').defaultHostName]"
The same with a static website (which is your case I guess) if you know the storage account name:
"[reference(concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName')), '2019-06-01', 'Full').properties.primaryEndpoints.web]"
Advance reference output manipulation
Answering on comment - if you would like to replace some characters in the output from the reference function the easiest way is to use build-in replace function (see docs)
In case you need a more advanced scenario I am pasting my solution by introducing a custom function which is removing https:// and / from the end so https://contonso.com/ is transformed to contonso.com:
"functions": [
{
"namespace": "lmc",
"members": {
"replaceUri": {
"parameters": [
{
"name": "uriString",
"type": "string"
}
],
"output": {
"type": "string",
"value": "[replace(replace(parameters('uriString'), 'https://',''), '/','')]"
}
}
}
}
],
# ...(some code)...
"resources": [
# ... (some resource)...:
"properties": {
"hostName": "[lmc.replaceUri(reference(variables('storageNameCdn')).primaryEndpoints.blob)]"
}
]
As of now, from what I see, the only way to use Azure Function Proxy is to re-route an existing api is to call upon that api directly. For example:
Backend URL
https://gateway-api.com/api/getSomething
Route Template
/api
Proxy URL
https://gateway.azurewebsites.net/api
What I want is to have the Backend URL pass through any endpoint relative to the main endpoint.
Effectively this:
Backend URL
https://gateway-api.com/* or i even tried this https://gateway-api.com/{*restOfPath}
This way, any api's that follow the core domain URL will still work as expected.
Here is a re-write of the example above:
Backend URL 2
https://gateway-api.com/*
Route Template 2
/*
Proxy URL 2
https://gateway.azurewebsites.net/api/getSomething
When I do this I can't get it to work or even reach the debuger to log anything.
Is this possible and if not would this be something Azure API Management would be able to accomplish?
Can you provide your configuration file? this is mine:
Proxies.json:
{
"$schema": "http://json.schemastore.org/proxies",
"proxies": {
"proxy1": {
"matchCondition": {
"methods": [ "GET" ],
"route": "/{test}"
},
"backendUri": "http://localhost:7071/abc/Function1"
}
}
}
host.json:
{
"version": "2.0",
"extensions": {
"http": {
"routePrefix": "abc"
}
},
"logging": {
"applicationInsights": {
"samplingExcludedTypes": "Request",
"samplingSettings": {
"isEnabled": true
}
}
}
}
This is the backend url and proxy of my function:
Both of them works fine.
If you change route template, I think the backend url will not have /api unless you give the /api to routePrefix.
Any way, please show the file about how to configure proxy and route template.
I have a backend API (that implements ApiController) which I'd like to put behind an APIM API. ApiController allows us to discriminate between two different GET operations based on the query parameters that are passed in. When I attempt to define these endpoints in APIM, I get the following error:
The message suggests an endpoint is defined solely by the path and operation. But that seems to contradict documentation I found here which suggests there's a way to differentiate between operations based on query parameters:
Required parameters across both path and query must have unique names.
(In OpenAPI a parameter name only needs to be unique within a
location, for example path, query, header. However, in API Management
we allow operations to be discriminated by both path and query
parameters (which OpenAPI doesn't support). That's why we require
parameter names to be unique within the entire URL template.)
I have an ApiController that defines two different Get operations, differing only by the query parameters. How do I represent that in my APIM API?
The problem comes from multiple operation objects with the same OperationId. This is invalid swagger. In the Swagger file did not match the name of the selected API, so change the title attribute of the doc tag to match the destination API it worked..
Here is a similar SO thread you could refer to.
I got my answer from Azure support, sharing the info here:
APIM endpoints are defined by the path, method, and the name you assign to the operation. To differentiate between two GET endpoints to the same controller, differing only by query parameters, you need to hardcode required query parameters into the path. See the following two images:
In the latter image, the hardcoded query parameter is classified by the UI as a template parameter, but it still behaves like a regular query parameter. Query arguments defined in this way:
Are required
Can appear in anywhere in a request's list of query arguments
Are not case-sensitive
Are listed as a "Request Parameter" along side all other path parameters and query arguments in the Development Portal
Edit:
There's a typo in the screenshots. The URLs are case sensitive, and the casing of "blah" were different in each case. Here's what the the Open API Specification looks like when the casing is consistent. The overloaded path (with the query parameter hardcoded into the path template) appears in a section called x-ms-paths:
{
"swagger": "2.0",
"info": {
"title": "Echo API",
"version": "1.0"
},
"host": "<hostUrl>",
"basePath": "/echo",
"schemes": ["https"],
"securityDefinitions": {
"apiKeyHeader": {
"type": "apiKey",
"name": "Ocp-Apim-Subscription-Key",
"in": "header"
},
"apiKeyQuery": {
"type": "apiKey",
"name": "subscription-key",
"in": "query"
}
},
"security": [{
"apiKeyHeader": []
}, {
"apiKeyQuery": []
}],
"paths": {
"/Blah": {
"get": {
"operationId": "blah",
"summary": "Blah",
"responses": {}
}
}
},
"tags": [],
"x-ms-paths": {
"/Blah?alpha={alpha}": {
"get": {
"operationId": "blah2",
"summary": "Blah2",
"parameters": [{
"name": "alpha",
"in": "query",
"required": true,
"type": "string"
}],
"responses": {}
}
}
}
}
When you create an Http triggered API, Azure function hosts it on
https://[function-app-name].azurewebsites.net/api/[Route-configured-in-application]
Is there any way of getting rid of the term api from the URL and make it look like:
https://[function-app-name].azurewebsites.net/[Route-configured-in-application]
The Azure Functions v2 solution is covered in this answer, the http bit needs to be wrapped in an extensions property.
{
"version": "2.0",
"extensions": {
"http": {
"routePrefix": "customPrefix"
}
}
}
Edit the host.json file and set routePrefix to empty string:
{
"http": {
"routePrefix": ""
}
}
The accepted answer no longer works if you're using version 2 functions, instead you need to put the http settings in an extensions property:
"extensions": {
"http": {
"routePrefix": ""
}
}
You can get caught out looking at the hosts.json reference because if you only look at the http section it shows just the http properties so make sure to check the start of the doc for the top level hosts.json format.
You could also leverage the power of Azure Function Proxies for this, which might be better if you want to be explicit about which methods or routes you want to access.
Just create a proxy.json file and add the following piece of JSON to it.
{
"$schema": "http://json.schemastore.org/proxies",
"proxies": {
"myazurefunctionproxy": {
"matchCondition": {
"methods": ["GET"],
"route": "/{slug}"
},
"backendUri": "https://%WEBSITE_HOSTNAME%/api/{slug}"
},
}
}
This sample will redirect all GET-requests to a route with /api/ prefixed.
I have successfully implemented a Lambda authorizer for my AWS API Gateway, but I want to pass a few custom properties from it to my Node.js endpoint.
My output from my authorizer follows the format specified by AWS, as seen below.
{
"principalId": "yyyyyyyy",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow|Deny",
"Resource": "arn:aws:execute-api:<regionId>:<accountId>:<appId>/<stage>/<httpVerb>/[<resource>/<httpVerb>/[...]]"
}
]
},
"context": {
"company_id": "123",
...
}
}
In my case, context contains a few parameters, like company_id, that I would like to pass along to my Node endpoint.
If I was to use a Lambda endpoint, I understand that this is done with Mapping Template and something like this:
{
"company_id": "$context.authorizer.company_id"
}
However, Body Mapping Template is only available under Integration Request if Lambda is selected as Integration type. Not if HTTP is selected.
In short, how do I pass company_id from my Lambda authorizer to my Node API?
Most of the credit goes out to #Michael-sqlbot in the comments to my question, but I'll put the complete answer here if someone else finds this question.
Authorizer Lambda
It has to return an object in this format, where context contains the parameters you want to forward to your endpoint, as specified in the question.
{
"principalId": "yyyyyyyy",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [{
"Action": "execute-api:Invoke",
"Effect": "Allow|Deny",
"Resource": "arn:aws:execute-api:<regionId>:<accountId>:<appId>/<stage>/<httpVerb>/[<resource>/<httpVerb>/[...]]"
}]
},
"context": {
"company_id": "123", <-- The part you want to forward
...
}
}
Method Request
Under Method Request / HTTP Request Headers, add the context property you want to forward:
Name: company_id
Required: optional
Cashing: optional
Integration Request
And under Integration Request / HTTP Headers, add:
Name: company_id
Mapped from: context.authorizer.company_id
Cashing: optional
If you're using lamda-proxy, you can access the context from your event.requestContext.authorizer.
So your company_id can be accessed using event.requestContext.authorizer.company_id.
If you're using lamda-proxy (at least with Golang backend) you can access to that values stored on authorizer context without mapping template usage!
Remember re-launch API and wait a minutes!
It's working for me.