Why I get Internal Server Error after publishing Azure Function? - c#

I've made an API in Azure Functions that uses an Azure Sql database.
I have this route:
[FunctionName("GetAllClassesForTeacher")]
public IActionResult GetAllClassesForTeacher(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequestMessage req,
ILogger log)
{
var headers = req.Headers;
if (headers.TryGetValues("TeacherId", out var Teacher))
{
int TeacherId = Convert.ToInt32(Teacher.First());
var result = _classDistributionServices.GetAllClassesForTeacher(TeacherId);
if (result != null)
{
return new OkObjectResult(result);
}
return new NotFoundResult();
}
return new NotFoundResult();
}
When tested localy, it works everytime. But after publishing it on Azure Portal, I get back from postman:
{
"statusCode": 500,
"message": "Internal server error",
"activityId": "f98c4112-0c31-4841-99a5-c79dffa41d86"
What I've tried/did until now:
to take the IP from Azure Function and register it in Firewalls and
virtual networks from Azure Sql Server.
I've made sure that the ConnectionString from local.settings.json it
is uploaded on portal.
I've declared this route in API Management.
But still the error persists.
I have a feeling that is still because of database firewall even if I've registered the right IP?
Can you please help me?

There a few way for you to figure out what is the problem:
1-Enable Application Insights and use the Live Metrics to capture the error
2-Use Kudu and check for more details about the error (e.g. https://myfunctionapp.scm.azurewebsites.net/azurejobs/#/functions)
3-You can also wrap your code with a try/catch and throw details of the exception in the response output.

Related

Azure SignalRTrigger not working with Azure Functions

I have created an Azure SignalR Serverless service with Azure Functions.
My client is a .NET 6 WPF Application.
The negotiate function is working as expected, and the connection gets established succesfully.
The CosmosDBTrigger, HttpTrigger and TimerTrigger functions also work as expected.
However, the SignalRTrigger isn't working and I can't figure out why.
SignalRTrigger function:
[FunctionName("SignalRTest")]
public async Task SignalRTest([SignalRTrigger("myHub", "messages", "SignalRTest")] InvocationContext invocationContext, string message, ILogger logger)
{
logger.LogInformation($"Receive {message} from {invocationContext.ConnectionId}.");
await Clients.All.SendAsync("signalRTestMessage", message);
}
Client Configuration:
connection = new HubConnectionBuilder()
.WithUrl("https://<SiteURL>.azurewebsites.net/api")
.Build();
await connection.StartAsync().ContinueWith(async (e) =>
{
try
{
await connection.InvokeAsync("SignalRTest", "TestMessage");
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
});
The exception always returns the error message:
Invocation failed, status code 404
I have configured the SignalR Upstream with the signalr_extension key generated in Azure Functions.
I have followed the official documentation on Microsoft docs but still couldn't fix the issue.
I empathize with you since we've been struggling with sending messages as well even though so much documentation makes it appear trivial. One thought I had on your situation is: Should you use the URL from the negotiate response when creating a hubConnection?
Here's our typescript example that uses a NegotiateResponse object with the URL and AccessToken returned from the negotiate HTTP call:
this.signalRService.negotiate().subscribe({
next: (negotiateResponse) => {
let options = {
accessTokenFactory: () => negotiateResponse.accessToken,
};
const connection = new signalR.HubConnectionBuilder()
.withUrl(negotiateResponse.url, options)
.build();
P.S. As mentioned we are also struggling with message sending, so this may not be a solution for you although I hope it is.

CosmosDB Azure function Trigger with SignalR output binding

I'm trying to create a Azure function with a Cosmos Trigger (in c#) that monitors changes to a CosmosDB, this seems relatively simple and I have managed to do this without trouble, I have an Azure function that logs changes to the DB to the log console. I am trying to write an output binding to send the changes to Azure SignalR but when I try this, I am met with isolated process doesn't support the assemblies required to do this. Does anyone have a very simple example of a c# Azure Cosmos Trigger function that sends the changes detected to a Cosmos DB to an Azure SignalR servicee so I can subscribe to this and report these to a client app. Any help would be greatly appreciated.
The code I found on the web for what I want to do (this is just a test function) is below:
[Function("CosmosTriggerSigR")]
public void Run([CosmosDBTrigger(
databaseName: "test",
collectionName: "testCollection",
ConnectionStringSetting = "cosmos_DOCUMENTDB",
LeaseCollectionName = "leases")]
IReadOnlyList<MyDocument> input,
[SignalR(HubName = "events", ConnectionStringSetting = "SignalRConnectionString")]
IAsyncCollector<SignalRMessage> signalRMessages,
ILogger log)
{
if (input != null && input.Count > 0)
{
_logger.LogInformation("Documents modified: " + input.Count);
_logger.LogInformation("First document Id: " + input[0].Id);
}
}
and the when trying to deploy it, it shows this error:
C:\Users\CosmosSigr1204\CosmosTriggerSigR.cs(29,14): error AZFW0001: The attribute 'SignalRAttribute' is a WebJobs attribute and not supported in the .NET Worker (Isolated Process). [C:\Users\CosmosSigr1204\CosmosSigr1204.csproj]
The terminal process "C:\Program Files\dotnet\dotnet.exe 'publish', '--configuration', 'Release', '/property:GenerateFullPaths=true', '/consoleloggerparameters:NoSummary'" terminated with exit code: 1.
I know pretty much nothing about Azure SignalR and I'm trying to muddle through so apologies if the code above isn't what it should be for what I'm trying to do.
Here is a full solution that uses the combination of services you specify: https://github.com/ealsur/serverlessazurefriday
Particularly this Function: https://github.com/ealsur/serverlessazurefriday/blob/master/src/DistributedDashboard/NotificationsTrigger.cs which ties the CosmosDBTrigger and SignalR output like so:
[FunctionName("Notifications")]
public static async Task Run(
[CosmosDBTrigger(
databaseName: "eventsDb",
collectionName: "events",
LeaseCollectionPrefix = "Notifications",
ConnectionStringSetting = "CosmosDBAzureFriday",
PreferredLocations = "%REGION%",
LeaseCollectionName = "leases")]
IReadOnlyList<Document> events,
[SignalR(HubName = "events", ConnectionStringSetting = "SignalRAzureFriday")]
IAsyncCollector<SignalRMessage> signalRMessages,
ILogger log)
{
await signalRMessages.AddAsync(new SignalRMessage()
{
Target = "console",
Arguments = new[] {
events.Select(singleEvent => JsonConvert.DeserializeObject<ConsoleLog>(singleEvent.ToString()))
}
});
}
In this case, the code will send 1 SignalR message containing all the documents that were received on the Trigger, you could opt to send 1 SignalR message per Trigger document, that is up to your application design.
In the case of this solution, the client application (browser) connects to the SignalR hub using the JS library and receives the SignalR message that contain all the Cosmos DB documents and consumes the array (reference https://github.com/ealsur/serverlessazurefriday/blob/master/src/ClientApp/scripts/site.js):
// Assuming connection is a SignalR connection created by SignalR library
// https://learn.microsoft.com/aspnet/core/signalr/javascript-client?#connect-to-a-hub
connection.on('console', function(messages){
angular.forEach(messages, function(message){
term.writeln('>> Device '+message.deviceId +' in ' + message.region + ' reports value of ' + message.value);
});
});
Where console is just the name that matches the Target on the SignalR output message.

Check in Azure Functions if client is still connected to SignalR Service

I've created negotiate and send message functions in Azure Functions (similar to the samples below) to incorporate the SignalR Service. I'm setting UserId on the SignalRMessage by using a custom authentication mechanism.
https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-signalr-service?tabs=csharp
[FunctionName("negotiate")]
public static SignalRConnectionInfo Negotiate(
[HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequest req,
[SignalRConnectionInfo
(HubName = "chat", UserId = "{headers.x-ms-client-principal-id}")]
SignalRConnectionInfo connectionInfo)
{
// connectionInfo contains an access key token with a name identifier claim set to the authenticated user
return connectionInfo;
}
[FunctionName("SendMessage")]
public static Task SendMessage(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")]object message,
[SignalR(HubName = "chat")]IAsyncCollector<SignalRMessage> signalRMessages)
{
return signalRMessages.AddAsync(
new SignalRMessage
{
// the message will only be sent to this user ID
UserId = "userId1",
Target = "newMessage",
Arguments = new [] { message }
});
}
I'd like to send a push notification if the client is no longer connected instead of adding a new object to the IAsyncCollector. I've also set up AppCenter push framework properly, but I'm facing an issue. Is there an easy way to find out which UserId is still connected to the hub? This way, I could decide to send a push. What is the recommended Microsoft guidance on this issue?
Have a look at this feature: Azure SignalR Service introduces Event Grid integration feature, where the SignalR Service emits two following event types:
Microsoft.SignalRService.ClientConnectionConnected
Microsoft.SignalRService.ClientConnectionDisconnected
More details here.

Azure Function ClaimsPrincipal locally

I'm using Azure AD Authenticated Azure Function. Everything work fine when deployed on Azure.
public static async Task<IActionResult> ExportT(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req, ClaimsPrincipal principal,
ILogger log)
{
var c = principal.Claims.Select(x => x.Value);
return new OkObjectResult(c);
}
Return :
[ "38a1e83b-c1d3-4fd7-bdc6-ef2447", "https://sts.windows.net/635e38ef-108e-4a26-b718-bbd532/", "1564501257", "1564501257", "1564505157", "42FgYFCuNWnL3ROWG9x3p4EzN3+c/6u8Gh5jV7sT0A", "pwd", "test", "test", "92.169.93", "test", "36fbab-425b-9d65-b2425ef3d9bf", "a1c1ee35-67ab-4f3a-2877c5580b1e", "ES_SALARIED", "XboeusxsxyvnjhCT_vJHkzncPE2JBU58Q50", "635e38ef-26-b718-bbd960991532", "testdedev#ins.coop", "testdedev#ins.coop", "PJ0vEo70o0G__HrwX8ghAA", "1.0" ]
But when executing locally with VS2019 I get :
[ "Admin" ]
Any idea ?
Regards,
The closest I could find to getting this to work was this using these examples from this repo (commit at the time of writing).
In the example AuthenticationService I added context.User = claimsPrincipal; after validating the claim.
From there, I can do the following on my HTTP Triggers:
[Authorize]
[FunctionName("AuthTest")]
public async Task<IActionResult> AuthTest(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "test")] HttpRequest req)
{
// Current user identity returned
// (quickly thrown together as you get a circular reference error when converting to JSON):
return new OkObjectResult(
req.HttpContext.User.Identities.Select(x =>
new {
Claims = x.Claims.Select(y => new
{
y.Type,
y.Value,
y.Issuer,
y.Properties
}),
x.Name,
x.Actor,
x.AuthenticationType,
x.NameClaimType
})
);
}
Coming in Cold?
Providing you have an access token, you can then do a GET: http://localhost:7071/api/test with a JWT token in your header.
E.g:
You will also need to
Register an
app
with Azure AD
Use the MSAL client
on your front-end to get a token
Caveats
This isn't using "easy auth". I opted for this approach as I wanted to use MSAL.NET which supports AAD V2. I found AAD V2 isn't yet supported:
At this time, AAD V2 (including MSAL) is not supported for Azure App Services and Azure Functions. Please check back for updates.
Ref
I had a real fight getting this far. Easy auth doesn't seem like an option if you need to test your apps before pushing to production. Even after this answer, I have no idea how you can make authorization as simple as a normal Web API policy approach.

Receiving custom WebHooks in a Windows Form Application

I am working to make an internal application available from an external location. To accomplish this I’m looking into different options like the use of a rest API or using a socket server which can be placed between the old application and the database on the server. One of the concerns of the end product is that if multiple users alter the same data and sent this to the server that there is the possibility that they overwrite each other’s changes. The planned approach for this is have a server sided log for data changes, and notify the user that relevant data has been altered to give them the choice to reload their form. The latter part is the issue from which my question has come forth. Unlike sockets web protocols do not work bidirectional. Potential fixes that I have found are polling, framing and WebHook of which the latter seem to be the most efficient.
However looking at the different available WebHook receiver samples they seem to have something in common. They use either MVC or WebAPI and all have an altered WebConfig in which the method config.InitializeCustomWebHooks(); has been added.
I’ve been trying to find a way to add this in by altering the Program.cs without success.
Also I’ve tried to find a method that has to do with Hooks using the HttpClient (which I’ve used for the rest of my demo application so far), WebClient and HttpResponse. However this search also did not bear any fruits. Also the only post of someone who tried to accomplish something similar (have a windows forms application listen to GitHub) that I found got a [response][1] that made me believe that webhooks and windows forms just don’t go together.
This search has ended with me wondering or:
Is it a possibility to consume an API 2 services WebHooks with a windows form application?
And if possible I would like to be informed how to do this?
For now my API 2 server has been pretty much altered according the example provided by [Asp.Net][2]
And my client has implemented the CustomWebHookHandler from the CustomReceiver from the same.
Edit for next issue:
note: both client and server contain the same packages as used in the repo as shown by Peter Bons.
The client contains a secret key within the app.config, matching the key within the subscribe method and the new Startup.cs has been called (I am able to sent echo's to the client sided hosted server) so that part is working. The registration class also matches up with the example repo
Client initiates the webhook with the following method:
public void SubscribeTo(String subscribeString)
{
HttpResponseMessage result;
// Create a webhook registration
Registration registration = new Registration
{
WebHookUri = $"{adressReceiver}/api/webhooks/incoming/custom",
Description = "A message is posted.",
Secret = "12345678901234567890123456789012",
Filters = new List<string> { "EmployeeChanged" }
};
result = client.PostAsJsonAsync(#"webhooks/registrations", registration).Result;
string resultString = result.ToString();
}
Where adress receiver is the same string that was used to start the echoable webservice.
Upon debugging the method the following actions server/client occur:
on the line containing result = the server sided debug point in CustomFilterProvider.GetFiltersAsync get's triggered.
At the return method it shows that 1 filer was returned with the Name: "EmployeeChanged" back on the client the resultString contains the StatusCode: Created
During the method the client has the system output:
ClientApp.exe Information: 0 : Registered 'IWebHookReceiver' instances with the following names: custom.
ClientApp.exe Information: 0 : Processing incoming WebHook request with receiver 'custom' and id ''.
ClientApp.exe Information: 0 : Registered configuration setting 'Custom' for ID '''.
And the server:
Application Insights Telemetry (unconfigured):
{
"name": "Microsoft.ApplicationInsights.Dev.RemoteDependency",
"time": "2017-09-12T10:16:25.8947601Z",
"tags": {
"ai.internal.nodeName": "PC-78.vericon.nl",
"ai.operation.id": "1348c1c9-4b7a5ac6dd5f1940",
"ai.cloud.roleInstance": "PC-78.vericon.nl",
"ai.internal.sdkVersion": "rdddsd:2.4.1-1362"
},
"data": {
"baseType": "RemoteDependencyData",
"baseData": {
"ver": 2,
"name": "GET /api/webhooks/incoming/custom",
"id": "|1348c1c9-4b7a5ac6dd5f1940.",
"data":
"http://localhost:55999/api/webhooks/incoming/custom?echo=e9f7b03ca8aa4226a7a9dfc99dacff16",
"duration": "00:00:00.2046329",
"resultCode": "200",
"success": true,
"type": "Http",
"target": "localhost:55999",
"properties": { "DeveloperMode": "true" }
}
}
}
Application Insights Telemetry (unconfigured):
{
"name": "Microsoft.ApplicationInsights.Dev.Request",
"time": "2017-09-12T10:14:50.0204275Z",
"tags": {
"ai.internal.nodeName": "PC-78.vericon.nl",
"ai.operation.name": "POST /api/webhooks/registrations",
"ai.operation.id": "lXKbEdh4GOU=",
"ai.location.ip": "::1",
"ai.cloud.roleInstance": "PC-78.vericon.nl",
"ai.internal.sdkVersion": "web:2.4.1-1362"
},
"data": {
"baseType": "RequestData",
"baseData": {
"ver": 2,
"id": "|lXKbEdh4GOU=.1348c1c8_",
"name": "POST /api/webhooks/registrations",
"duration": "00:01:36.1228603",
"success": true,
"responseCode": "201",
"url": "http://localhost:55997/api/webhooks/registrations",
"properties": { "DeveloperMode": "true" }
}
}
}
After a bit of research my reasoning for this output is that the server sends an echo request to the ip given by the client to see or it is a reachable destination. The code created also implies that the server found this adress because otherwise a code BadRequest would occur
So I think that the webhook get's correctly established futher on in the application I have got a method that makes a post request to the server. I put a debug point in the server sided method to see what happens (there's also is a debug point in my client handler).
after doing the client sided action that should get me in my controller the debug point within indeed get's triggered in my method:
public async Task<IHttpActionResult> Post(Employee e)
{
await this.NotifyAsync(CustomFilterProvider.EmployeeChanged, new { Employee = e });
if (EmployeeModifier.updateEmployee(e))
{
log.Info("User: " + User.Identity.Name + " Updated an employee with the following new value " + Newtonsoft.Json.JsonConvert.SerializeObject(e));
return Ok();
}
else
{
log.Error("User: " + User.Identity.Name + " failed to update an employee");
return BadRequest();
}
}
when stepping through it it follows the route towards to return Ok()
the client receives the system output:
ClientApp.exe Information: 0 : Processing incoming WebHook request with receiver 'custom' and id ''.
However the debug point within the clients handler never get's triggered.
the handler for now looks like:
class CustomWebHookHandler : WebHookHandler
{
public CustomWebHookHandler()
{
}
public override Task ExecuteAsync(string generator, WebHookHandlerContext context)
{
System.Windows.Forms.MessageBox.Show("In handler");
return Task.FromResult(true);
}
}
My client startup.cs Configuration method looks like:
public void Configuration(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();
var controllerType = typeof(WebHookReceiversController);
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
"DefaultApi",
"api/{controller}/{id}",
new { id = RouteParameter.Optional }
);
config.InitializeReceiveCustomWebHooks();
var traceWriter = config.EnableSystemDiagnosticsTracing();
traceWriter.IsVerbose = true;
traceWriter.MinimumLevel = TraceLevel.Error;
appBuilder.UseWebApi(config);
}
So that contains the var controllerType
I hope this is enough relevant code for you, my fear is that my server is only sending echo's/ping requests to my client.
But I don't know how to change that since I've tried both NotifyAsync and NotifyAllAsync, with and without Filter( in client Registration class instance). The clients OWIN hosts keeps up for sure since it is echo able and the client output shows that it actually does receive a message from the server but that's where I'm crashed.

Categories