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();
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've created a test HTTP Trigger azure function. I can trigger it correctly on azure portal and browser. However trigger from Unity editor gives "Error HTTP/1.1 500 Internal Server Error".
starting Azure function:
public static async Task<IActionResult> Run(HttpRequest req, ILogger log){
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
return name != null
? (ActionResult)new OkObjectResult($"Hello, {name}")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");}
My Unity code:
formData.Add(new MultipartFormDataSection("name", "SampleName", "text/plain"));
UnityWebRequest www = UnityWebRequest.Post("https://samplefunction.azurewebsites.net/api/HttpTriggerTest?herelongblob", formData);
yield return www.SendWebRequest();
Azure CORS configuration: Request Credentials ON: Enable Access-Control-Allow-Credentials.
Function is setup always on. Integrate-Trigger: selected methods GET, POST. Authorisation level:Function.
function's host.json: "version": "2.0", "extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle", "version": "[1.*, 2.0.0)"
App Service authentication:Anonymous
Unity-Azure sdk and google setup search results seems all outdated/not supported :(
What route should I take to get this to work please? happy to try any sdk / unity asset store to reach azure you may suggest! Cheers!
The error occurs in the two lines below in your code:
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
Since you set text/plain in your code. So when you do the www.SendWebRequest(), it will send a form data but not a json format data. So it will show the error message.
Below I provide the code for your reference:
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var arr = req.Form.Keys;
foreach (var key in arr) {
log.LogInformation(key);
log.LogInformation(req.Form[key]);
}
return (ActionResult)new OkObjectResult("success");
}
You can get the key and the value of the form data in the code above and then compose them to json format and then do DeserializeObject. Or if you want to use the data to create a object, you can even do not do DeserializeObject and just set the value into your object directly.
By the way, you can also try to change the text/plain to application/json in your code new MultipartFormDataSection("name", "SampleName", "text/plain"). But, as I don't know much about MultipartFormDataSection, so I'm not sure if this solution can work.
Hope it helps.
I am trying out Azure Function Apps.
The first one following the example in a tutorial with Open Weather map, stopped working after I used log.WriteLine(), which correctly threw a compiler error. I changed to log.Info() and it kept complaining about TraceWriter not containing a definition for WriteLine.
After a lengthy troubleshooting session, I created a new function, copying all the content of the broken one, and it worked immediately.
Created a new function, as before, and began making changes to the Run() method, and running this function yields:
"The resource you are looking for has been removed, had its name
changed, or is temporarily unavailable."
Bearing in mind, the function URL is based on the default key Azure generates when the function is created: https://.azurewebsites.net/api/WeatherWhereYouAre?code=my1really2RAndom3defauLT4Key5from6Azure==
Created yet another function, with no changes from the default "Hello Azure" sample, and it yields a 500 error with:
"Exception while executing function: Functions.HttpTriggerCSharp2 ->
One or more errors occurred. -> Exception binding parameter 'req' ->
Input string was not in a correct format."
This is the content of the project.json file:
{
"frameworks": {
"net46": {
"dependencies": {
"Microsoft.IdentityModel.Clients.ActiveDirectory": "3.16.0",
"Microsoft.Azure.KeyVault": "2.3.2",
"Microsoft.AspNet.WebApi.Client": "5.2.3"
}
}
}
}
And the run.csx:
using System.Net;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
// 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);
}
EDIT
In the above image, note that this is httpTriggerFSharp1, but the exception is HttpTriggerCSharp2 (which is the only one that works!)
Is there a way I can properly troubleshoot these?
For the default HttpTrigger template for C#, you could call it as follows:
Get https://brucefunapp.azurewebsites.net/api/HttpTriggerCSharp3?name=bruce&code=ItDhLMxwDYmTvMTYzVbbALtL5GEcmaL5DlzSaD4FRIuFdh17ZkY71g==
Or
Post https://brucefunapp.azurewebsites.net/api/HttpTriggerCSharp3?code=ItDhLMxwDYmTvMTYzVbbALtL5GEcmaL5DlzSaD4FRIuFdh17ZkY71g==
Content-type: application/json
{"name": "bruce"}
For more details about Azure Functions C# script, you could refer to here.
Is there a way I can properly troubleshoot these?
Per my understanding, you could leverage Precompiled functions and use Visual Studio 2017 Tools for Azure Functions for creating, local debugging, and publishing to Azure.
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.
How do I use docuementdb binding with azure function http binding:
using System.Net;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, out object locationDocument, TraceWriter log){
log.Info("C# HTTP trigger function processed a request.");
var data = await req.Content.ReadAsStringAsync();
return req.CreateResponse(HttpStatusCode.OK, $"{data}");
}
Getting this error:
error CS1988: Async methods cannot have ref or out parameters
This is not specific to Document DB. If your function is async, and you already used the return value for HTTP output binding, you would need to inject IAsyncCollector<T> for all other output bindings.
See the second example in this answer.