I'm working on a learning project in which I developed 2 APIs Buyer and Seller and containerized it using Docker. To setup communication between these 2 APIs, I wanted to use KubeMQ RPC. I referred their cookbook and wrote a simple SendRequestAsync and SubscribeRequest.
public async Task<Response> SendRequest()
{
Channel = new Channel(new ChannelParameters
{
RequestsType = RequestType.Query,
Timeout = 10000,
ChannelName = "SampleChannel",
ClientID = "MyAPI",
KubeMQAddress = "localhost:50000"
});
var result = await Channel.SendRequestAsync(new KubeMQ.SDK.csharp.CommandQuery.Request
{
Metadata = "MyMetadata",
Body = Converter.ToByteArray("A Simple Request from Buyer.")
});
//Async
if (result.Executed)
return result;
return null;
}
private void SubscribeToChannel()
{
SubscribeRequest subscribeRequest = new SubscribeRequest(SubscribeType.Queries, "MyAPI", "SampleChannel", EventsStoreType.Undefined, 0);
_responder.SubscribeToRequests(subscribeRequest, HandleIncomingRequests, HandleIncomingError);
}
private Response HandleIncomingRequests(RequestReceive request)
{
// Convert the request Body to a string
string strBody = Converter.FromByteArray(request.Body).ToString();
_logger.LogDebug($"Respond to Request. ID:'{request.RequestID}', Channel:'{request.Channel}', Body:'{strBody}'");
// Create the Response object
Response response = new Response(request)
{
Body = Converter.ToByteArray("OK"),
Error = "None",
ClientID = this.ClientID,
Executed = true,
Metadata = "OK",
};
return response;
}
private void HandleIncomingError(Exception ex)
{
_logger.LogWarning($"Received Exception :{ex}");
}
My docker.yaml reads as below
services:
kubemq:
image: kubemq/kubemq:latest
container_name: kubemq
ports:
- "8080:8080"
- "9090:9090"
- "50000:50000"
environment:
- KUBEMQ_HOST=kubemq
- KUBEMQ_TOKEN=<<MyToken>>
networks:
- backend
volumes:
- kubemq_vol:/store
networks:
backend:
volumes:
kubemq_vol:
I am simply trying to send a message and get a response. But I'm getting the below error:
Grpc.Core.RpcException: Status(StatusCode="Unavailable", Detail="failed to connect to all addresses", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"#1654251803.063997885","description":"Failed to pick subchannel","file":"/var/local/git/grpc/src/core/ext/filters/client_channel/client_channel.cc","file_line":5420,"referenced_errors":[{"created":"#1654251803.063991635","description":"failed to connect to all addresses","file":"/var/local/git/grpc/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc","file_line":398,"grpc_status":14}]}")
at Grpc.Core.Internal.AsyncCall`2.UnaryCall(TRequest msg)
at Grpc.Core.Calls.BlockingUnaryCall[TRequest,TResponse](CallInvocationDetails`2 call, TRequest req)
at Grpc.Core.DefaultCallInvoker.BlockingUnaryCall[TRequest,TResponse](Method`2 method, String host, CallOptions options, TRequest request)
at Grpc.Core.Interceptors.InterceptingCallInvoker.<BlockingUnaryCall>b__3_0[TRequest,TResponse](TRequest req, ClientInterceptorContext`2 ctx)
at Grpc.Core.ClientBase.ClientBaseConfiguration.ClientBaseConfigurationInterceptor.BlockingUnaryCall[TRequest,TResponse](TRequest request, ClientInterceptorContext`2 context, BlockingUnaryCallContinuation`2 continuation)
I'm new to docker, KubeMQ and Microservices. So I'm maybe doing something wrong here. Any inputs is appreciated.
I didn't find a lot of articles/questions on KubeMQ when compared to Kafka or RabbitMQ. I have to continue using KubeMQ since I can't change the requirement now.
Related
I have an Azure Durable function (.NET 6) triggered with httpTrigger, I'm trying to prevent start of the function based on the parameters received in the http request.
The code I tried so far :
[FunctionName(nameof(StartOrchestrator))]
public async Task<HttpResponseMessage> StartOrchestrator(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestMessage req,
[DurableClient] IDurableOrchestrationClient starter)
{
var stringContent = await req.Content.ReadAsStringAsync();
var parameter = GetParam(stringContent);
if (string.IsNullOrEmpty(filter.MyParam))
{
//Here I want to prevent start of the orchestration with a 400 bad request error
return req.CreateErrorResponse(HttpStatusCode.BadRequest, "Request doesn't contains MyParam");
}
var instanceId = await starter.StartNewAsync(nameof(RunOrchestrator), parameter);
return starter.CreateCheckStatusResponse(req, instanceId);
}
The result I'm getting :
Is there a way to do what I'm trying to ?
It should be a semaphore issue, two threads are created right here and azure functions doesn't support synchronouse operations so this is where the 500 is coming from.
The solution is to set the variable FUNCTIONS_V2_COMPATIBILITY_MODE
I have an Azure Function in C# which I have almost built. It is setup to receive data from SendGrid, verify the data and then write the data to Cosmos. Here is the code:
namespace SendGrid.CosmosLogging
{
public static class SendGrid_Logging
{
[FunctionName("SendGrid_Logging")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
HttpRequest req,
[CosmosDB(
databaseName: "portal-dev",
collectionName: "sendgrid",
ConnectionStringSetting = "CosmosDbConnectionString")]
IAsyncCollector<dynamic> documentsOut,
ILogger log)
{
try
{
log.LogInformation("SendGrid C# HTTP trigger function started processing a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
var headers = req.Headers;
var sgbody = req.Body;
//Check validity
CheckValidity(log, documentsOut, sgbody); //req.Body - has to be Stream object
}
catch (Exception e)
{
log.LogInformation("Error validating SendGrid event, error is: "+ e.ToString());
throw;
}
}
private static async void CheckValidity(ILogger log, IAsyncCollector<dynamic> documentsOut, Stream sgBody)
{
// Add a JSON document to the output container.
await documentsOut.AddAsync( new
{
//Write the sendgrid body only to the Cosmos container
sgBody
//Body = "This is something"
//name = name
});
responseMessage =
"The SendGrid HTTP triggered function executed successfully processing Id: ";//${doc_id}
log.LogInformation(responseMessage);
}
}
}
The problem is the line writing the sgBody during the CheckValidity method. This never comes through to the Cosmos container. However if it is replaced with something like:
// Add a JSON document to the output container.
await documentsOut.AddAsync( new
{
//Write the sendgrid body only to the Cosmos container
//sgBody
Body = "This is something"
//name = name
});
Then I get an output that looks like this in Cosmos:
{
"Body": "This is something",
"id": "e65c2efe-79d2-4997-b1b2-33833dbf14ce",
"_rid": "2WwUAKT2MsvdKgAAAAAAAA==",
"_self": "dbs/2WwUAA==/colls/2WwUAKT2Mss=/docs/2WwUAKT2MsvdKgAAAAAAAA==/",
"_etag": "\"12015ff1-0000-1a00-0000-61679d8c0000\"",
"_attachments": "attachments/",
"_ts": 1634180492
}
So my question is, how do I get the entire contents of the sgBody variable to write? There is something I am clearly missing here (I aren't a C# developer, but am trying my best!).
EDIT:
The format I am wanting to get as output to the Cosmos container is a properly formatted JSON body, something like:
{
"body":{
"timestamp":1631141801,
"sg_template_name":"Receiver - NonProd",
"ip":"149.72.246.163",
"tls":1,
"smtp-id":"<2MhvBnobRN-KQNdFF9cO4w#geopod-ismtpd-5-0>",
"email":"somewhere#something.com",
"response":"250 2.6.0 <2MhvBnobRN-KQNdFF9cO4w#geopod-ismtpd-5-0> [InternalId=300647724419, Hostname=SY4P282MB3809.AUSP282.PROD.OUTLOOK.COM] 289934 bytes in 0.131, 2154.683 KB/sec Queued mail for delivery",
"sg_message_id":"2MhvBnobRN-KQNdFF9cO4w.filterdrecv-75ff7b5ffb-vm78l-1-61393FA4-43.0",
"event":"delivered",
"sg_event_id":"ZGVsaXZlcmVkLTAtMjI4MDI0MTAtMk1odkJub2JSTi1LUU5kRkY5Y080dy0w",
"sg_template_id":"d-04096fb423674bdf8870dfc92eec944f",
"category":[
"develop",
"Something"
]
},
"id":"0c4143fa-d5a2-43e8-864f-82f333ace3cd",
"_rid":"SL81APS-4Q21KQAAAAAAAA==",
"_self":"dbs/SL81AA==/colls/SL81APS-4Q0=/docs/SL81APS-4Q21KQAAAAAAAA==/",
"_etag":"\"7700726c-0000-1a00-0000-61393fb20000\"",
"_attachments":"attachments/",
"_ts":1631141810
}
OK, I managed to do this myself. This will be particularly helpful to anyone using an Azure function to consume SendGrid events, which are then written to a Cosmos database, but obviously useful in other situations too. I have also added in headers, which come through as an iHeaderDictionary type, which is a bit hard to work with when you want standard JSON.
namespace SendGrid.CosmosLogging
{
public static class SendGrid_Logging
{
[FunctionName("SendGrid_Logging")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
HttpRequest req,
[CosmosDB(
databaseName: "portal-dev",
collectionName: "sendgrid",
ConnectionStringSetting = "CosmosDbConnectionString")]
IAsyncCollector<dynamic> documentsOut,
ILogger log)
{
try
{
log.LogInformation("SendGrid C# HTTP trigger function started processing a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
var headers = req.Headers;
var sgbody = req.Body;
//Create a new dictionary and fill it with the items in the existing headers. This is done due to needing to
//convert the headers to a dynamic object for the Cosmos write. Can only easily stringify a Dictionary object,
//but not the specific HttpRequest header object, so have to convert.
Dictionary<string,string> ihHeaders = new Dictionary<string,string>
{};
foreach (var item in req.Headers)
{
ihHeaders.Add(item.Key,item.Value);
}
string jsonHeaders = JsonConvert.SerializeObject(ihHeaders,Formatting.Indented);
//Use these dynamic objects for writing to Cosmos
dynamic sendgriddata = JsonConvert.DeserializeObject(requestBody);
dynamic sendgridheaders = JsonConvert.DeserializeObject(jsonHeaders);
//Check validity
CheckValidity(log, documentsOut, sendgriddata, sendgridheaders); //req.Body - has to be Stream object
}
catch (Exception e)
{
log.LogInformation("Error validating SendGrid event, error is: "+ e.ToString());
throw;
}
}
private static async void CheckValidity(ILogger log, IAsyncCollector<dynamic> documentsOut, dynamic sgBody, dynamic sgHeaders)
{
// Add a JSON document to the output container.
await documentsOut.AddAsync( new
{
//Write the sendgrid body only to the Cosmos container
headers = sgHeaders
body = sgBody
});
responseMessage =
"The SendGrid HTTP triggered function executed successfully processing Id: ";//${doc_id}
log.LogInformation(responseMessage);
}
}
This gives a nicely formatted JSON output in Cosmos:
I have a working azure functions app where I added a sub-orchestrator with activity functions inside. The rest of the app is working just fine, but not the added code. From the logs, I can see that the sub-orchestrator is reached, but when it tries to hit the activity function, it throws the error below.
Function 'TestActivity (Orchestrator)' failed with an error. Reason: Microsoft.Azure.WebJobs.FunctionFailedException: The activity function 'GetStringInput' failed: "Unable to resolve function name 'GetStringInput'.". See the function execution logs for additional details. ---> System.InvalidOperationException: Unable to resolve function name 'GetStringInput'. at Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.FunctionInstanceLogger.d__6.MoveNext() in C:\projects\azure-webjobs-sdk-script\src\WebJobs.Script.WebHost\Diagnostics\FunctionInstanceLogger.cs:line 80
Here's the client orchestrator:
[FunctionName(FUNC_INITIALIZE)]
public static async Task<HttpResponseMessage> InitializeAsync(HttpRequestMessage req,
[OrchestrationClient(TaskHub = "%Name%")]
DurableOrchestrationClientBase client,
ILogger logger)
{
var body = new BodyObject {Something:"inside"};
var instance_id = await client.StartNewAsync(FUNC_RELEASE, body);
return req.CreateResponse(HttpStatusCode.Accepted);
}
Orchestrator:
[FunctionName(FUNC_RELEASE)]
public static async Task<string> Release(
[OrchestrationTrigger] DurableOrchestrationContextBase ctx,
ILogger log)
{
var ctx_obj = ctx.GetInput<BodyObject>();
var response = await ctx.CallSubOrchestratorAsync<int>(FUNC_ORCHESTRATE_MORE, JsonConvert.SerializeObject(ctx_json));
//do stuff with response
return new ResultObject = {Result:"response"};
}
Another Orchestrator:
[FunctionName(FUNC_ORCHESTRATE_MORE)]
public static async Task<string> OrchestrateMore(
[OrchestrationTrigger]
DurableOrchestrationContextBase ctx,
ILogger log)
{
var input = ctx.GetInput<string>();
//do stuff
var inpu_json = JsonConvert.SerilializeObject(input);
var response = await ctx.CallActivityAsync<int>(FUNC_ACTIVITY, input_json);
//do stuff with response
return JsonConvert.SerializeObject(new ResultObject = {Result:"response"});
}
Activity
[FunctionName(FUNC_ACTIVITY)]
public static async Task<string> DoingActivity(
[ActivityTrigger] string input,
ILogger log)
{
//do stuff with input
return string_of_info;
}
From the portal, I can see that the activity functions exist and are enabled. Changing the signature of the activity functions to use the type DurableActivityContext yielded the same results. Submitting the request from inside the portal itself yielded a 404 not found error only on the activities added, not on existing activities.
Other info:
.NET 472,
Microsoft.Azure.WebJobs v2.3.0,
Microsoft.Azure.WebJobs.Http v1.2.0,
Microsoft.Azure.WebJobs.Extensions.DurableTask v1.8.4
Microsoft.Azure.WebJobs.ServiceBus v2.2.0
Microsoft.NET.Sdk.Functions v1.0.29
App is being deployed through Azure DevOps.
Any help would be much appreciated!
im trying to develop a game where i store a scoreboard in a text file which is stored on server (currently on localhost). I am using http get and post calls in order to communicate with the server and get and send the data that i want. Now i want to implement websockets in order to send a notification from the server to the c# client. The notification will just display on the console a message for the user, for example in mu case i want to display a message to the user each time a user is added to the scoreboard, each time the UpdateScoreBoard method is called. Based on tutorials i found online i have managed to build the following code, can anyone make it more clear for me how the i will build the websocket for the server and how i will initialize the websocket on the client? Thank you
Startup.cs (Server)
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
//deleted code
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
ReceiveBufferSize = 4 * 1024
};
app.UseWebSockets(webSocketOptions);
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(context, webSocket);
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
}
private async Task Echo(HttpContext context, WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
while (!result.CloseStatus.HasValue)
{
await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}
HttpClass.cs (Client) - where i call the http post request
public async override Task<List<Scoreboard>> UpdateScoreBoards(string username, int attempts, int seconds, DateTime date)
{
HttpResponseMessage response = null;
//Creating a new instance of object Scoreboard
//deleted code
var url = "http://localhost:5000/api/Scoreboard";
var socket_url = new Uri("ws://localhost:5000");
var exitEvent = new ManualResetEvent(false);
using (var client = new WebsocketClient(socket_url))
{
client.ReconnectTimeout = TimeSpan.FromSeconds(30);
client.ReconnectionHappened.Subscribe(info =>
Log.Information($"Reconnection happened, type: {info.Type}"));
client.MessageReceived.Subscribe(msg => Log.Information($"Message received: {msg}"));
await client.Start();
await Task.Run(() => client.Send("test"));
exitEvent.WaitOne();
}
// deleted code
}
can anyone make it more clear for me how the i will build the websocket for the server and how i will initialize the websocket on the client?
As the example that you referenced demonstrated, making use of WebSocket in ASP.NET Core, we can add the WebSockets middleware in the Configure method, then add/configure request delegate to check and handle incoming WebSocket requests.
And after transitioned a request to a WebSocket connection with AcceptWebSocketAsync() method, we can use the returned WebSocket object to send and receive messages.
In Echo method, we can also perform custom code logic to generate and send reply message/notification based on received message(s).
//received message
var mes = Encoding.UTF8.GetString(buffer, 0, result.Count);
//code logic here
//...
//create reply message
var reply_mes = $"You sent {mes}.";
byte[] reply_mes_buffer = Encoding.UTF8.GetBytes(reply_mes);
await webSocket.SendAsync(new ArraySegment<byte>(reply_mes_buffer, 0, reply_mes.Length), result.MessageType, result.EndOfMessage, CancellationToken.None);
Besides, ASP.NET Core SignalR is an open-source library that simplifies implementing real-time communication functionality. And it does support WebSockets transport and we can easily achieving push messages/notifications to all connected clients or specified subsets of connected clients.
For more information about ASP.NET Core SignalR, you can check this doc: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/websockets?view=aspnetcore-3.1
The only thing you need in your Startup is to add the UseWebsockets middleware.
Then you can define your own middleware and filter connections if they are websocket type like below:
Startup
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
app.UseWebSockets();
app.UseMiddleware<SocketWare>();
}
Middleware
public class SocketWare {
private RequestDelegate next;
public SocketWare(RequestDelegate _next) {
this.next = _next;
}
public async Task Invoke(HttpContext context) {
if (!context.WebSockets.IsWebSocketRequest) {
return;
}
var socket=await context.WebSockets.AcceptWebSocketAsync();
await RunAsync(socket);
}
private async Task RunAsync(WebSocket socket) {
try {
var client = new ChatClient(socket);
await client.RunAsync();
} catch (Exception ex) {
throw;
}
}
}
In my middleware i prefer to keep my business logic in a separate class that gets the Websocket injected in it like below:
Client
public class ChatClient
{
private Task writeTask;
private Task readTask;
private WebSocket socket;
private CancellationTokenSource cts=new CancellationTokenSource();
ChatClient(WebSocket socket)
{
this.socket=socket;
}
public async Task RunAsync()
{
this.readTask=Task.Run(async ()=>await ReadLoopAsync(cts.Token),cts.Token);
this.writeTask=Task.Run(async()=>await WriteLoopAsync(cts.Token),cts.Token);
await Task.WhenAny(this.readTask,this.writeTask);
}
public async Task WriteLoopAsync()
{
Memory<byte> buffer=ArrayPool<byte>.Shared.Rent(1024);
try {
while (true) {
var result= await this.socket.ReceiveAsync(buffer,....);
var usefulBuffer=buffer.Slice(0,result.Count).ToArray();
var raw=Encoding.Utf8.GetString(usefulBuffer);
//deserialize it to whatever you need
//handle message as you please (store it somwhere whatever)
}
} catch (Exception ex) {
//socket error handling
//break loop or continue with go to
}
}
public async Task ReadLoopAsync()
{
try {
while (true) {
var data = await this.[someMessageProvider].GetMessageAsync() //read below !!!
var bytes = Encoding.UTF8.GetBytes(data);
//send the message on the websocket
await this.socket.SendAsync(data, WebSocketMessageType.Text, true, CancellationToken.None);
}
} catch (Exception ex) {
//do incorrect message/socket disconnect logic
}
}
}
Now regarding producing messages and consuming them. In your case you could define your producers as some Controller routes like below .You would hit a route , produce a message and publish it to some message broker. I would use a Message Queue (RabbitMQ) or even a Redis Pub/Sub as a message bus.
You would publish messages from your route(s) and then consume them in your ReadLoopAsync method from WebSocketClient (look above).
Producing messages
public UpdateController:Controller
{
private IConnection
[HttpPost]
[someroute]
public void UpdateScoreboard(string someMessage)
{
this.connection.Publish("someChannel",someMessage);
}
[HttpPost]
[someotherroute]
public void DeletePlayer(string someOtherMessage)
{
this.connection.Publish("someChannel",someMessage);
}
}
Redis pub/sub Check redis pub/sub here Also check my repository
on github here in which i am
using exactly what you need (websockets, redis,pub sub)
RabbitMq Another option as a message bus is to use RabbitMQ , for more info regarding C# API
here
In Memory
You could also avoid using a third party and use some in memory data
structure like a BlockingCollection.You could inject it as a
Singleton service both in your Controller(s) and your socket
Middleware(s)
I am developing a chat application for our internal application using SignalR in a javascript angularJS client with a (self hosted for the moment) webAPI. This is in a cross domain connection.
using SignalR 2.2.1
using Owin 3.0.1
using Angular 1.5.7 if that's relevant
My problem is whenever I try to establish a connexion with my hub,
[08:26:38 GMT-0400 (Est (heure d’été))] SignalR: Auto detected cross domain url.jquery.signalR.js:82
[08:26:38 GMT-0400 (Est (heure d’été))] SignalR: Client subscribed to hub 'chathub'.jquery.signalR.js:82
[08:26:38 GMT-0400 (Est (heure d’été))] SignalR: Negotiating with 'https: localhost:44361/signalr/negotiateclientProtocol=1.5&connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D'.jquery.signalR.js:82
[08:26:38 GMT-0400 (Est (heure d’été))] SignalR: webSockets transport starting.jquery.signalR.js:82
[08:26:38 GMT-0400 (Est (heure d’été))] SignalR: Connecting to websocket endpoint 'wss: localhost:44361/signalr/connect?transport=webSockets&clientProtocol=1…kAIY9w9Q%3D%3D&connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D&tid=4'.jquery.signalR.js:82
[08:26:38 GMT-0400 (Est (heure d’été))] SignalR: Websocket opened.
the start request fails
[08:26:38 GMT-0400 (Est (heure d’été))] SignalR: webSockets transport connected. Initiating start request.
Failed to load resource: the server responded with a status of 500 ()
XMLHttpRequest cannot load https: localhost:44361/signalr/start?transport=webSockets&clientProtocol=1…D%3D&connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D&_=1471436795468. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https: localhost:3000' is therefore not allowed access. The response had HTTP status code 500.
I've tried to pin point this problem for a couple of days now and what've noticed is that in the start request call, the response is missing the 'Access-Control-Allow-Origin' header. What bugs me most is that the negotiate request and the abort request both contains the header
Negotiate Request
Request URL:https: localhost:44361/signalr/negotiate?clientProtocol=1.5&connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D&_=14714 39245326
Request Method:GET
Status Code:200 OK
Remote Address:[::1]:44361
Response Headers
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:https: localhost:3000
Cache-Control:no-cache
Content-Type:application/json; charset=UTF-8
Date:Wed, 17 Aug 2016 13:07:29 GMT
Expires:-1
Pragma:no-cache
Server:Microsoft-IIS/10.0
Transfer-Encoding:chunked
X-AspNet-Version:4.0.30319
X-Content-Type-Options:nosniff
X-Powered-By:ASP.NET
X-SourceFiles:=?UTF-8?B?QzpcVXNlcnNccmFwaGFlbC5tb3JpblxTb3VyY2VcUmVwb3NcVGVhbXdvcmtTb2x1dGlvblxUZWFtd29yay5BcGlcc2lnbmFsclxuZWdvdGlhdGU=?=
but not my start request
Start Request
Request URL:https: localhost:44361/signalr/start?transport=webSockets&clientProtocol=1.5&connectionToken=tR9V6HAxpgmW7r5Ro%2BzJzhUoJdMUcmv7eDv1ZDM%2Fq6yur21LXCZ2Dg1rrNrDGc5VBXQzfanyisyZKOcWNP7SKOl3TsTkBl3luS4I2UnYtdw8biviZ5NtcE1caoXPi3lVHaHs%2FjQnicwGVDlmJdvRzA%3D%3D&connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D&_=1471439245327
Request Method:GET
Status Code:500 Internal Server Error
Remote Address:[::1]:44361
Response Headers
Cache-Control:private
Content-Type:text/html; charset=utf-8
Date:Wed, 17 Aug 2016 13:08:05 GMT
Server:Microsoft-IIS/10.0
Transfer-Encoding:chunked
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET
X-SourceFiles:=?UTF-8?B?QzpcVXNlcnNccmFwaGFlbC5tb3JpblxTb3VyY2VcUmVwb3NcVGVhbXdvcmtTb2x1dGlvblxUZWFtd29yay5BcGlcc2lnbmFsclxzdGFydA==?=
Here is my Startup class
[assembly: OwinStartup(typeof(Teamwork.Api.Startup))]
namespace Teamwork.Api
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration {
EnableJavaScriptProxies = false,
EnableDetailedErrors = true};
map.RunSignalR(hubConfiguration);
});
}
}
}
My hub
namespace Teamwork.Api.Hubs
{
public class ChatHub : Hub
{
public void TransferMessage(string receiver, string message)
{
var name = this.Context.User.Identity.Name;
var context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
context.Clients.Group(name).AddMessage(name, message);
context.Clients.Group(receiver).AddMessage(receiver, message);
}
public override Task OnDisconnected(bool stopCalled)
{
var name = this.Context.User.Identity.Name;
Clients.All.changeStatus(name, 4);
return base.OnDisconnected(stopCalled);
}
public override Task OnConnected()
{
var name = this.Context.User.Identity.Name;
Clients.All.changeStatus(name, 0);
return Groups.Add(name, name);
}
}
}
I access it using an angularJS service provider that didn't have any problem with until i tried to subscribe to my hub
Service Provider
class ChatServiceProvider implements IChatServiceProvider {
baseUrl: string;
chatHub: HubProxy;
public setBaseUrl(url: string) {
this.baseUrl = url;
}
public $get(
$rootScope: fuse.interfaces.IRootScope
): IChatService {
var self = this;
var connection = $.hubConnection(self.baseUrl);
var chatHub = connection.createHubProxy("chatHub");
function initialize(): JQueryPromise<any> {
connection.logging = true;
return connection.start();
};
return {
chatHub: undefined,
initialize: () => {
return initialize()
},
on: function (eventName, callback) {
chatHub.on(eventName, function (result: any) {
$rootScope.$apply(function () {
if (callback) {
callback(result);
}
});
});
}
}
}
Controller
self.chatService.on("addMessage", function (name: string, message: string) {
this.addMessage(name, message);
})
this.$scope.reply = function (id: string, message: string) {
this.chatService.chatHub.invoke("transferMessage", id, message);
}
this.chatService.initialize()
.done(function (data: HubProxy) {
self.chatService.chatHub = data;
console.log("Connected");
})
.fail(function () { console.log("Failed") });
I tried to add this code to my Global.asax file without any success:
Context.Response.AppendHeader("Access-Control-Allow-Credentials", "true");
var referrer = Request.UrlReferrer;
if (Context.Request.Path.Contains("/signalr") && referrer != null){
Context.Response.AppendHeader("Access-Control-Allow-Origin", referrer.Scheme + ": " + referrer.Authority);
}
I've been looking for 4 days now for a similar issue and i can find none. As i am not proficient with webAPI and HtmlRequest, i may have miss something obvious. If not then any tips/ideas/answers would be greatly appreciated. If anything is missing, tell me and I'll add it as soon as possible.
Thanks to Holly which had a similar problem but I was too dumb to search correctly