Can´t add header to MassTransit message from OperationContext? - c#

I am trying to add a header to a Masstransit message containing information from the currently logged user on a WCF Web application.
So at my web app, I have the following when the app starts:
IBusControl bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
{
cfg.Host(new Uri("rabbitmq://localhost/test"), r =>
{
//...
});
cfg.ConfigureSend(
s => s.UseSendExecute(
c => c.Headers.Set(Constants.UserInfo,
OperationContext.Current.Channel.Extensions.Find<PropertyUserInfo>().Info)));
});
I am using an IoC container to create IBusControl only once in the application (singleton scope), then IBusControl gets injected into the Web service.
Note that OperationContext.Current does not exists when I am creating the servicebus, I am expecting the lambda c= > c.Headers.Set(...) to be called within the request context
Now, when the user makes a request, I am using a request-response pattern (although I dont think this matters)
var requestClient = _bus.CreateRequestClient<AddTicketRequest, AddTicketResponse>(uri, timeout);
var response = requestClient.Request(requestMessage);
The problem is, when this code is executed, and Masstransit tries to add the header to the message, OperationContext.Current is null as apparently it is running on a different thread then the user call.
Oddly enough, eventually Masstransit starts to call UseSendExecute from the right thread, and everything starts to work. And I have to restart IIS to replicate the bug again (!?).
Has anybody ever had this problem ? thanks.
I know I can add the Header when publishing the message, but I wanted to have all messages originated from the web application to have this header, and was expecting have it set up globally.

The issue https://github.com/MassTransit/MassTransit/issues/921 looks related. I'll go with the same workaround, and I will work with a wrapper around IBusControl.

Related

How to design a middleware which attaches token to every URL going to external API provider?

I'm working on building a web application which communicates with external API's. So basically I'm using React + Dotnet Core, what I need is a middleware which will attach the token to every URL leaving the app to the external API provider so that I don't need to worry about attaching the header to every URL.
From frontend, I'm calling backend controller which consist URLs to API endpoint. I need middleware between backend and API endpoint.
I come from Laravel, Nodejs world and very new to the .net world, this question may sound dumb but I have stuck on this, not sure where to begin, any hints or directions will be helpful.
Edit:
I'm storing the token in _cache using the key, the middleware needs to get token from _cache
public async Task<IActionResult> getApps()
{
var ul = Path.Combine(_baseurl_SB, "apps");
return Ok(await ul.WithOAuthBearerToken((String)_cache.Get("sb")).GetJsonAsync());
}
If i understand you question correctly, What you looking is need to alter the API (URL) which getting initiated from Client *.js ( React / angular ) need to add something to the URL before it pass through the network.
1. Interceptor -You can use the URL interceptor you can make use of the HTTP interceptor
angular HTTP interceptor
2. Service worker - Fetch event listener - which will intercept all the GET call initiated from the browser against the registers service worker domain. Service worker can read from the cache ( Cache API or IndexedDb )
If passing the token as a request header instead is an option, then this sounds a lot like you could use Named HttpClients in your Backend and configure request headers there.
In your Startup:
services.AddHttpClient("myExternalApiClient", c =>
{
c.BaseAddress = new Uri("https://myExternalApi");
c.DefaultRequestHeaders.Add("Authorization", "myBearerToken from _cache"); // or extract the Bearer token from HttpContext if the token is provided by the frontend.
});
In your Controller (inject IHttpClientFactory)
var client = _httpClientFactory.Create("myExternalApiClient");
var httpResponse = await client.GetAsync("myRelativePath");
return await httpResponse.Content.ReadAsStringAsync();

Adding middleware or alike in Azure functions v2

I need to add correlationId to my logging context and I did it on my MVC project by adding CorrelationId nuget to the project and setting up its middleware, but I could not do the same in Azure functions.
I have loaded the ICorrelationContextAccessor using Dependency injection and then set my correlationId like this:
[FunctionName("func1")]
public async Task Run([ServiceBusTrigger("mytopic", "MySubscription", Connection = "ServiceBusConnectionString")]Message message)
{
_correlationContextAccessor.CorrelationContext = _correlationContextFactory.Create(message.CorrelationId, "X-Correlation-ID");
_logger.LogInformation($"C# ServiceBus topic trigger function processed message: {message.MessageId}, {Encoding.UTF8.GetString(message.Body)}");
It works fine and I see my correlationId in the log line below and in my services in the function. The only part that I am missing is that I have logs regarding the start and finish of the function that still has no correlationId, which kind of make sense becaue when the function wants to log that it has received the message the correlationId is not set.
The short version is that you can't effect the logging code that runs before your function using the built in bindings.
You won't be able to change that first "C# Timer trigger function processed message" as the message hasn't been read at that point-- it would be the same as trying to get the correlation ID set in your MVC project before reading the incoming HTTP request.
You could add logging as soon as the message is first received by creating a custom binding. I would encourage you to consider carefully whether or not it is worth building and maintaining a custom binding to get your logging setup a few lines sooner.

404 from server events heartbeat endpoint

We are recieving proportionately low but consistent 404 from server events from a channel subscription. This seems to only be via our react interface which uses the typescript adapter here:
https://docs.servicestack.net/typescript-server-events-client
If I understand correctly, the 404 returns when the client has failed to respond with a heartbeat before the time out setting, therefore the id passed is invalid.
(HeartbeatInterval =60,IdleTimeout = 180)
Looking at application insights gives up 6 out of 330 fails in an hour:
404s logged
Digging into the request, nothing seems untoward:
Request Properties
Problem is, I cannot see anything on the server throwing errors or missing ids in the reports even though DebugMode and ReturnInnerException are both true. I have run this against a local copy and server with a c# client connected to server events feed and cannot seem to get a 404 to return while observing with fiddler.
Is there a way I can catch these server side and see why they are returning a 404 ? I can only assume something is failing and returning a 404 but it is hidden behind service stacks log handling layer. Is this reported to the logging layer as is it likely to happen regually therefore discarded ?
The 404 Response in a heartbeat is due to the subscription no longer existing, you can use the OnHeartbeatInit callback to inspect each heartbeat, e.g:
Plugins.Add(new ServerEventsFeature
{
OnHeartbeatInit = req =>
{
var subscriptionId = req.QueryString["id"];
var subscription = req.TryResolve<IServerEvents>().GetSubscriptionInfo(subscriptionId);
if (subscription == null)
{
//... subscription no longer exists
}
}
});
Which will let you inspect the subscription of 404 heartbeat responses but it wont tell you why the subscription was removed.
You can handle the OnUnsubscribe to get a callback when a subscription is unsubscribed which should help identify why the subscription was removed.
To help with debugging you can upgrade to the latest v5.4.1 on MyGet which now has embedded pdbs and source-link enabled which will let you easily debug into ServiceStack source code.

Is there a way to make Azure WebJobs ServiceBus out parameter optional?

I am building a console application that will be run as a continuous Azure WebJob. I am using the Azure WebJobs SDK via the Nuget Package Microsoft.Azure.Jobs.ServiceBus v0.3.1-beta (prerelease). I have static method that triggers on an Azure ServiceBus queue. I do some processing and then want to have the option to send a response via the output parameter to another queue. The method signature looks like this:
public static void TriggerOnQueue(
[ServiceBusTrigger(QueueName)] BrokeredMessage receivedMessage,
[ServiceBus(QueueResponseName)] out BrokeredMessage responseMessage)
{
...
}
My initial thought was to set the responseMessage to null. However, when I do this an error appears in the console window. It doesn't stop execution (so it technically does what I want it to do), but I would rather not push something throwing errors to production. Is there any non-hackish way to set a value in the response message that will not throw an error, but will not submit the message to the response queue?
If not, is there another pattern I am missing that I could use? I would prefer to use the pipeline feature of the WebJobs SDK as opposed to creating the output queue manually. I could probably submit the requests that need a response on to a separate queue and have 2 separate triggers, but with the small amount I am having this do I would rather keep it simple and together.
Thoughts?
This pattern of specifying null for an out parameter works for Azure Queues but it throws an exception for Service Bus Queues. This looks like bug in the SDK. I will open a bug for us to fix it. Thank you for reporting this issue

Should RedisMqServer/RedisMqHost be configured once per application?

I have a web app and a background service that processes messages from Redis. However, I'm unsure as to whether or not the web application's RedisMqServer should be configured as a singleton (I'm using Ninject as my IoC container). Each request that comes is will need to send messages to the background service (one-way), but I'm not sure it it should be instantiated per-request or per-application.
I was thinking that the container would be configured like this:
var clientManager = new PooledRedisClientManager();
var mqHost = new RedisMqHost(clientManager);
Bind<IMessageProducer>()
.ToMethod(_ => mqHost.MessageFactory.CreateMessageProducer())
.InRequestScope();
Or maybe the RedisMqHost/RedisMqServer isn't necessary when the messages are one-way? Therefore, reducing the configuration to:
var clientManager = new PooledRedisClientManager();
Bind<IMessageProducer>()
.ToMethod(_ => new RedisMessageProducer(clientManager))
.InRequestScope();
You don't actually need to register the IMessageService if your services don't need access to the host directly. But if you do end up using it, then Yes it should be registered as a singleton.
The only thing that needs to be registered is IMessageFactory. In this case RequestScope is the same as Ninject's default TransientScope since if it's being used, it'll only ever be resolved once per request, in your Service class.
The IMessageFactory is used in the base Service to lazily load a IMessageProducer so you can publish a message in your services with:
base.MessageProducer.Publish(new RequestDto());
Note: You're using RedisMqHost in code which process all messages on a single background thread. Changing to use RedisMqServer will create a background thread for each message type, allowing processing of different messages to happen in parallel.

Categories