SignalR (serverless) .NET console client not receiving messages - azure

I'm trying to learn SignalR by creating a sample .NET console application that receive messages through a serverless SignalR, via a hosted Azure function app; I've been following this tutorial https://www.nikouusitalo.com/blog/qr-code-pings-with-azure-functions-and-azure-signalr/ but even though I get the connection stablished, I never get any message when running a POST request against a given Azure function in Postman. This is what I have:
Azure Functions
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
[SignalR(HubName = "QRCodeRehash")] IAsyncCollector<SignalRMessage> signalRMessages,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
await signalRMessages.AddAsync(
new SignalRMessage
{
Target = "pingQR",
Arguments = new[] { "ping" }
});
var responseMessage = "Success";
return new OkObjectResult(responseMessage);
}
[FunctionName("negotiate")]
public static SignalRConnectionInfo GetOrderNotificationsSignalRInfo(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
[SignalRConnectionInfo(HubName = "QRCodeRehash")] SignalRConnectionInfo connectionInfo)
{
return connectionInfo;
}
}
.NET Console application to receive message
public class SignalRConnection
{
public async void Start()
{
var url = "https://sample.azurewebsites.net/api";
var connection = new HubConnectionBuilder()
.WithUrl(url)
.WithAutomaticReconnect()
.Build();
// receive a message from the hub
connection.On<string, string>("pingQR", (user, message) => OnReceiveMessage(user, message));
await connection.StartAsync();
}
private void OnReceiveMessage(string user, string message)
{
Console.WriteLine($"{user}: {message}");
}
}
And following the tutorial's steps, I just call Function1 in Postman:
I always get "200 OK" and the logs can be seen in the Azure function as well; also, the negotiate works ok as it seems to connect every time to SignalR:
I've set CORS in the Azure function app to allow anything while I get this to work:
I would appreciate your help on this; it's odd how it works for the tutorial's owner, however, maybe something was left out that I need to do on my end, so any thoughts would be highly appreciated.
Thanks a lot!
Update: Thanks #Brennan's comment, my mistake was in to providing a different number of arguments than the ones detailed in the connection in the client. It's working as expected now.

Related

HTTP Listener in a HTTP Trigger Azure Function

I have a HTTP Listener console app that works on my local machine. When I try to use it inside a HTTP Trigger Azure Function. I always get the 418 error code.
In my console app:
HttpListener listener = new HttpListener();
try
{
listener.Prefixes.Add("http://localhost:11000/");
listener.Start();
} catch (Exception e)
{ // }
do {
var ctx = listener.GetContext();
var res = ctx.Response;
var req = ctx.Request;
var reqUrl = req.Url;
var readStream = new StreamReader(req.InputStream);
var content = readStream.ReadToEnd();
Console.WriteLine(content);
// business logic
readStream.Close();
res.StatusCode = (int)HttpStatusCode.OK;
res.ContentType = "text/plain";
res.OutputStream.Write(new byte[] { }, 0, 0);
res.Close();
if (stopListener) { listener.Stop(); }
} while (listener.IsListening);
Now HTTP Trigger Function uses the HttpRequest class and that seems to give me the 418 error code. I replaced it with HttpListener() but when I add the prefix of the Azure Function string connection (on the CLI), the stream never goes through and its as if its not capturing it? Or what connection should I use? I feel like self-referencing it is the reason its not working.
Azure Function:
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpListener listener,
ILogger log,
IBinder binder)
{//same as above}
Is this the right approach to getting data from an external app? So far this has been the way I can see it working via the HTTP Listener.
Any suggestions are welcomed.
Is this the right approach to getting data from an external app?
The right way to access Data from an external source and any other source. You can create an API and use this API to access data from external sources.
For create azure function click hereby Microsoft documents.
Below sample code for access web API in azure function.
var _httpclient = new HttpClient();
var _response = await _httpclient .GetAsync(rul);
var result_= await _response .Content.ReadAsStringAsync();
its use is just like using API in C# code.
Azure Function Code:-
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Net.Http;
namespace _73093902_FunctionApp10
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
var _httpclient = new HttpClient();
var _response = await _httpclient.GetAsync("https://localhost:7101/WeatherForecast");
var result_ = await _response.Content.ReadAsStringAsync();
return new OkObjectResult(result_);
}
}
}
Debug Output:-

Route or multiple function in Azure Function App

I am working with Azure Function to consume a SOAP Service and exposing the data in 5 REST endpoints.
What I did is, uses the
class ServiceFactory {
// properties
// String path, ILogger log, IConfig mappingConfig
//constructors
public IService CreateService() {
switch(path) {
case ServicePath.service1:
return new service1(log, mappingConfig);
case ServicePath.service2:
return new service2(log, mappingConfig);
case ServicePath.service3:
return new service3(log, mappingConfig);
case ServicePath.service4:
return new service4(log, mappingConfig);
}
}
}
and then, the caller method is the azure function
[FunctionName("ServiceFunction")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "{path?}")]
HttpRequest req,
ILogger log, string? path)
{
// Validate Credential
var validatorResult = await ValidateCredential(_credential);
if (!validatorResult.IsValid)
{
var errors = validatorResult.Errors.Select(error => new
{
field = error.PropertyName,
error = error.ErrorMessage
});
return new BadRequestObjectResult(
JsonConvert.SerializeObject(
new
{
success = false,
message = errors
}
)
);
}
IService service = ServiceFactory(path, req, log, _mappingConfigurationProvider, _service, _credential).CreateService();
return await service.ServiceTask();
}
so the path is here to call different endpoints.
I am asked to implement each of the endpoint with different functions.
What will be the pros and cons here?
Pros:
Single responsibility per function, better maintainability, open closed principle
PS: Extract the common logic to a class and share it among the functions.
Cons:
I can't think any cons about this.

there any 2 ways to read all service bus messages from azure function HTTP trigger

there are 2 ways to read all the messages from service bus topic.
which one is recommended for azure functions
option 1 -
var messageReceiver = new MessageReceiver(SBConnString, QueueName, ReceiveMode.PeekLock);
Message message = await messageReceiver.ReceiveAsync();
option 2 -
static async Task ReceiveOrProcessMessagesAsync(Message message, CancellationToken token)
{
// Process the message
Console.WriteLine($"Received message: SequenceNumber:{message.SystemProperties.SequenceNumber} Body:{Encoding.UTF8.GetString(message.Body)}");
// Complete the message so that it is not received again.
// This can be done only if the queueClient is created in ReceiveMode.PeekLock mode (which is default).
await queueClient.CompleteAsync(message.SystemProperties.LockToken);
// Note: Use the cancellationToken passed as necessary to determine if the queueClient has already been closed.
// If queueClient has already been Closed, you may chose to not call CompleteAsync() or AbandonAsync() etc. calls
// to avoid unnecessary exceptions.
}
what should be used for azure functions http trigger ?
Check the azure binding table, you could find azure function doesn't support service bus input binding for now.
So if you want to receive messages in the function, you could only use the service bus trigger or the service bus sdk. The first sample you provide is the sdk and the second is the trigger.
And you said you want to receive messages in the http trigger function, so you have to use the Service Bus SDK to implement. You could refer to the below code.
public static class Function1
{
[FunctionName("Function1")]
public static async void Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string serviceBusConnectionString = Environment.GetEnvironmentVariable("servicebuscon");
var messageReceiver = new MessageReceiver(serviceBusConnectionString, "myqueue", ReceiveMode.PeekLock, null, 500);
var tempMessages = await messageReceiver.ReceiveAsync(500, TimeSpan.FromSeconds(1));
foreach (Message m1 in tempMessages)
{
log.LogInformation($"C# HTTP trigger function processed message: {Encoding.UTF8.GetString(m1.Body)}");
}
}
}
With reference to George Chen's code, you need to make a few changes in order to read all the messages from the Topic.
public static class Function1
{
[FunctionName("Function1")]
public static async void Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string serviceBusConnectionString = Environment.GetEnvironmentVariable("servicebuscon");
var messageReceiver = new MessageReceiver(serviceBusConnectionString, "myqueue", ReceiveMode.PeekLock, null, 500);
do
{
var tempMessages = await messageReceiver.ReceiveAsync(500, TimeSpan.FromSeconds(1));
foreach (Message m1 in tempMessages)
{
log.LogInformation($"C# HTTP trigger function processed message: {Encoding.UTF8.GetString(m1.Body)}");
}
}while(tempMessages!=null);
}
}
The above code has been modified with a do-while loop that receives all the messages from the Topic rather than the first messages in the queue.

403 (Forbidden) while calling one azure function from another

I need to call an azure function; fn(b), from another azure function; fn(a).
fn(a) -> fn(b)
Both these functions are in same function app. The problem is whenever I try to call (b), I get 403-Forbidden "data at the root level is invalid".
Is it possible to call an azure function from another azure function within same function app?
Function 1
public static class Function1
{
[FunctionName("Function1")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]
HttpRequestMessage req, TraceWriter log)
{
log.Info("---- C# HTTP trigger function 1 processed a request.");
UploadToF2(log);
return null;
}
private static IRestResponse UploadToF2(TraceWriter log)
{
SomeObject payload = new SomeObject();
payload.One = "One";
payload.Two = 2;
payload.Three = false;
payload.Four = 4.4;
var Fn2Url = Convert.ToString(ConfigurationManager.AppSettings["F2Url"]);
log.Info("Hitting F2 at " + Fn2Url);
var method = Method.POST;
var client = new RestClient(Fn2Url);
var body = JsonConvert.SerializeObject(payload);
var request = new RestRequest(method);
request.RequestFormat = DataFormat.Json;
request.AddHeader("Content-Type", "application/json");
request.AddBody(payload); // uses JsonSerializer
IRestResponse response = client.Execute(request);
return response;
}
}
class SomeObject
{
public string One { get; set; }
public int Two { get; set; }
public bool Three { get; set; }
public double Four { get; set; }
}
Function 2
public static class Function2
{
[FunctionName("Function2")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
log.Info("---- C# HTTP trigger function 2 processed a request.");
string payload = await req.Content.ReadAsStringAsync();
log.Info("payload == "+payload);
return null;
}
}
Additional Information:
F2Url is a fully qualified url coming from config.
I tried running both functions in localhost. It works. I.e. fn(a) can call fn(b) in localhost. However when I host both of them in Azure, fn(b) is not callable from fn(a).
I tried a hybrid test too. I.e. I kept one function in local and another one in Azure. It works this way too. I.e. I kept fn(a) in local and fn(b) in Azure, fn(b) is callable.
I tried calling fn(b) directly from Postman and again it works.
authLevel is anonymous for both functions
I have IP restrictions (Platform features > Networking > IP restrictions) applied to the Function app. When I remove IP restrictions, Function1 is able to call Function2. However keeping IP restrictions, the call is not allowed.
The only condition when fn(a) cannot call fn(b) is when both these functions are hosted in Azure.
403 (Forbidden) while calling one azure function from another
If don't add the client Ip in the IP restrictions, then you test it in you client will get 403 error. Not only call on azure function from another ,but also all functions are restricted if you don't add the client IP in the IP restrictions.
In your case, you need to add your test client Ip in the IP restrictions, then it will work.
Update:
Add the test result.
Works locally through a GET to Function1 when using:
var Fn2Url = "http://localhost:7071/api/Function2";
What value are you using in your configuration?
Call Function #2 by its full URL, since there's a front end layer that gets hit first before the request makes it to your function app. This is true even for functions calling each other within the same function app.
GET https://{func-app-name}.azurewebsites.net/api/function2
If the authLevel is not anonymous in function.json, pass in the API key as ?code= —
https://{func-app-name}.azurewebsites.net/api/function2?code={API-key}
or as a header —
GET https://{func-app-name}.azurewebsites.net/api/function2
x-functions-key: {API-key}
When running locally (Visual Studio/func host start), the value of authLevel is ignored. Looking at your decorator, AuthorizationLevel.Anonymous is present so most probably that's not it.
More on authorization keys here.
On top of that, you could do better that returning null in Function #2: 200 OK, 202 Accepted, 204 No Content, all valid choices depending on what's supposed to happen next (async/sync processing).

Can an azure job with a HTTP Trigger return a http response body?

I have an azure function like this:
[FunctionName("DoStuff")]
[return: Queue("stuff-queue")]
public static async Task<StuffContext> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
var context = await req.Content.ReadAsAsync<StuffContext>();
context.TransactionId = Guid.NewGuid();
return context;
}
It listens on a https url, deserializes the request body, and send body to the queue. Can I also have it return something (the transaction id in this case) as part of the http response.
Can I also have it return something (the transaction id in this case) as part of the http response.
Using [return: Queue("stuff-queue")] will return the information to the queue. But it could not return a response and add the information to the queue at the same time.
If you want to do, you could refer to my code.It works fine on my side.
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post")]HttpRequestMessage req,
TraceWriter log,
[Queue("stuff-queue")] IAsyncCollector<string> outputQueue)
{
log.Info("C# HTTP trigger function processed a request.");
string yourinfo = "yourinfo";
await outputQueue.AddAsync(yourinfo);
return req.CreateResponse(HttpStatusCode.OK, yourinfo);
}
For more details, you could refer to this article.

Resources