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.
Related
I want to pass the contents of an email into my Function App in order to remove HTML.
I'm following this tutorial but I'm puzzled at how the incoming email is passed in. I know that it is a http request but not sure which line is dealing with this email that we can then do work on.
We have our req which is our http request so I'm guessing that once we create the trigger in Azure, req is passed straight in?
This the example code that is provided:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
public static async Task<IActionResult> Run(HttpRequest req, ILogger log) {
log.LogInformation("HttpWebhook triggered");
// Parse query parameter
string emailBodyContent = await new StreamReader(req.Body).ReadToEndAsync();
// Replace HTML with other characters
string updatedBody = Regex.Replace(emailBodyContent, "<.*?>", string.Empty);
updatedBody = updatedBody.Replace("\\r\\n", " ");
updatedBody = updatedBody.Replace(#" ", " ");
// Return cleaned text
return (ActionResult)new OkObjectResult(new { updatedBody });
}
req.Body is the body of your HTTP message when you send a POST request to your Function. So the body of your request should contain your entire email content, then you should be good to go.
Actually your comment // Parse query parameter is wrong. It is not parsing a query parameter but reading the HTTP message body.
btw: I would advise you to do yourself a favor and not write C# Script Functions in the Azure Portal but write and test them locally, e.g. in VS Code, then compile them and deploy them properly to Azure Functions.
I am trying to use the Azure REST API with SAS Service. While I got this working with the most basic GET command, I am having trouble with the setup as soon as I need to add variables to the call as they are added at the same place as the SAS token. e.g. for "List Containers" I should use the URL "https://myaccount.blob.core.windows.net/?comp=list". But the "?comp=list" part is that the same place as the SAS Token. How can I give the request both the tokens and the variables? (I do not have much experience with REST APIs, so maybe I am misunderstanding something). I also posted my code below.
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
namespace ConsoleProgram
{
public class DataObject
{
public string Name { get; set; }
}
public class Class1
{
private const string URL = "url";
private static string urlParameters = "?token";
static void Main(string[] args)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(URL);
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
// List data response.
HttpResponseMessage response = client.GetAsync(urlParameters+ "&comp=list").Result; // Blocking call! Program will wait here until a response is received or a timeout occurs.
if (response.IsSuccessStatusCode)
{
Console.WriteLine(response.ToString());
// Parse the response body.
var dataObjects = response.Content.ReadAsStringAsync().Result; //Make sure to add a reference to System.Net.Http.Formatting.dll
Console.WriteLine("{0}", dataObjects);
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
}
// Make any other calls using HttpClient here.
// Dispose once all HttpClient calls are complete. This is not necessary if the containing object will be disposed of; for example in this case the HttpClient instance will be disposed automatically when the application terminates so the following call is superfluous.
client.Dispose();
}
}
}
When we use the SAS token to call Azure blob rest API, the SAS token is used as the query string. So we can use '&' to splice SAS token and other query parameters, such as https://myaccount.blob.core.windows.net/?comp=list&{sasToken}.
Besides, please note that if you want to list containers in one storage account, you need to create an account SAS token. The service SAS token cannot implement it. Regarding how to create the account SAS token, please refer to here
We use an Azure Service Bus to post all of our requests from our Xamarin mobile app. The Azure Service Bus is bound to an Azure Function which is triggered each time a requests hits the Azure Service Bus.
We have found that we are getting errors from this Azure Function when we send data above a certain size. We can send up to 800 records without a problem but when we send >=850 records we get the following error:
[Error] Exception while executing function:
Functions.ServiceBusQueueTrigger. mscorlib: Exception has been thrown
by the target of an invocation. mscorlib: One or more errors occurred.
A task was canceled.
The service that is being invoked is an ASP.NET Web API RESTful service that saves the data records into a database. This doesn't generate any errors at all.
Here is my Azure Function code.
#r "JWT.dll"
#r "Common.dll"
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Microsoft.ServiceBus.Messaging;
public static void Run(BrokeredMessage message, TraceWriter log)
{
log.Info($"C# ServiceBus queue trigger function processed message: {message.MessageId}");
if (message != null)
{
Common.Entities.MessageObjectEntity messageObject = message?.GetBody<Common.Entities.MessageObjectEntity>();
string msgType = messageObject?.MessageType;
var msgContent = messageObject?.MessageContent;
log.Info($"Message type: {msgType}");
double timestamp = (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
string subscriber = "MYSUBSCRIBER";
string privatekey = "MYPRIVATEKEY";
Dictionary<string, object> payload = new Dictionary<string, object>()
{
{"iat", timestamp},
{"subscriber", subscriber}
};
string token = JWT.JsonWebToken.Encode(payload, privatekey, JWT.JwtHashAlgorithm.HS256);
using (var client = new HttpClient())
{
string url = $"http://myexamplewebservices.azurewebsites.net/api/routingtasks?formname={msgType}";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(subscriber, token);
HttpContent content = new StringContent((string)msgContent, Encoding.UTF8, "application/json");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.PostAsync(new Uri(url), content);
if (response == null)
{
log.Info("Null response returned from request.");
}
else
{
if (response.Result.IsSuccessStatusCode && response.Result.StatusCode == System.Net.HttpStatusCode.OK)
{
log.Info("Successful response returned from request.");
}
else
{
log.Info($"Unsuccessful response returned from request: {response.Result.StatusCode}.");
}
}
}
log.Info("Completing message.");
}
}
This code has been working for several years and works across all our other apps / web sites.
Any ideas why we're getting errors wehen we post large amounts of data to our Azure Service Bus / Azure Function?
It may caused by "new httpclient", there is a limit to how quickly system can open new sockets so if you exhaust the connection pool, you may get some errors. You can refer to this link: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
And could you please share some more error message ?
I can see that you are creating httpclient connection on each request which possibly be causing this issue. Httpclient creates a socket connection underneath it and has hard limit on it. Even when you dispose it it remains there for couple of mins that can't be used. A good practice is to create single static httpclient connection and reuse it. I am attaching some documents for you to go through.
AzFunction Static HttpClient , Http Client Working , Improper instantiation
I have an HTTP Trigger Azure Function which is currently in 1.x. The code is as below:
using System.Net;
using System.Threading.Tasks;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info($"C# HTTP trigger function processed a request. RequestUri={req.RequestUri}");
// parse query parameter
string name = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
.Value;
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
// Set name to query string or body data
name = name ?? data?.name;
return name == null
? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
: req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
}
While trying to upgrade it to 2.x, I am getting an issue with GetQueryNameValuePairs
I am getting error - 'HttpRequestMessage' does not contain a definition for 'GetQueryNameValuePairs'
Is there no support for this method in 2.0? How can this be accomplished in .net standard?
Function runtime 1.x is on Full .Net Framework, while 2.x runs on .NET Core env and our function code targets at .NET Standard.
For this class HttpRequestMessage, it doesn't have GetQueryNameValuePairs method in .NET Standard assembly.
Migrating from 1.x to 2.x usually needs work of code modification. Since it's just a template, I suggest you delete it and recreate a Http Trigger in 2.x runtime. You may see a different template work with .NET Standard.
Here is the sample code that looks up query string parameters in Functions V2.x
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
public static IActionResult Run(HttpRequest req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
if (req.Query.TryGetValue("name", out StringValues value))
{
return new OkObjectResult($"Hello, {value.ToString()}");
}
return new BadRequestObjectResult("Please pass a name on the query string");
}
In Functions v2 this has changed to req.GetQueryParameterDictionary();
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"
]