I've set up Signalr on my MVC 5 website, and most of it is working fine. For example, if I try and send a test message using Javascript like so it works great:
$.connection.hub.start().done(function () {
chat.server.send("Username", "This is a test message");
});
And my C# send method in the Hub sends the message like so:
public void Send(string username, string message)
{
string name = Context.User.Identity.Name;
Clients.Group(username).getMessages("From: " + name + " - " + message);
}
The problem arises when i try to call the Send method via C#, like this:
ChatHub ch = new ChatHub();
ch.Send("Username", "This is a test message");
When it hits the method during debugging, the Context object is null and it fails.
Essentially, a user sends a message in the View, it hits a controller method, this method calls a notification method in a separate helper class, and this separate class calls the Send method of my Signalr Hub.
Would I need to get the context in the controller, and pass it to the helper class, and then to the Hub? Or is there a better, alternative approach?
Thanks.
UPDATE
I did manage to get the username using:
string name = System.Web.HttpContext.Current.User.Identity.Name;
However, even if I do this, it now throws an exception when it hits this line:
Clients.Group(username).getMessages("From: " + name + " - " + message);
So i think there's a context issue when calling Send from C# only (not from Javascript)
So it turns out from reading this: https://stackoverflow.com/a/36988086/4181058
"If you want to send messages to clients from your own code that runs outside the Hub class, you can't do it by instantiating a Hub class instance, but you can do it by getting a reference to the SignalR context object for your Hub class."
So my solution instead of this:
ChatHub ch = new ChatHub();
ch.Send("Username", "This is a test message");
Was this:
var context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
context.Clients.Group(username).getMessages("From: " + name + " - " + message);
Related
I have created a webmethod in a C# webservice that listens for Docusign to call when an Envelope status changes:
[WebMethod]
public void DocuSignConnectUpdate(DocuSignEnvelopeInformation DocuSignEnvelopeInformation)
{
//Check if null
if (DocuSignEnvelopeInformation == null)
{
File.WriteAllText("C:\\websites\\DataAPI\\datalog.txt", "Data: " + "Data is null");
}
else
{
string envelopeId = "";
try
{
//Write a line in a file
File.WriteAllText("C:\\websites\\DataAPI\\datalog.txt", "Data: " + DocuSignEnvelopeInformation.ToString());
//Get some data out
envelopeId = DocuSignEnvelopeInformation.EnvelopeStatus.EnvelopeID;
//Write Data to a file
File.WriteAllText("C:\\websites\\DataAPI\\innerdatalog.txt", "Data: " + DocuSignEnvelopeInformation.ToString());
}
catch (Exception ex)
{
// could not serialize
File.WriteAllText("C:\\websites\\DataAPI\\errorlog.txt", "Exception: " + ex.Message);
throw new SoapException(ex.Message, SoapException.ClientFaultCode);
}
}
The issue I am having is that DocuSignEnvelopeInformation argument is not being set when called, so the code keeps terminating at the if==null statement. When I run the envelope data to the API using SoapUI everything works correctly. Any ideas what I'm missing would be appreciated.
EDIT: I wanted to Add the Interface here too since I forgot it originally
[ServiceContract(ConfigurationName = "IOperations", Namespace = "https://www.docusign.net/API/3.0")]
public interface IOperations
{
[OperationContract(Action = "DocuSignConnectListener/Operations/DocuSignConnectUpdate")]
[XmlSerializerFormat]
string DocuSignConnectUpdate(DocuSignEnvelopeInformation DocuSignEnvelopeInformation);
}
When a DocuSign webhook is set to use SOAP mode, the notification is sent as a SOAP request to your server (your listener).
If SOAP mode is off, then the notification is sent as a regular POST request with an XML body.
In your question, you say that
When I run the envelope data to the API using SoapUI everything works correctly
So it sounds like everything is worked as designed.
Ok I finally figured this out, turns out it just wasn't pretty enough so I added a decoration specifically:
[SoapDocumentMethod("http://tempuri.org/DocuSignConnectUpdate",
RequestNamespace = "http://tempuri.org",
ResponseNamespace = "http://tempuri.org",
Use = System.Web.Services.Description.SoapBindingUse.Literal,
ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
on the method and now everything works like its supposed too. Now that I look at it, it makes a lot more sense.
Let's say I have multiple services. One service is calling another service to get something. That service also gets some information from a third party vendor. Let's say the third party vendor returned a Too Many Requests message with 429 status code. The method that calls the third party vendor is used by lots of other methods and can be called directly via GET Request or within another Http Request.
When I detect the 429 status code, I was throwing a custom exception (let's say TooManyRequestsException) and using the Mapper to send the code to the requester.
Config.MapExceptionToStatusCode.Add(typeof(TooManyRequestsException),429);
It works if the method is called directly. But obviously I forgot the fact that this method was called by many other methods inside the service and all other methods are wrapping this exception with generic System.Exception objects with custom messages inside their catch blocks.
What other options do I have other than changing all the methods that wrap the exception with System.Exception? I tried to go over http://docs.servicestack.net/error-handling but couldn't really find a way that would help me, or couldn't make sense of it.
I recommend looking at Overriding OnExceptionTypeFilter in your AppHost which will let you apply custom logic to inspect the Exception and customize the ResponseStatus returned:
public override void OnExceptionTypeFilter(
Exception ex, ResponseStatus responseStatus)
{
var argEx = ex as ArgumentException;
var isValidationSummaryEx = argEx is ValidationException;
if (argEx != null && !isValidationSummaryEx && argEx.ParamName != null)
{
var paramMsgIndex = argEx.Message.LastIndexOf("Parameter name:");
var errorMsg = paramMsgIndex > 0
? argEx.Message.Substring(0, paramMsgIndex)
: argEx.Message;
responseStatus.Errors.Add(new ResponseError
{
ErrorCode = ex.GetType().Name,
FieldName = argEx.ParamName,
Message = errorMsg,
});
}
}
Actually i am running cometd-demo server in my local using maven jetty run shown in the doc https://docs.cometd.org/current/reference/ and trying to subscribe and publish something in a broadcast channel. Using Groovy script shown below,
ClientSessionChannel.MessageListener mylistener = new Mylistener();
def myurl = "http://localhost:8080/cometd/"
MyHttpClient httpClient = new MyHttpClient();
httpClient.start()
Map<String, Object> options = new HashMap<String, Object>();
ClientTransport transport = new LongPollingTransport(options, httpClient);
BayeuxClient client = new BayeuxClient(myurl, transport)
println 'client started on URL : '+ client.getURL()
client.handshake ( new ClientSessionChannel.MessageListener() {
public void onMessage(ClientSessionChannel channel, Message message) {
if (message.isSuccessful()) {
println 'Handshake Message : ' + message
}
}
})
boolean handshakecheck = client.waitFor(1000, BayeuxClient.State.CONNECTED);
println 'Handshake check : '+ handshakecheck
client.batch( new Runnable() {
public void run() {
client.getChannel("/foo/hello").subscribe(
new ClientSessionChannel.MessageListener() {
public void onMessage(ClientSessionChannel channel,
Message message) {
println "subscribed : "+ message
}
})
}
});
The program Output :
client started on URL : http://localhost:8080/cometd/
Handshake Message : [minimumVersion:1.0, clientId:fv0ozxw8cb5e11vtlwpacm7afp, supportedConnectionTypes:[websocket, long-polling, callback-polling], advice:[reconnect:retry, interval:0, maxInterval:10000, timeout:20000], channel:/meta/handshake, id:1, version:1.0, successful:true]
Handshake check : true
Here I can't get the subscribed message as in the code. But in server log It prints like shown below,
2018-02-12 20:30:32,687 qtp2069584894-17 [ INFO][examples.CometDDemoServlet] Monitored Subscribe from fv0ozxw8cb5e11vtlwpacm7afp,last=0,expire=0 for /foo/hello
Update 1:
Also i can't subscribe with callback method, i get the message as [channel:/meta/subscribe, id:4, subscription:/foo/hello, error:403:denied_by_not_granting:create_denied, successful:false]. I don't know what i am doing wrong ? I am just following the documentation steps. Thanks in advance.
The ClientSessionChannel.MessageListener that you pass to the subscribe(...) method will be invoked whenever a message will be published on channel /foo/hello.
Your program never publishes a message on that channel, so the listener is never invoked, therefore in your code subscribed is never printed.
You want to double check what version of the subscribe() method you want to use, as there are 2 versions.
The single parameter version takes a listener, while the two parameter version takes a listener and a callback.
Guessing from your code, you want the subscribed log line be in the callback not in the listener, so you just need to change your code to use the two parameter version of the subscribe() method.
Also, pay attention to the fact that if the JVM exits at the end of your groovy script, then that client will be gone and will never receive any message.
I have an Azure WebJobs project which handles a number of time-consuming tasks triggered by website actions. It works fine.
But the mapping from message to method call uses magic strings:
public class SomeClass
{
public async Task ProcessMessage(
[ QueueTrigger( "%" + nameof( ContainerQueueConstants.FilteredVoterFiles ) + "%" ) ] AgencyOutreachMessage
msg,
TextWriter azureLogWriter
)
{
PhaseNames.SetNames( "Exporting Data", "Job Completed" );
await ExecuteFromMessage( msg, azureLogWriter, Launch );
}
}
public class ContainerQueueConstants
{
public const string ImportFile = "import-file";
public const string VoterTraits = "voter-traits";
public const string Voter = "voter";
public const string FilteredVoterFiles = "filtered-voter-files";
}
I'd like to get away from using hard-coded strings for queue names. Ideally, I'd like to be able to route a message to a particular method based on the value of a property contained in the message.
But I'm not sure if that's even possible, at least in the 1.1.x version of the WebJobs SDK.
Suggestions or advice appreciated.
I suggest using N CloudQueue instances to monitor N different Storage Queues. Since you're doing this in a WebJob, you will probably do this as a continuous webjob and have to perform the polling for each queue yourself. You will also have to take responsibility for removing successfully processed messages.
The QueueTriggerAttribute has built-in support for deadlettering. I do not believe that there is automatic deadlettering support if you do not use the QueueTriggerAttribute.
What is the difference between Gateway and Service Activator as Message Endpoints (in terms of Enterprise Integration Patterns)?
http://eaipatterns.com/
Typically, a service activator is used to invoke a local service, in such a manner that the service doesn't know it's being invoked from a messaging system.
A gateway s typically an entry or exit point for the messaging system.
The service activator calls a method on an object where the application developer provides the implementation. Spring Integration takes care of calling the method with messages from the input channel and shunting the results off to some output channel. The application-provided code can do some arbitrary work.
For the gateway the application developer provides only an interface, its implementation is provided by Spring.
An appendix to the Spring Integration documentation includes a Cafe example where Barista is a service called through a service activator, and Cafe is a gateway.
The application's main method looks up a Cafe object from the Spring application context and calls placeOrder, on it, passing an Order in as an argument:
public static void main(String[] args) {
AbstractApplicationContext context = null;
if (args.length > 0) {
context = new FileSystemXmlApplicationContext(args);
}
else {
context = new ClassPathXmlApplicationContext(
"cafeDemo.xml", CafeDemo.class);
}
Cafe cafe = (Cafe) context.getBean("cafe");
for (int i = 1; i <= 100; i++) {
Order order = new Order(i);
order.addItem(DrinkType.LATTE, 2, false);
order.addItem(DrinkType.MOCHA, 3, true);
cafe.placeOrder(order);
}
}
The Cafe is an interface that the application does not provide an implementation for. Spring generates an implementation that sends the Orders passed into it down the input channel called "orders".
Further down the pipeline, there are two service-activators that have a reference to the Barista. The Barista is a POJO that has code for creating a Drink like this:
public Drink prepareHotDrink(OrderItem orderItem) {
try {
Thread.sleep(this.hotDrinkDelay);
System.out.println(Thread.currentThread().getName()
+ " prepared hot drink #" + hotDrinkCounter.incrementAndGet()
+ " for order #" + orderItem.getOrder().getNumber()
+ ": " + orderItem);
return new Drink(orderItem.getOrder().getNumber(),
orderItem.getDrinkType(),
orderItem.isIced(), orderItem.getShots());
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
}
The Barista receives drink orders from the service-activator's input channel and has a method called on it that returns a Drink, which gets sent down the service-activator's output channel, "preparedDrinks".
For me the gateway is used for making an abstraction and provide a normalised API for one or more back-end services.
E.g You have 5 providers which are using different ways to interface with you (SOAP, REST, XML/http, whatever), but your client want only one way to get the data (let says json/REST).
The gateway will convert the json request form your client and convert them to the right backend with its own protocol, after it will convert the backend response to json to give the response to your client.
The service activator acts more as a trigger on an incoming message. Let say your activator poll a database for incoming message and then when the condition meet the "activation" it calls the underlying service.
Info for gateway here.
Info for Activator here.