How to use Azure Function App Function Tester (Example) - azure

Could someone provide and example of how to use the Azure Function App Function Tester with an Example?
This is some default sample code for a webhook function:
#r "Newtonsoft.Json"
using System;
using System.Net;
using Newtonsoft.Json;
public static async Task<object> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info($"Webhook was triggered!");
string jsonContent = await req.Content.ReadAsStringAsync();
dynamic data = JsonConvert.DeserializeObject(jsonContent);
if (data.first == null || data.last == null)
{
return req.CreateResponse(HttpStatusCode.BadRequest, new
{
error = "Please pass first/last properties in the input object"
});
}
return req.CreateResponse(HttpStatusCode.OK, new
{
greeting = $"Hello {data.first} {data.last}!"
});
}
This is an image of the Function App Function Tester in MS Azure

In the Tester blade, you could set the HTTP method, header, parameter,etc.
For the sample code you gave, you should provide Request body like a JSON format, then click the Run button.
{
"first": "Azure",
"last": "Functions"
}
Then you could find the function log.
Update:
If you want to use different http methods,
you could go to Integrate of the function, then check the Selected HTTP methods option.
you could set the methods in the function.json file.
For example:
"methods": [
"get",
"post",
"delete",
"head"
]

Related

Azure Function HTTP Triggers / Method Routing / Open API Implementation

I am currently making an API that will be hosted via Azure Functions. I'm running .net core 3.1. The way I have the project routed right now is defining the accepted methods as a parameter for the HttpTrigger then I have an if statement for determining how the endpoint was called. I am attempting to use the OpenAPI package to create API definitions, but when I assign Methods to the function, the Swagger document only picks up the first Method listed (PUT). I am unsure of the intended structure / usage of endpoints that have multiple possible request methods.
See code below. (OpenAPI tags are placeholder descriptions)
namespace Dyn.Sync.Func.PractifiSync
{
public class Prospect
{
[FunctionName("Prospect")]
[OpenApiOperation(operationId: "Run", tags: new[] { "name" })]
[OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)]
[OpenApiParameter(name: "name", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **Name** parameter")]
[OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "text/plain", bodyType: typeof(string), Description = "The OK response")]
public async Task<IActionResult> Create([HttpTrigger(AuthorizationLevel.Anonymous, "post", "put", Route = null)] HttpRequest req, ILogger log)
{
string primarySecretsContainerName = "Main";
DynUser user = await DynAuthManager.CreateDynUserAsync(req);
DynProspect prospect = JsonSerializer.Deserialize<DynProspect>(req.Body);
PFIConnection pfiConnector = PFIConnectionsCache.GetConnection(user, DynSecretsCache.GetSecretsContainer(primarySecretsContainerName));
try
{
if (!pfiConnector.IsConnected) { await pfiConnector.Connect(); }
if (req.Method == "POST") { return await pfiConnector.CreateProspect(prospect); }
if (req.Method == "PUT") { return await pfiConnector.UpdateProspect(prospect); }
else { return new ObjectResult("Invalid method.") { StatusCode = 400 }; }
}
catch (Exception ex)
{
DynError dynError = new DynError(ex);
log.LogError(ex, "Exception " + dynError.RequestID.ToString() + " occured.");
return (IActionResult)new ExceptionResult(ex, true);
}
}
}
}
My question is this: When the swagger document is created, it only lists whatever method I defined first (in other words, it ignores the "put" method). What is the intended way to structure an API when creating it in Azure functions? I tried creating a separate method in the same class for each HTTP method that it would accept, but then I couldn't even hit the endpoint when making requests. Does microsoft want us to create a new function class for each endpoint? So then instead of:
PUT http://myapi.azure.com/api/prospect
POST http://myapi.azure.com/api/prospect
it would be:
PUT http://myapi.azure.com/api/updateprospect
POST http://myapi.azure.com/api/prospect
I should note that this will eventually live under and Azure API Management instance, which makes me even more worried to implement it in a "one function per method" fashion as when loading azure functions the way I have done it, it correctly assigns the methods in APIM and I'd prefer not to have to manually configure it.
I have been searching for documentation on this specific issue with no luck. Anyone have any ideas how Microsoft intended this to be used?
Thanks.

Send JSON to restful API via Logic App and Azure Function

I have JSON file which is composed in Logic Apps, Now i need to send this JSON file to a Rest API using the Post method through Azure Function.
Does anyone know how to achieve this using PowerShell in Function if not any other languages like c# or python?
I would recommend you to upload the file to a blob storage and generate a SAS url. Then you can use azure function to call the http and send the url.
public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
List<FileAndUrl> filenamesAndUrls;
if (req.Method == HttpMethod.Get)
{
filenamesAndUrls = req.RequestUri.Query
.Remove(0, 1) // remove '?' at the start of the query
.Split('&') // all comands are seperated by '&'
.Select(s =>
{
var keyValue = s.Split('=');
return new FileAndUrl() { FileName = keyValue[0], Url = keyValue[1] };
}).ToList();
}
else
{
filenamesAndUrls = await req.Content.ReadAsAsync<List<FileAndUrl>>();
}
return CreateResult(filenamesAndUrls);
}
EXAMPLE
You can use the Azure functions connector in your workflow (logic apps). Then, you just need to set the body of the request with your payload, also specify the header 'Content-Type' as 'application/json'
https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-azure-functions

Error creating HttpTrigger Azure Function with CosmosDB document output

I want to make an Azure Function that takes a JSON body passed to it and inserts this document in to an Azure COSMOSDB instance.
To do so, I created this function in the Azure Functions Portal:
And implement the function like so:
#r "Newtonsoft.Json"
using Newtonsoft.Json.Linq;
using System.Net;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log, object outputDocument)
{
var requestContent = await req.Content.ReadAsStringAsync();
log.Verbose($#"Received request:\n{requestContent}");
var newDoc = JObject.Parse(requestContent);
newDoc["Id"] = Guid.NewGuid().ToString();
newDoc["shardKey"] = newDoc.Value<string>(#"Id").Substring(8);
outputDocument = newDoc;
return req.CreateResponse(System.Net.HttpStatusCode.Created);
}
In the portal, I put in an easy sample doc:
{
"prop1": 2,
"prop2": "2017-02-20",
}
and click 'Run'
I'm immediately met with
as an overlay in the portal IDE along with
{
"id": "145ee924-f824-4064-8364-f96dc12ab138",
"requestId": "5a27c287-2c91-40f5-be52-6a79c7c86bc2",
"statusCode": 500,
"errorCode": 0,
"message": "'UploadDocumentToCosmos' can't be invoked from Azure WebJobs SDK. Is it missing Azure WebJobs SDK attributes?"
}
in the log area.
There seems to be nothing I can do to fix the issue, yet I sure feel like I'm doing everything right.
What do I need to do to simply take a JSON object as an HTTP Request to an Azure Function, and insert/upsert said object in to an Azure Cosmos DB instance??
For async functions you should use IAsyncCollector:
public static async Task<HttpResponseMessage> Run(
HttpRequestMessage req, TraceWriter log,
IAsyncCollector<object> outputDocuments)
{
...
await outputDocuments.AddAsync(newDoc);
}
Can you try adding out to dynamic outputDocument?
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log, out dynamic outputDocument)

How to wire up an HttpTrigger to the Designer

I'm new to Azure Functions.
I'm trying to write an Http Trigger that will not only "fail" bad json (that doesn't match my schema, I want to provide feedback to the caller with the invalid messages about the json they submitted.
Ok, so first I brought up VS2017.
Then I coded it up. I can use PostMan to test it, it works fine during PostMan testing.
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
////using MyExceptionLibrary;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
namespace MyNamespace.AzureFunctionsOne
{
public static class MyFirstHttpTrigger
{
[FunctionName("MyFirstHttpTriggerFunctionName")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function MyFirstHttpTriggerFunctionName about to process a request.");
try
{
string jsonSchemaText = #"{
'description': 'A person',
'type': 'object',
'properties':
{
'name': {'type':'string'},
'hobbies': {
'type': 'array',
'items': {'type':'string'}
}
}
}";
JSchema schema = JSchema.Parse(jsonSchemaText);
var content = req.Content;
string jsonContent = content.ReadAsStringAsync().Result;
JObject jobj = JObject.Parse(jsonContent);
IList<string> messages;
bool valid = jobj.IsValid(schema, out messages);
if (!valid)
{
string errorMsg = string.Join(",", messages);
throw new ArgumentOutOfRangeException(string.Format("Bad Json. ({0})", errorMsg));
}
}
catch (Exception ex)
{
string errorMsg = ex.Message; //// ExceptionHelper.GenerateFullFlatMessage(ex);
log.Error(errorMsg);
return req.CreateResponse(HttpStatusCode.BadRequest, errorMsg);
}
log.Info("C# HTTP trigger function MyFirstHttpTriggerFunctionName processed a request.");
return req.CreateResponse(HttpStatusCode.OK);
}
}
}
I then "published" this azure function to the cloud.
My issue is now........how do I wire this into the Logic App Designer to be the trigger?
In the below, I'm able to add the generic-request-trigger.
In the below, I've also looked for ~my~ azure http trigger that I published, and no luck.
So I can't figure out how to get my custom Http-Trigger to be available in the Logic App designer so it can be the entry-point-trigger.
Am I missing some basic concept?
My end game is:
I want a third-party to POST some json to my azure-logic-app as an http-request. That should be the trigger. But I only want the trigger to keep running if they submit valid json. (This I know can be done via the generic request trigger). My caveat (and thus my custom http-trigger) is that I want the third-party to get schema violation messages so they know what they did wrong.
If I understand this correctly, you have a workflow you want 3rd party to invoke via HTTP request, and when the request body isn't well formatted, you want to return a friendly error.
So you coded up an Azure Function that expose itself as a request endpoint, and does the validation.
If that's the case, you will just need to have the Azure Function to invoke Logic App after success validation, and pass the original payload to Logic App. So you can create the Logic App with a request trigger, save and get the Url, and have Function call that Url.

Azure Web Function: Create binding from http request to inputBlob path

Desired Scenario
From an Arduino:
take a photo and upload image as a blob in Azure storage container (works fine)
call a Web Function using HTTP with blob name and other info (works fine)
From the Web function
read the HTTP request (works fine)
read the blob using the information from the HTTP request (does not work)
process the blob (not implemented yet)
respond result to Arduino
Problem
I can't figure out how to make the binding from the path to the HTTP parameters.
Web Function Config
{
"bindings": [
{
"authLevel": "function",
"name": "request",
"type": "httpTrigger",
"direction": "in",
"methods": [
"post"
]
},
{
"name": "$return",
"type": "http",
"direction": "out"
}
],
"disabled": false
}
Error:
Function ($HttpTriggerCSharp1) Error: Microsoft.Azure.WebJobs.Host: Error indexing method 'Functions.HttpTriggerCSharp1'. Microsoft.Azure.WebJobs.Host: No binding parameter exists for 'blobName'.
ORIGINAL, NON-WORKING code (working code below):
using System.Net;
public static async Task<HttpResponseMessage> Run(
HttpRequestMessage request,
string blobName, // DOES NOT WORK but my best guess so far
string inputBlob,
TraceWriter log)
{
// parse query parameter
string msgType = request.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "msgType", true) == 0)
.Value;
// Get request body
dynamic data = await request.Content.ReadAsAsync<object>();
// Set name to query string or body data
msgType = msgType ?? data?.msgType;
string deviceId = data.deviceId;
string blobName = data.BlobName; // DOES NOT COMPILE
log.Info("blobName=" + blobName); // DOES NOT WORK
log.Info("msgType=" + msgType);
log.Info("data=" + data);
return msgType == null
? request.CreateResponse(HttpStatusCode.BadRequest, "HTTP parameter must contain msgType=<type> on the query string or in the request body")
: request.CreateResponse(HttpStatusCode.OK, "Hello " + deviceId + " inputBlob:");// + inputBlob );
}
HTTP request looks like this:
https://xxxxxxprocessimagea.azurewebsites.net/api/HttpTriggerCSharp1?code=CjsO/EzhtUBMgRosqxxxxxxxxxxxxxxxxxxxxxxx0tBBqaiXNewn5A==&msgType=New image
"deviceId": "ArduinoD1_001",
"msgType": "New image",
"MessageId": "12345",
"UTC": "2017-01-08T10:45:09",
"FullBlobName": "/xxxxxxcontainer/ArduinoD1_001/test.jpg",
"BlobName": "test.jpg",
"BlobSize": 9567,
"WiFiconnects": 1,
"ESPmemory": 7824,
"Counter": 1
(I know, msgType appears both in URL and in headers. I've tried different combinations - no effect).
If what I'm trying to do is impossible, alternative suggestions are also welcome. I just need a way through.
This code works thanks to Tom Sun's hint. The trick was to remove the binding to the storage blob in the JSON and instead just call the blob directly from the code.
#r "Microsoft.WindowsAzure.Storage"
using Microsoft.WindowsAzure.Storage; // Namespace for CloudStorageAccount
using Microsoft.WindowsAzure.Storage.Blob; // Namespace for Blob storage types
using Microsoft.WindowsAzure.Storage.Queue;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using System.Net;
using System.IO;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage request, string inputBlob, TraceWriter log)
{
string msgType = request.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "msgType", true) == 0)
.Value;
dynamic data = await request.Content.ReadAsAsync<object>();
msgType = msgType ?? data?.msgType;
string deviceId = data.deviceId;
string blobName = data.BlobName;
string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("nnriothubcontainer/" + deviceId);
CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
MemoryStream imageData = new MemoryStream();
await blob.DownloadToStreamAsync(imageData);
return msgType == null
? request.CreateResponse(HttpStatusCode.BadRequest, "HTTP parameter must contain msgType=<type> on the query string or in the request body")
: request.CreateResponse(HttpStatusCode.OK, "Hello " + deviceId + " inputBlob size:" + imageData.Length);// + inputBlob );
}
take a photo and upload image as a blob in Azure storage container (works fine)
call a Web Function using HTTP with blob name and other info (works fine) From the Web function
read the HTTP request (works fine)
Base on my understanding, you can read the Http info including blob uri ,blob name, and try to operate the storage blob in the Azure Http Trigger Function.
If it is that case, we could try to refer to "Microsoft.WindowsAzure.Storage" and import namespaces. Then we could operate Azure storage with Azure storeage SDK. More detail info about how to operate storage blob please refer to document.
#r "Microsoft.WindowsAzure.Storage"
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;

Resources