I created a Function App which is triggered via Http. The function app should publish a message to Azure Service Bus Topic. I am not getting any message published in the topic for some reasons. I have the function app successfully triggered. I am not sure what I am doing wrong here. Below is my code.
[FunctionName("MessageProcessorFunction")]
[return: ServiceBus("mytopic", Connection = "Endpoint=sb://abcsb.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=wBCZ4ssssssohbg1ZUYKw4q8cpKaoZLIG9NR28ZoUDhBG8=")]
public async Task<string> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "sms")] HttpRequest req,
ILogger log)
{
log.LogInformation("HTTP trigger function processed a request.");
return "hello World";
}
However, the console app below successfully published a message to service bus topic. Here is the code
TopicClient _topicClient = new TopicClient("Endpoint=sb://abcsb.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=wBCZ4ssssssohbg1ZUYKw4q8cpKaoZLIG9NR28ZoUDhBG8=", "whispir");
string data = JsonConvert.SerializeObject("Hello world");
Message message = new Message(Encoding.UTF8.GetBytes(data));
try
{
await _topicClient.SendAsync(message);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
local.settings.json
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"ServiceBusConnectionString": "Endpoint=sb://abcsb.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=wBCZ4okoohbg1ZUYKw4q8cpKaoZLIG9NR28ZoUDhBG8="
}
Any idea?
-Alan-
The Connection property should receive a settings key, not the actual connection string.
If you want to try it locally, you'll have to change the local.settings.json file and add the connection string to a specific key:
{
"Values": {
"ServiceBusConnectionString": "Endpoint=sb://abcsb.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=wBCZ4ssssssohbg1ZUYKw4q8cpKaoZLIG9NR28ZoUDhBG8="
}
}
Then, use it in your function like this:
[FunctionName("MessageProcessorFunction")]
[return: ServiceBus("mytopic", Connection = "ServiceBusConnectionString")]
public async Task<string> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "sms")] HttpRequest req,
ILogger log)
{
log.LogInformation("HTTP trigger function processed a request.");
return "hello World";
}
Should be working.
Let me know if not. :)
Related
I am not able to run Cosmos DB Change Feed Trigger function locally.
Cosmos DB Change Feed Trigger Azure Function:
public static class NotificationChangeFeed
{
[FunctionName("NotificationChangeFeed")]
public static async Task Run([CosmosDBTrigger(
databaseName: "FleetHubNotifications",
collectionName: "Notification",
ConnectionStringSetting = "CosmosDBConnection",
CreateLeaseCollectionIfNotExists = true,
LeaseCollectionName = "leases")]IReadOnlyList<Document> input,
[Inject] ILoggingService loggingService,
[Inject] IEmailProcessor emailProcessor)
{
var logger = new Logger(loggingService);
try
{
if (input != null && input.Count > 0)
{
foreach (Document document in input)
{
string requestBody = document.ToString();
var notification = requestBody.AsPoco<Notification>();
var result = await emailProcessor.HandleEmailAsync(notification, logger);
if (result)
{
logger.Info($"Email Notification sent successfully for file name: {document.Id}");
}
else
{
logger.Warning($"Unable to process document for Email Notification for file with name: {document.Id}");
}
}
}
}
catch (Exception ex)
{
logger.Error($"Unable to process Documents for Email Notification for Files: {input?.Count}", ex,
nameof(NotificationChangeFeed));
throw;
}
}
}
local.settings.json
{
"IsEncrypted": "false",
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"AzureWebJobsDashboard ": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"CosmosDbId": "FleetHubNotifications",
//Localhost
"CosmoDbAuthKey": "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
"CosmoDbEndpoint": "https://localhost:8081/",
"CosmosDBConnection": "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
}
}
When I press F5, it got stuck in the console window.(As shown in the below screen shot)
Also not able call http trigger functions. Getting below error while calling:
Error: connect ECONNREFUSED 127.0.0.1:7071
Any thoughts?
Yeah in that part you only get the endpoints of the http functions. The other functions are also initialized and waiting for an event of whatever type. Yo can see it here: Found the following functions, 4 http trigger the other 2 are blob trigger.
If you want to debug your NotificationChangeFeed function you will have to create a new document on the DB and have the function runing and waiting for that event. And you will see the telemetry in the console and you can debug the function.
I saw the Microsoft documentation for Azure Service Bus output binding for Azure Functions version 3. When I want to send a message to a service bus as return of the function, I can use this code:
[FunctionName("ServiceBusOutput")]
[return: ServiceBus("myqueue", Connection = "ServiceBusConnection")]
public static string ServiceBusOutput([HttpTrigger] dynamic input, ILogger log)
{
log.LogInformation($"C# function processed: {input.Text}");
return input.Text;
}
My problem started when I want to have as an output 2 messages to different Service Bus. Is it possible to output binding more than one output? In the online editor you can add more than one output. How can I do that in the code?
In the documentation there is a section of Usage, it explains what I can use as output binding. They mention ICollector<T> or IAsyncCollector<T> but I'm not sure it is what I'm looking for.
Other question is what happens in an API that returns one value to the bus and another to the user?
You could have two IAsyncCollector<T> output bindings:
[FunctionName("HttpTriggeredFunction")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
[ServiceBus("queuename1", Connection = "ServiceBusConnectionString1")] IAsyncCollector<dynamic> outputServiceBus1,
[ServiceBus("queuename2", Connection = "ServiceBusConnectionString2")] IAsyncCollector<dynamic> outputServiceBus2,
ILogger log)
{
await outputServiceBus1.AddAsync("Item1");
await outputServiceBus2.AddAsync("Item2");
return new OkObjectResult(null);
}
local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"ServiceBusConnectionString1": "Endpoint=sb://sb1.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=",
"ServiceBusConnectionString2": "Endpoint=sb://sb2.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey="
}
}
Other question is what happens in an API that returns one value to the bus and another to the user?
In the above example, it is returning (adding) a message to the Service Bus and returning a IActionResult (HTTP response) to the user.
You could write to a single service bus topic that has multiple subscribers
I am working on two functions in Azure Functions
Timer Function and Http Trigger Function
My timer functions runs every 1 hour and it executes the http function via an Http Client.
Now I do get an error Synchronous operations are disallowed
And I know how to solve this using the article on stack overflow
But I am curious as why am I getting this error?
Whats the cause of it?
The error doesn't occur when using Postman.
My Timer Code
Azure Functions Core Tools (3.0.2245 Commit hash: 1d094e2f3ef79b9a478a1621ea7ec3f93ac1910d)
Function Runtime Version: 3.0.13139.0
Host configuration file read:
{
"version": "2.0"
}
public static class DoSomeStuffTimer
{
[FunctionName("DoSomeStuffTimer")]
public static void Run([TimerTrigger("0 0 7 * * *")]TimerInfo myTimer, ILogger log)
{
try
{
log.LogInformation($"C# DoSomeStuffTimer executing at: {DateTime.Now}");
string url = Environment.GetEnvironmentVariable(EnvironmentKey.HostKey) + "/api/DoSomeStuff";
HttpClient client = new HttpClient();
client.PostAsJsonAsync(url, new DoSomeStuffRequest());
log.LogInformation($"C# DoSomeStuffTimer executed at: {DateTime.Now}");
}
catch (Exception e)
{
log.LogInformation(e.ToString());
}
}
}
My Http Code
public class DoSomeStuffFunction
{
[FunctionName("DoSomeStuffFunction")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "DoSomeStuff")]
HttpRequestMessage req,
ILogger log)
{
var response = new ContentResult {ContentType = "application/json", StatusCode = 200};
try
{
DoSomeStuffRequest
request = req.Content.ReadAsAsync<DoSomeStuffRequest>().Result;
}
catch (Exception e)
{
log.LogInformation(e.ToString());
}
}
}
Starting with ASP.NET Core 3.0 synchronous calls are disabled by default So any function running on 3.0 will encounter this when it tries to do synchronous calls
I looked into it and found the reason why it happens in your function is that ReadAsAsync<>() somewhere in it's operation does something synchronously. I am not sure exactly why it does this or why it doesn't break when you call the httptrigger directly. That'll require quite a bit more work to figure out.
To make your code work without FUNCTIONS_V2_COMPATIBILITY_MODE set to True you can use one of the other readers, for example ReadAsStreamAsync() instead.
Below you can find the method that works (I tested it locally). However, I would not recommend you call another function in your function app directly and instead follow the recommendations by Microsoft or create an abstraction that contains the logic that both functions can call on independently.
public class DoSomeStuffFunction
{
[FunctionName("DoSomeStuffFunction")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "DoSomeStuff")]
HttpRequestMessage req,
ILogger log)
{
var response = new ContentResult { ContentType = "application/json", StatusCode = 200 };
try
{
var request = await req.Content.ReadAsStreamAsync();
using (StreamReader rd = new StreamReader(request))
{
var result = JsonConvert.DeserializeObject<DoSomeStuffRequest>(await rd.ReadToEndAsync()).;
}
return new OkObjectResult(response);
}
catch (Exception e)
{
log.LogInformation(e.ToString());
return new BadRequestObjectResult("It went wrong");
}
}
}
The solution you mentioned is to set the variable FUNCTIONS_V2_COMPATIBILITY_MODE to true. We can see some information about this variable in this page.
So,did you do the operation to upgraded your function from v2 to v3 ? It may cause this issue.
Update:
I test it in my side on local visual studio. When I create the function app in visual studio, I choose azure function v3(.net core). And below is my code, it works fine without any error.
namespace FunctionApp8
{
public static class Function2
{
[FunctionName("Function2")]
public static void Run([TimerTrigger("0 */1 * * * *")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
string url = "http://localhost:7071/api/triggerFunction";
HttpClient client = new HttpClient();
client.PostAsJsonAsync(url, "");
log.LogInformation($"C# DoSomeStuffTimer executed at: {DateTime.Now}");
}
}
}
namespace FunctionApp8
{
public static class Function1
{
[FunctionName("triggerFunction")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
return (ActionResult)new OkObjectResult($"Hello");
}
}
}
I'm not sure if your function app is related to .net core 2.x. So could you please follow the steps recreate another function app(check if your visual studio has been updated to the lastest version and choose Azure Function v3 .NET core at the beginning) and test if it works fine.
I do not want to use service bus trigger using .net code http trigger function app to read messages from topic.
Run below code locally or deploy it on azure portal. send a message
into topic/sub it will read messages successfully.
wait for 1 minute
again send messages to topic/sub , it wont read messages from
topic/sub. if running locally keep fucntion running and make a http
call.
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string serviceBusConnectionString = "connectionstring";
var messageReceiver = new MessageReceiver(serviceBusConnectionString, "topicName/subscriptions/subscriptionName, ReceiveMode.PeekLock, null, 500);
var messages = await messageReceiver.ReceiveAsync(500, TimeSpan.FromSeconds(1));
if (messages.Count > 0)
{
foreach (var item in messages)
{
await messageReceiver.CompleteAsync(item.SystemProperties.LockToken);
}
}
return new OkObjectResult("");
}
}
Is there another code which will work for .net code http trigger function app?
Can someone describe me how I can configure a C# azure function which uses an HTTP input trigger and a blob storage output trigger?
Maybe also with an example code snippet and an example function.json. I don't get it to work locally with the azure functions core tools.
This is a combined HTTP triggered function with a output blob binding:
[FunctionName("HttpTriggeredFunction")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest httpRequest,
[Blob("blobcontainer", Connection = "StorageConnectionString")] CloudBlobContainer outputContainer,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
await outputContainer.CreateIfNotExistsAsync();
var requestBody = await new StreamReader(httpRequest.Body).ReadToEndAsync();
var blobName = Guid.NewGuid().ToString();
var cloudBlockBlob = outputContainer.GetBlockBlobReference(blobName);
await cloudBlockBlob.UploadTextAsync(requestBody);
return new OkObjectResult(blobName);
}
It uses the CloudBlobContainer output type to get a reference to the blob container which then enables you to use methods such as .GetBlockBlobReference("blobPath") to get a reference to a blob.
Once you have a reference to a blob, you can use different methods to upload:
cloudBlockBlob.UploadFromByteArrayAsync()
cloudBlockBlob.UploadFromFileAsync()
cloudBlockBlob.UploadTextAsync()
cloudBlockBlob.UploadFromStreamAsync()
To get it running locally, you need set some things up. Notice in my example the attribute [Blob("blobcontainer", Connection = "StorageConnectionString")]
"blobcontainer" this can be whatever you want and will be the name of the container that will be created in your storage account by this line outputContainer.CreateIfNotExistsAsync(); (if it doesn't exist already).
Connection = "StorageConnectionString" this can be a setting in your local.settings.json for the connection string of your storage account. When developing locally I would recommend setting this to "UseDevelopmentStorage=true" so that you can take advantage of the storage emulator. Then when you are ready to deploy onto Azure, you would create a setting in the function app containing the real connection string.
local.settings.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"StorageConnectionString": "UseDevelopmentStorage=true"
}
}
to make an http function that saves to Blob Storage use this code:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
public static async Task<IActionResult> Run(HttpRequest req, ILogger log,TextWriter outputBlob)
{
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
outputBlob.WriteLine(requestBody);
string result = "{ 'result': 'ok' }";
dynamic data = JsonConvert.DeserializeObject(result);
return new OkObjectResult(data);
}
You need to set the output binding:
You can then run a test posting content on the test window
Everything you need is there in the Official docs page,
(i) Http and WebHooks
(ii)Output binding blob storage
Http Trigger Sample code
[FunctionName("HttpTriggerCSharp")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
HttpRequest req, ILogger log)
Blob Storage Output binding
[FunctionName("ResizeImage")]
public static void Run(
[BlobTrigger("sample-images/{name}")] Stream image,
[Blob("sample-images-sm/{name}", FileAccess.Write)] Stream imageSmall,
[Blob("sample-images-md/{name}", FileAccess.Write)] Stream imageMedium)