I have two asp.net applications running locally in development mode. One is running HotChocolate as the GraphQL server and working well. The second application is attempting to run StrawberryShake GraphQL client and access a subscription.
The StrawberyShake documentation is pretty sparse, especially with subscriptions and I'm unable to figure out how to know when a connection error happens. I've pasted below the code that shows what I've tried so far with no success. I've added comments to show what doesn't happen.
try
{
// client is the client auto-generated by StrawberryShake.
session = client.KeyRequest
.Watch(new ServerInfoInput
{
Name = "test",
MachineIdentifier = "machine",
}).Subscribe(result =>
{
// A breakpoint in here never gets hit.
var data = result.Data;
}, () =>
{
// This writeline never gets hit.
Console.WriteLine("Complete");
});
}
catch (Exception e)
{
// An exception is never thrown.
Console.WriteLine(e.ToString());
}
In conclusion, is there a way to know if my connection to the server was successful?
Related
I am trying to handle disconnect error with reconnection so I used the nextRetryDelayInMilliseconds method for reconnection, but in this case, if the connection is lost or there are some problems with the connection, the onclose handler does not work.
I am using "#microsoft/signalr": "^7.0.2" package in Angular project
I used "withAutomaticReconnect" option, but it doesn't fire immediately. I get a callback after some timeout.
Connection builder:
this.hubconnection = new HubConnectionBuilder()
.withUrl(url)
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: () => {
this._errorState$.next(true);
return 1000;
}
})
.build();
Error handler (which doesn't fire if I use "withAutomaticReconnect"):
this.hubConnection.onclose(error => callback(error)
Expecting to handle the error immediately when there are some errors related to connection.
After test, I found the correct format should be like below.
connection.onclose(() => {
});
In your scenario, code should be
this.hubConnection.onclose((error) => {
console.error(`Something went wrong: ${error}`);
});
After testing, if you restart the signalr server, you may not get the error information, which should be normal. I don’t know if you can see more in the production environment. This is the standard usage, you can also refer to the following Link.
onclose event in signalr client
Suggestion:
The test above I test in signalr javascript client, and I also reproduce the issue, if it not works, please use #microsoft/signalr 7.0.0 version to troubleshoot problems caused by the new version.
I also using reconnectDelay property when build the connection.
var connection = new signalR.HubConnectionBuilder().withUrl("/mainHub")
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: () => {
this._errorState$.next(true);
return 1000;
},
reconnectDelay: 500 // set the reconnect delay to 500ms
})
.configureLogging(signalR.LogLevel.Trace).build();
C# learner here. I hope the question makes sense, but if not, read on!
I have an existing Azure function setup (.NET 6) that, when it receives a http trigger, will trigger an orchestrator function that uses an activity trigger to start a function that will copy all messages from an Azure Storage Queue to A Cosmos DB (the QueueStore function below). I would like to also send each of the messages to a client using SignalR via an existing SignalR Service and Hub, also in Azure.
There is a lot of documentation on creating the SignalR and negotiate functions but how do I send a message from within my already called function?
The code for the copy function is below. I'm sure those more experienced developers will spot lots of ways I can optimise things, but I am honestly just happy getting it to work at this stage. As it currently stands, the function works as expected and required but I want to add the SignalR functionality at the commented location in the code.
How can I best go about this?
[FunctionName(nameof(QueueStore))]
public static async Task<string> QueueStore([ActivityTrigger] QueueName queue, ILogger log)
{
// Get the connection string
string connectionString = Environment.GetEnvironmentVariable("QueueStorage");
try
{
CosmosClient client = new CosmosClient("some info here");
Database database = client.GetDatabase("database");
bool databaseExists = true;
try
{
var response = await database.ReadAsync();
}
catch (CosmosException ex)
{
if (ex.StatusCode.Equals(HttpStatusCode.NotFound))
{
// Does not exist
databaseExists = false;
}
}
//Instantiate a QueueClient which will be used to manipulate the queue
QueueClient queueClient = new QueueClient(connectionString, queue.Name);
QueueProperties properties = await queueClient.GetPropertiesAsync();
bool appDisconnected = false;
//string message = "Stored messages ";
if (queueClient.Exists() && databaseExists)
{
Container container = await database.CreateContainerIfNotExistsAsync(id: queue.Name,
partitionKeyPath: "/partKey", //name of the json var we want as partition key
throughput: 400
);
while (appDisconnected == false)
{
if (queueClient.GetProperties().Value.ApproximateMessagesCount == 0)
{
Thread.Sleep(100);
}
else
{
QueueMessage[] retrievedMessage = await queueClient.ReceiveMessagesAsync(1);
var fd = JsonConvert.DeserializeObject<JObject(retrievedMessage[0].Body.ToString());
if (!fd.ContainsKey("disconnected"))
{
PartitionKey partKey = new PartitionKey(queue.PartKey);
// save to db
var createdItem = await container.CreateItemAsync<JObject>(
item: fd,
partitionKey: partKey);
//######## HERE IS WHERE I WANT TO SEND THE fd Object via SignalR
//######## I have tried many different things but nothing works
await queueClient.DeleteMessageAsync(retrievedMessage[0].MessageId,
retrievedMessage[0].PopReceipt);
}
else
{
appDisconnected = true;
}
}
}
return "Copied all Items";
}
else
{
return $"The queue peek didn't work because I can't find the queue:-(";
}
}
catch (Exception ex)
{
return ex.Message;
}
}
I have tried calling a SignalR function from the orchestrator but that is adding a function outside the copy function process and would mean duplicating the calls to the queue and doesn't really help. I haven't seen any way to just send a SignalR message from the location indicated in the code. I have also tried standard .Net SignalR code, but can find no examples that have worked for me. Any pointers and suggestions would be greatly received.
Can this be done? Should I just create an entirely new function app and make a http call to that?
Trying to make my intended version of this has been causing me a lot of issues and, having not found any documentation on it, I may be architecting things wrong but thought I would ask here for any suggestions before re-writing everything as I am rather time-limited to work on this app.
Thanks in advance for any help!
I have tried adding an example Azure SignalR Function called by the orchestrator and also called from the commented area of the code and I have tried examples from the .NET documentation and the SignalR Azure Functions examples. I was hoping that there were examples of this or a tutorial somewhere I could follow but it seems like I'm trying to do something no one else has done, which might mean I'm barking up the wrong tree entirely... :-(
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.
I have been trying to register with NH from my Xamarin Android project. I managed to configure FireBase, but when I try to register with Notification Hub on the client side I am getting Unauthorized exceptions. I am sure that I have used correct connection credentials, as I have used the same in UWP project and it works there.
Here is my code:
Hub = new NotificationHub(Constants.NotificationHubName, "Endpoint=sb://namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=mykey=",
context);
try
{
Hub.UnregisterAll(registrationId);
}
catch (Exception ex)
{
Log.Error(MyBroadcastReceiver.TAG, ex.Message);
}
var tags = new List<string>() { "droid" }; // create tags if you want
// var tags = new List<string>() { };
try
{
var hubRegistration = Hub.Register(registrationId, tags.ToArray());
}
catch (Exception ex)
{
Log.Error(MyBroadcastReceiver.TAG, ex.Message);
}
I am getting unauthorized exceptions from both methods UnregisterAll and Register. Please any advice, how to solve this issue.
It indicates that your Notification hub credentials are wrong or invalid. Could you ensure that you are referring right Notification Hub and corresponding connection string.
On side note, you don’t need to call unregister api. Register API acts as CreateOrUpdate. It will take care of creating registration if it doesn’t exist.
Since you mentioned that working throuhg the MobileServiceClient works, you can register through App Service Push. Check out http://aka.ms/zumobook - chapter 5. This includes code for each platform on how to register with tags.
I have a set of WCF web services, which, if an exception occurs, will use OutgoingWebResponseContext to return the error description back to the caller.
My problem is that sometimes, when something goes wrong, the web service is shown as failed, with an ERR_CONNECTION_RESET message, rather than returning the error message, and I would like to prevent this from happening.
For example, here's my beautiful C# code which queries my Northwind database, and returns a list of Customer names.
Or rather, it would do, but I've deliberately stuck an exception in there.
public List<string> GetAllCustomerNames()
{
// Get a list of unique Customer names.
//
try
{
throw new Exception("Oh heck, something went wrong !");
NorthwindDataContext dc = new NorthwindDataContext();
var results = (from cust in dc.Customers select cust.CompanyName).Distinct().OrderBy(s => s).ToList();
return results;
}
catch (Exception ex)
{
OutgoingWebResponseContext response = WebOperationContext.Current.OutgoingResponse;
response.StatusCode = System.Net.HttpStatusCode.InternalServerError;
response.StatusDescription = ex.Message;
return null;
}
}
In my AngularJS controller, I can call this web service...
$http.get('http://localhost:15021/Service1.svc/getAllCustomerNames')
.then(function (data) {
// We successfully loaded the list of Customer names.
$scope.ListOfCustomerNames = data.GetAllCustomerNamesResult;
}, function (errorResponse) {
// The WCF Web Service returned an error
var HTTPErrorNumber = errorResponse.status;
var HTTPErrorStatusText = errorResponse.statusText;
alert("An error occurred whilst fetching Customer Names\r\nHTTP status code: " + HTTPErrorNumber + "\r\nError: " + HTTPErrorStatusText);
});
...and if this exception occurs, I do see the full exception message...
Wonderful.
For all readers, who've wanted to know a simple, generic way to return and display Exceptions from your WCF web services, there's your solution !
Okay, so now I'll remove my Exception line of code, and the web service runs fine again. The Customer data is returned to the Angular controller without any problems.
But.. if an exception occurs when connecting to the database (for example, it times out, I have the wrong database name in my connection string, etc), then the following line of code does throw an exception...
NorthwindDataContext dc = new NorthwindDataContext();
...my catch code does kick in (I put a breakpoint on it to check), and we set the StatusDescription to the Exception message..
But the response which is sent back doesn't contain either my HTTP Status number or text.
My Angular controller just receives an HTTP Status Code of 0, with no StatusMessage.
And if I hit F12 in Chrome, you can see that it actually says the web service failed, rather than returning Status Code 403, and there's no message.
So my question is simply, is there a way to prevent this failure from happening ?
Obviously, its much friendlier to return a description of what went wrong, rather than just "The web service failed miserably... but we can't tell you why."
If I can get over this issue, this'd be a nice way to make all of our WCF web services error-message friendly in our in-house apps. But of course, for production systems, sure, you wouldn't really want to bombard the user with technical exception messages.
Update, many hours later..
I found the cause, and the solution.
Are you ready for this?
The problem was that when the DataContext line threw an exception...
NorthwindDataContext dc = new NorthwindDataContext();
...the exception message had a line-feed in it.
Cannot open database "Northwind" requested by the login.
The login failed.Login failed for user 'MikesWrongUserName'.This session has been assigned a tracing ID of '1f0513d1-9ce1-47ef-9d44-1f4546eb0b73'. Provide this tracing ID to customer support when you need assistance.
That line-feed was causing the problem. If you attempt to return that in a StatusDescription field, it causes the entire web service to be reported as "failed".
So, the solution is to simply remove any line-feeds before you return the exception message.
So here, finally, is an example of how to use a try..catch in your WCF web services, so they'll always return exception messages back to the caller.
public List<string> GetAllCustomerNames()
{
// Get a list of unique Company Names.
//
try
{
NorthwindDataContext dc = new NorthwindDataContext();
var results = (from cust in dc.Customers select cust.CompanyName).Distinct().OrderBy(s => s).ToList();
return results;
}
catch (Exception ex)
{
OutgoingWebResponseContext response = WebOperationContext.Current.OutgoingResponse;
response.StatusCode = System.Net.HttpStatusCode.InternalServerError;
// If a database Exception occurs, it might have a line-feed in it,
// which will cause the web service to completely fail (no Status
// Code or Status Message will get returned.)
response.StatusDescription = ex.Message.Replace("\r\n", "");
return null;
}
}
Thanks for all of your suggestions!