How to set up HTTP outputs in Azure Functions - c#

I've got a simple Azure function for which I set up a DocumentDB output (as an example):
I then added the outputDocument parameter to the function and assigned a value to it in the code (by the way I was surprised that when I set up the output that the runtime didn't automatically modify the function signature):
using System;
public static void Run(string input, out object outputDocument, TraceWriter log)
{
log.Info($"C# manually triggered function called with input: {input}");
outputDocument = new {
text = $"I'm running in a C# function! {input}"
};
}
When I run the function the Azure Functions runtime does it's binding magic and the DocumentDB document gets created.
I then set up an HTTP output:
and defined the res output parameter.
But now what? What's the process of assigning to res? I've of course got to define the destination, request type, parms, etc.

Howiecamp,
The HTTP output binding works in conjunction with the HTTP Trigger to act as the response handler for an HTTP request.
Currently, there isn't an output binding that would send the output payload over HTTP for you, so you'd need to make that HTTP request from your function code (e.g. using the HttpClient and issuing the request). You can see an example in one of our templates here: https://github.com/Azure/azure-webjobs-sdk-templates/blob/10650dbf9c4bad75b0c89b9c355edc53fe913cde/Templates/GitHubCommenter-CSharp/run.csx#L40-L49
I hope this helps!

Related

How to debug locally Azure Function triggered by EventGrid?

I create a new Azure Function which I want it to run by EventGrid. I use Visual Studio 2022 Community.
I want to debug the Function by Postman but I couldn't find the way to do it.
My TestFunction.cs:
public static class TestFunction
{
[FunctionName("Function1")]
public static async Task Function1([EventGridTrigger] EventGridEvent triggerEvent,
[EventGrid(TopicEndpointUri = "Topic", TopicKeySetting = "TopicKey")] IAsyncCollector<EventGridEvent> outputEvents,
ILogger log)
{
//TODO
}
}
When I F5 the project to debug, the console app shows:
Functions:
Function1: eventGridTrigger
For detailed output, run func with --verbose flag.
I try to post by Postman to the Url:
http://localhost:7071/runtime/webhooks/EventGrid?functionName=Function1
There are two things that happen:
1.The Postman request returns:
cannot find function: 'Function1'
2.The console application shows:
[2022-03-14T12:20:57.715Z] Executing HTTP request: {
[2022-03-14T12:20:57.717Z] requestId: "a31453db-3717-43d8-a008-bd459a454fb0",
[2022-03-14T12:20:57.718Z] method: "POST",
[2022-03-14T12:20:57.718Z] userAgent: "PostmanRuntime/7.29.0",
[2022-03-14T12:20:57.719Z] uri: "/runtime/webhooks/EventGrid"
[2022-03-14T12:20:57.721Z] }
[2022-03-14T12:20:57.724Z] cannot find function: 'Function1', available function names: []
[2022-03-14T12:20:57.725Z] Executed HTTP request: {
[2022-03-14T12:20:57.726Z] requestId: "a31453db-3717-43d8-a008-bd459a454fb0",
[2022-03-14T12:20:57.727Z] identities: "(WebJobsAuthLevel:Admin, WebJobsAuthLevel:Admin)",
[2022-03-14T12:20:57.728Z] status: "404",
[2022-03-14T12:20:57.729Z] duration: "10"
[2022-03-14T12:20:57.729Z] }
This is the post which I read to get the Url for debugging: https://harrybellamy.com/posts/debugging-azure-function-event-grid-triggers-locally/
Is there anything which I'm missing in order to debug the Function by Postman?
Thank you in advanced!
Update 15.03.2022
When I remove this line (the parameter in Function1) then it works, I can debug the Function1:
[EventGrid(TopicEndpointUri = "MyEventGridTopicUriSetting", TopicKeySetting = "MyEventGridTopicKeySetting")]IAsyncCollector<EventGridEvent> outputEvents,
The referenced article says:
If you try to send the request to the URL now, you will find that it doesn’t work. This is because Event Grid requests require a ‘magic’ header for them to work.
Add the following header to your request in your request generator:
aeg-event-type: Notification
Have a look at the official Microsoft documentation on how to Manually run a non HTTP-triggered function.
To run a non HTTP-triggered function, you need a way to send a request to Azure to run the function. The URL used to make this request takes a specific form.
Host name: The function app's public location that is made up from the function app's name plus azurewebsites.net or your custom domain.
Folder path: To access non HTTP-triggered functions via an HTTP request, you have to send the request through the folders admin/functions.
Function name: The name of the function you want to run.
This works both in Azure and local.
Next to this, see Azure Function Event Grid Trigger Local Debugging which takes an alternative approach of running your Function locally and using ngrok to have Event Grid actually call into your locally running Function.

Setting up an HTTP endpoint for Azure EventGrid Webhook in my MVS Webapp?

I'm trying to create an Azure Eventgrid and Webhook response to send me inbound text messages from Azure communications services to my Azure hosted WebApp. Right now I'm having a really hard time finding documentation that shows how to create an endpoint within a Webapp controller to get the Azure webhook response to handshake and validate. I've created an endpoint within my controller that I believe should be catching the the data and processing it in a POST method, but it fails because of the arguments I'm trying to mimic. Any insight on this topic is appreciated.
I tried integrating a lot of what I found in these Docs into my app controller to try and get it to work, but I think I might be doing this all the wrong way since it says this code is for an Azure function??? I'm not entirely sure how those are used, but I tried integrating the same C# code into my controller. See Docs below:
https://learn.microsoft.com/en-us/azure/event-grid/receive-events
And here is the controller I have that is trying to imitate what I read in the docs I linked
[HttpPost("incoming")]
public async Task<IActionResult> GetFlightInfo([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest incoming,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string response = string.Empty;
BinaryData events = await BinaryData.FromStreamAsync(incoming.Body);
log.LogInformation($"Received events: {events}");
EventGridEvent[] egEvents = EventGridEvent.ParseMany(events);
foreach (EventGridEvent eventGridEvent in egEvents)
{
// Handle system events
if (eventGridEvent.TryGetSystemEventData(out object eventData))
{
// Handle the subscription validation event
if (eventData is SubscriptionValidationEventData subscriptionValidationEventData)
{
log.LogInformation($"Got SubscriptionValidation event data, validation code: {subscriptionValidationEventData.ValidationCode}, topic: {eventGridEvent.Topic}");
// Do any additional validation (as required) and then return back the below response
var responseData = new SubscriptionValidationResponse()
{
ValidationResponse = subscriptionValidationEventData.ValidationCode
};
return new OkObjectResult(responseData);
}
}
}
return new OkObjectResult(response);
}
I'd suggest to start by deploying and exploring the Azure Event Grid Viewer sample application according to the Handle SMS events for Delivery Reports and Inbound Messages tutorial.
This app is designed to consume any events generated by the Event Grid, including the SMS ones. The app utilizes SignalR, just as #roman-kiss suggests in his answer, to push the events in near real time to the user.
Once you get a good grasp of the whole flow, you can start adjusting the code to match your use case. A good first step would be adjusting the deserialization logic to take advantage of more specific models. You can get the sample JSON models for SMS events here and convert them to C#.

Response bindings for Functions with ActivityTrigger

I want to create a Durable Function that calls an Activity Function, and then returns a value using dotnet core in a v2 function app. The function will of course validate its input, so may return a successful value or it may return an invalid error: a 200 or a 400 in HTTP terms. My activity function will be something like this:
[FunctionName("MyFunc")]
public static async Task<object> Run(
[ActivityTrigger] string input,
ILogger log)
{
// return something like
return new { Status = "OK", Content = content };
}
What return type should I use for this? Should make my own DTO that would be a valid response, or is there a way of returning HttpResponse objects to the orchestrator?
I think there may be a simpler solution here. Have you considered returning a simple value if your verification passes, and throwing an exception handled by the orchestrator if your verification fails?
The ActivityTrigger binding handles both input and output. Per the docs on error handling, an activity function can return any sort of JSON-serializable object to the orchestrator, and unhandled exceptions thrown by an activity function are marshalled back to the orchestrator, where they can be handled by catch blocks.
Activity-orchestrator communication doesn't use HTTP requests and responses; it uses Azure Storage tables to record history events and Azure Storage queues to trigger activities to perform async work or wake up the orchestrator once some activity's async work has completed, respectively. Unless you specifically need an HttpResponse object somewhere in your orchestration, there's no need to wrap your activity's return value in one.

How to read parameter from Request.Body in ASP.NET Core

Context: My application is behind a central login app, whenever the user apply access to my application, my application got a http request contain the user info. And I need to retrieve the user info from the HttpRequest Body.
This is what I tried so far:
currentContext.HttpContext.Request.Query["user-name"].toString(); // got nothing
using (var reader = new StreamReader(currentContext.HttpContext.Request.Body))
{
var body = reader.ReadToEnd();
} // I can get the raw HttpRequest Body as "user-name=some&user-email=something"
Is there any method I can use to parse the parameters and values from the Request.Body?
I tried the following, got nothing either.
HttpContext.Item['user-name'] \\return nothing
Request.Form["user-name"] \\ return nothing
and the reason I am not be able to use model binding is, in the HttpRequest body, the key name is "user-name", and in c#, I can't create a variable with a "-"
Meanwhile, in the my .net 4.6 application, Request["KeyName"].toString() works just fine.
I figured out a way to convert the raw HttpRequest Body to a Query String, then read parameters from it.
Here is the code:
var queryString = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(requestBody);
string paramterValueIWant = queryString["KeyName"];
There is one problem though, when the KeyName doesn't exist in the body, it will throw an exception. So you have to null check or do a try catch.
Still I feel like there should be a better way to read the parameter, as I mentioned, in my .net 4.6 application, all I need to do is Request["KeyName"].
Assuming that we are talking about POST/PUT/PATCH call, you can use
Request.Form["KeyName"]
in your API method and set the 'contentType' of the Ajax request as application/x-www-form-urlencoded
Notice that Request is automagically available inside your method. No need to explicit call it.
When using GET/DELETE call i prefer to use
[HttpGet("{UserId}")] // api/User/{UserId}
public IActionResult Get(int UserId){
// do stuff calling directly UserId
}
Or with PUT/PATCH
[Route("User/{EntityId}/{StatusFilter}")] // api/User/{EntityId}/{StatusFilter}
[HttpPut]
public IActionResult Put(int EntityId, int StatusFilter){
// do stuff calling directly EntityId and StatusFilter
}
where you can then still take data from the Body using Request.Form["KeyName"]

How to clean up existing response in webapi?

There is a authentication library that I have to use that helpfully does things like
Response.Redirect(url, false);
inside of it's method calls. I can't change this libraries code and it's fine for MVC style apps but in angular SPA -> WebApi apps this is just awful.
I really need a 401 otherwise I get into trouble with CORS when my angular scripts, using $http, try to call out to the auth server on another domain in response to the 302, that's if it even could as the Response.Redirect also sends down the object moved html and the angle brackets cause an error to be thrown.
Since I have to make the call to the auth library first the Response.Redirect is already in the response pipeline and so I need to clean it up to remove the body content and convert the 302 into a 401. I thought I could just:
return new HttpWebResponse(StatusCode.UnAuthorized){
Content = new StringContent("data");
}
but this just gets appended to the response and doesn't replace it plus I also need the Location: header which I can't seem to access via WebApi methods.
So instead I've had to do this in my ApiController:
var ctxw = this.Request.Properties["MS_HtpContext"] as HttpContextWrapper;
var ctx = ctxw.ApplicationInstance.Context;
var url = ctx.Response.RedirectLocation;
ctx.Response.ClearContent();
return new HttpWebResponse(StatusCode.UnAuthorized){
Content = new StringContent(url);
}
But this seems terrible and counter to webapi "feel". Plus I'm tied to the controller in doing this. I can't get the wrapper in a MessageHandler for example.
What I'd like to do is monitor the response for a given route in a message handler or in an AuthorizationFilterAttribute, if its a 302, I want to read it's headers, take what I want, wipe it and replace it with my own "fresh" response as a 401. How can I do this?
You might want to write your own ActionFilter and override its OnActionExecuted method where you can access HttpActionExecutedContext. From there, you can check response code, for example, and overwrite response with whatever you want.
Ref: https://msdn.microsoft.com/en-us/library/system.web.http.filters.actionfilterattribute.onactionexecuted%28v=vs.118%29.aspx#M:System.Web.Http.Filters.ActionFilterAttribute.OnActionExecuted%28System.Web.Http.Filters.HttpActionExecutedContext%29

Categories