Rebus: Unable to modify / add new headers with BeforeMessageHandled event - rebus

We are using the Rebus framework in our application and we currently have some issues adding additional headers before the messages are handled by our handlers.
In my startup:
}).Events(e =>
{
e.BeforeMessageSent += rebusEventHandler.RebusBeforeMessageSent;
e.BeforeMessageHandled += rebusEventHandler.RebusBeforeMessageHandled;
})
Catching the event:
public void RebusBeforeMessageHandled(IBus bus, Dictionary<string, string> headers, object message, IncomingStepContext context, MessageHandledEventHandlerArgs args)
{
… we are fetching mocking headers here
foreach (var mockingHeader in mockingHeaders)
{
headers.TryAdd(mockingHeader.Key, mockingHeader.Value); // values added here are gone in the next step
}
headers["my-test"] = "test"; //added for testing (also not saved)
}
When I look at the messageContext.headers in the next handler, the headers I set in the BeforeMessageHandled event are gone again. The my-test header is also gone. So it seems that the modified headers are not saved.
We also use the BeforeMessageSent event and those headers are saved as expected.
public void RebusBeforeMessageSent(IBus bus, Dictionary<string, string> headers, object message, OutgoingStepContext context)
{
//…
var messageIdentity = _mijnIdentityContext.CurrentIdentity;
var messagingIdentityJson = JsonConvert.SerializeObject(messageIdentity);
headers.Add("x-mijn -identity", messagingIdentityJson);
}
Am I correct that both events are supposed to allow us to modify (and save) the headers?

The problem is that messageContext.Headers refers to the headers of the transport message, which is the (Dictionary<string, string>, byte[]) tuple as represented by the type TransportMessage that Rebus uses to carry the incoming message.
During the execution of the incoming message pipeline, the message goes through several steps, and one of them is deserialization, which results in a new copy of the message – the "logical message" – which comes in the form of Message, which is basically a (Dictionary<string, string>, object) tuple.
The headers of Message can be accessed via messageContext.Message.Headers, so I bet you can find your header there. 🙂

Related

How to abort old request processing when new request arrives on ASP.NET MVC 5?

I have a form with hundreds of check boxes and dropdown menus (Which value of many of them are coupled together). In the action there is updating mechanism to update an object in Session. This object does all validation and coupling of values, for example if user types %50 in one input filed, we might add 3 new SelectListItem to a dropdown.
Everything works fine, but if use starts to clicking on check boxes very quick (which is the normal case in our scenario), controller get multiple posts while it is processing previous ones. Fortunately we are only interested in the last POST, so we need a way to abort\cancel on going requests when newer request from same form comes.
What I tried:
1- blocking client side to make multiple posts when server still working on previous one. It is not desirable because it makes noticeable pauses on browser side.
2- There are several solutions for blocking multiple post backs by using HASH codes or AntiForgeryToken. But they don't what I need, I need to abort on-going thread in favor of new request, not blocking incoming request.
3- I tried to extend pipeline by adding two message handlers (one before action and another after executing action) to keep a hash code (or AntiForgeryToken) but problem is still there, even I can detect there is on-going thread working on same request, I have no way to abort that thread or set older request to Complete.
Any thoughts?
The only thing you can do is throttle the requests client-side. Basically, you need to set a timeout when a checkbox is clicked. You can let that initial request go through, but then any further requests are queued (or actually dropped after the first queued request in your scenario) and don't run that until the timeout clears.
There's no way to abort a request server-side. Each request is idempotent. There is no inherent knowledge of anything that's happened before or since. The server has multiple threads fielding requests and will simply process those as fast as it can. There's no order to how the requests are processed or how responses are sent out. The first request could be the third one that receives a response, simply due to how the processing of each request goes.
You are trying to implement transactional functionality (i.e. counting only the last request) over an asynchronous technology. This is a design flaw.
Since you refuse to block on the client side, you have no method by which to control which requests process first, OR to correctly process the outcome again on the client-side.
You might actually run into this scenario:
Client sends Request A
Server starts processing Request B
Client sends Request B
Server starts processing Request B
Server returns results of Request B, and client changes accordingly
Server returns results of Request A, and client changes accordingly (and undoes prior changes resulting from Request B)
Blocking is the only way you can ensure the correct order.
Thanks for your help #xavier-j.
After playing around this, I wrote this. Hope it be useful for someone who needs same thing:
First you need add this ActionFilter
public class KeepLastRequestAttribute : ActionFilterAttribute
{
public string HashCode { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
Dictionary<string, CancellationTokenSource> clt;
if (filterContext.HttpContext.Application["CancellationTokensDictionary"] != null)
{
clt = (Dictionary<string, CancellationTokenSource>)filterContext.HttpContext.Application["CancellationTokensDictionary"];
}
else
{
clt = new Dictionary<string, CancellationTokenSource>();
}
if (filterContext.HttpContext.Request.Form["__RequestVerificationToken"] != null)
{
HashCode = filterContext.HttpContext.Request.Form["__RequestVerificationToken"];
}
CancellationTokenSource oldCt = null;
clt.TryGetValue(HashCode, out oldCt);
CancellationTokenSource ct = new CancellationTokenSource();
if (oldCt != null)
{
oldCt.Cancel();
clt[HashCode] = ct;
}
else
{
clt.Add(HashCode, ct);
}
filterContext.HttpContext.Application["CancellationTokensDictionary"] = clt;
filterContext.Controller.ViewBag.CancellationToken = ct;
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
if (filterContext.Controller.ViewBag.ThreadHasBeenCanceld == null && filterContext.HttpContext.Application["CancellationTokensDictionary"] != null) {
lock (filterContext.HttpContext.Application["CancellationTokensDictionary"])
{
Dictionary<string, CancellationTokenSource> clt = (Dictionary<string, CancellationTokenSource>)filterContext.HttpContext.Application["CancellationTokensDictionary"];
clt.Remove(HashCode);
filterContext.HttpContext.Application["CancellationTokensDictionary"] = clt;
}
}
}
}
I am using AntiForgeryToken here as key token, you can add your own custom hash code to have more control.
In the controller you will have something like this
[HttpPost]
[KeepLastRequest]
public async Task<ActionResult> DoSlowJob(CancellationToken ct)
{
CancellationTokenSource ctv = ViewBag.CancellationToken;
CancellationTokenSource nct = CancellationTokenSource.CreateLinkedTokenSource(ct, ctv.Token, Response.ClientDisconnectedToken);
var mt = Task.Run(() =>
{
SlowJob(nct.Token);
}, nct.Token);
await mt;
return null;
}
private void SlowJob(CancellationToken ct)
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(200);
if (ct.IsCancellationRequested)
{
this.ViewBag.ThreadHasBeenCanceld = true;
System.Diagnostics.Debug.WriteLine("cancelled!!!");
break;
}
System.Diagnostics.Debug.WriteLine("doing job " + (i + 1));
}
System.Diagnostics.Debug.WriteLine("job done");
return;
}
And finally in your JavaScript you need to abort ongoing requests, otherwise browser blocks new requests.
var onSomethingChanged = function () {
if (currentRequest != null) {
currentRequest.abort();
}
var fullData = $('#my-heavy-form :input').serializeArray();
currentRequest = $.post('/MyController/DoSlowJob', fullData).done(function (data) {
// Do whatever you want with returned data
}).fail(function (f) {
console.log(f);
});
currentRequest.always(function () {
currentRequest = null;
})
}

Apache Camel - from jms to http

I have a spring-boot project using Apache Camel.
I want to read a message from an activemq queue containing a file and send it to a web server.
I am trying to find the proper way to do this.
I believe I can make something like:
from("activemq:queue").bean(MyBean.class, "process")
And manually build a http request but I can't help thinking there is probably a better way to do it. Like:
from("activemq:queue").bean(MyBean.class, "process")
.setHeader(Exchange.HTTP_METHOD,constant("POST"))
.to("http://localhost:8080/test");
But I don't know how to manipulate the "exchange" to have a valid http Message.
MyBean receives an Exchange object containing a JmsMessage. I see that there is also a HTTPMessage but I don't think I should build that manually. (It requires HTTPRequest and Response objects I am not sure how to get.)
Can someone shed some light on this problem?
Update
I am going for the bean solution.
from("activemq:queue").bean(MyBean.class, "sendMultipart");
public void sendMultipart(Exchange exchange) {
ByteArrayInputStream in = new ByteArrayInputStream((byte[]) exchange.getIn().getBody());
InputStreamBody contentBody = new InputStreamBody(in, ContentType.create("application/octet-stream"), "filename");
HttpEntity entity = MultipartEntityBuilder
.create()
.addPart("file", contentBody)
.build();
HttpPost httpPost = new HttpPost("http://localhost:8080/upload/");
httpPost.setEntity(entity);
CloseableHttpClient httpClient = HttpClients.createDefault();
try {
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
System.out.println(httpResponse);
} catch (IOException e) {
e.printStackTrace();
}
}
Updated post
I found this http://hilton.org.uk/blog/camel-multipart-form-data. It allows you to leverage the camel http component.
"jms:queue/SomeQ" ==> {
process(toMultipart)
setHeader(Exchange.CONTENT_TYPE, "multipart/form-data")
process((e: Exchange) => e.getIn.setHeader(Exchange.HTTP_URI,"http://localhost:8111/foo"))
to ("http:DUMMY")
}
def toMultipart(exchange: Exchange): Unit = {
val data = exchange.in[java.io.File]
val entity = MultipartEntityBuilder.create()
entity.addBinaryBody("file", data)
entity.addTextBody("name", "sample-data")
// Set multipart entity as the outgoing message’s body…
exchange.in = entity.build
}
Side note: this would really be a nice use-case to try-out reactive streams.
Original post
I am still having some problems understanding your actual problem. Perhaps some code might help:
I am now assuming you are receiving bytes in some character encoding and want to sent it onward to a dynamically established http-endpoint.
Is the following something you are looking for (code is in camel's scala-dsl)
"jms:queue/SomeQ" ==> {
convertBodyTo(classOf[String],"UTF-32" )
process((e: Exchange) => e.in = e.in[String].toUpperCase + "!")
process((e: Exchange) => e.getIn.setHeader(Exchange.HTTP_URI,"http://localhost:8111/foo"))
to ("http:DUMMY")
}
It will be send as an HTTP POST as the body is not null.
I receive it all well on another endpoint i created to ensure the code above is correct:
"jetty:http://localhost:8111/foo" ==> {
log("received on http 8111 endpoint ${body}")
}

How to handle exceptions in Odata V4 client?

Asp.Net Web API Odata Controller Action:
public async Task<IHttpActionResult> Post(Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Products.Add(product);
await db.SaveChangesAsync();
return Created(product);
}
Odata client code:
(Odata v4 client code generator v4)
static void AddProduct(Default.Container container, ProductService.Models.Product product)
{
container.AddToProducts(product);
var serviceResponse = container.SaveChanges();
foreach (var operationResponse in serviceResponse)
{
Console.WriteLine("Response: {0}", operationResponse.StatusCode);
}
}
I would like to handle exception in a proper way inside AddProducts() Method while saving the changes.
How can I catch process the ModelState error which is sent from server return BadRequest(ModelState);?
Finally I just want to show the error message to the end uses which was sent from server.
Example:
"Product category is required."
What is the use of ODataException class? Will this help me?
Please help me.
if I understood well, you want to intercept that the ModelState is not valid, and customize the OData error that is shown to the user.
If you just want that the errors of the invalid model show up in the returned payload, you can use:
if (!ModelState.IsValid)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
If you want to fully control the exceptions handling and messages shown, I'd suggest several action points for you to accomplish this:
Intercept ModelState is not valid: you can do this with a custom ActionFilterAttribute. In there, you can override the method OnActionExecuting(HttpActionContext actionContext). You can access the ModelState through actionContext.ModelState, check if it is valid, check the fields that have errors, check the nature of these errors and the generated messages for these errors, etc. The ModelState may be not valid for different reasons, like different types than the expected, not meet requirements specified by DataAnnotations, etc. You can check more on Model validation in here. For your case, I guess the Product entity will have a Required data annotation in the Category field.
After checking all errors, you can throw a custom Exception with the error/list of errors with the messages you want. This is necessary to later intercept your custom exception and be able to return your custom message in the error payload.
Intercept your custom exception: create a custom ExceptionFilterAttribute to intercept your thrown exceptions. Overriding the
OnException(HttpActionExecutedContext filterContext) you will have access to the exception, and inspecting it you will be able to build your proper OdataError:
In here you should return the HttpResponseMessage with the BadRequest http status code and the created ODataError as a payload. As an example of very simple code (you can see that it would depend on how you build your custom exception):
public override void OnException(HttpActionExecutedContext filterContext)
{
Exception ex = filterContext.Exception;
HttpRequestMessage currentRequest = filterContext.Request;
if (filterContext.Exception.GetType() == typeof(YourCustomValidationException))
{
var oDataError = new ODataError()
{
ErrorCode = "invalidModel",
Message = "Your model is not valid.",
InnerError = new ODataInnerError()
{
TypeName = ex.TheEntityThatHasErrors
},
};
foreach (var validationError in ex.ValidationErrors)
{
oDataError.InnerError.Message += validationError + ", ";
}
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
response.RequestMessage = currentRequest;
response.Content = new StringContent(JsonConvert.SerializeObject(oDataError));
filterContext.Response = response;
}
}
Finally, you will have to setup the custom ActionFilterAttribute and the custom ErrorFilterAttribute to be used each time that a request reach your controller. You can decorate your actions, controllers, or you can set the filters for all your API controllers in the WebApiConfig, with config.Filters.Add(...);
You can find more information about all of this in here. In the end, the error and exception handling is the same for ASP.Net Web API, with or without OData; difference is that if you have an OData API, you should return errors in OData style.
Hope all this info is understandable and helps you somehow.

Problem with null object reference in Url.Action in MVC3 project

I am trying to set up a mocking scenario for my payment processor on a web site. Normally, my site redirects to the processor site, where the user pays. The processor then redirects back to my site, and I wait for an immediate payment notification (IPN) from the processor. The processor then posts to my NotifyUrl, which routes to the Notify action on my payments controller (PayFastController). To mock, I redirect to a local action, which after a conformation click, spawns a thread to post the IPN, as if posted by the processor, and redirects back to my registration process.
My mock processor controller uses the following two methods to simulate the processor's response:
[HttpGet]
public RedirectResult Pay(string returnUrl, string notifyUrl, int paymentId)
{
var waitThread = new Thread(Notify);
waitThread.Start(new { paymentId, ipnDelay = 1000 });
return new RedirectResult(returnUrl);
}
public void Notify(dynamic data)
{
// Simulate a delay before PayFast
Thread.Sleep(1000);
// Delegate URL determination to the model, vs. directly to the config.
var notifyUrl = new PayFastPaymentModel().NotifyUrl;
if (_payFastConfig.UseMock)
{
// Need an absoluate URL here just for the WebClient.
notifyUrl = Url.Action("Notify", "PayFast", new {data.paymentId}, "http");
}
// Use a canned IPN message.
Dictionary<string, string> dict = _payFastIntegration.GetMockIpn(data.paymentId);
var values = dict.ToNameValueCollection();
using (var wc = new WebClient())
{
// Just a reminder we are posting to Trocrates here, from PayFast.
wc.UploadValues(notifyUrl, "POST", values);
}
}
However, I get an 'Object reference not set to an instance of an object.' exception on the following line:
notifyUrl = Url.Action("Notify", "PayFast", new {data.paymentId}, "http");
data.paymentId has a valid value, e.g. 112, so I'm not passing any null references to the Url.Action method. I suspect I have lost some sort of context somewhere by calling Notify on a new thread. However, if I use just notifyUrl = Url.Action("Notify", "PayFast");, I avoid the exception, but I get a relative action URL, where I need the overload that takes a protocol parameter, as only that overload gives me the absolute URL that WebClient.UploadValues says it needs.
When you are inside the thread you no longer have access to the HttpContext and the Request property which the Url helper relies upon. So you should never use anything that relies on HttpContext inside threads.
You should pass all the information that's needed to the thread when calling it, like this:
waitThread.Start(new {
paymentId,
ipnDelay = 1000,
notifyUrl = Url.Action("Notify", "PayFast", new { paymentId }, "http")
});
and then inside the thread callback:
var notifyUrl = new PayFastPaymentModel().NotifyUrl;
if (_payFastConfig.UseMock)
{
// Need an absoluate URL here just for the WebClient.
notifyUrl = data.notifyUrl;
}

GWT Preventing Set-Cookie HTTP Header from Actually Setting Cookie?

I am a GWT noob but am working with someone else who is more advanced than I, and we cannot figure out why a cookie being returned by the server as a Set-Cookie HTTP header is not actually being set in the browser.
I wrote a server using Tomcat that has an authentication call. I wrote a dummy website all in HTML that uses web forms to send a request to the server with the authentication information and receives a response that contains a Set-Cookie header. This all works. It then has a second button in a different form on the same page that sends a different request to my server with some form data, and the browser automatically injects the cookie into the header as expected. Therefore, the server, for the second call, can pull the cookie header out of the request and authenticate the request. This all works and is great.
Now, for the test GWT application we have developed, I have used the code that is automatically generated when a new GWT application is developed (no AppEngine) and modified it in the following ways on the client side's EntryPoint class. I removed the TextBox for entering my name and the GWT RPC calls. I modified MyHandler so that it no longer implemented KeyPressedListener or whatever and does implement RequestCallback. I edited the contents of the onClick to create a new RequestBuilder that sends a POST with the authentication information. So far, this all works as I can watch the logs on my server and it receives the request, processes it, and places the authentication cookie in the response. Using Firebug, I can see that the response contains the Set-Cookie header with the necessary cookie information. However, the browser never actually saves this information. Unsurprisingly, a subsequent call to the server doesn't include the cookie.
GWT is just compiled into JavaScript when deployed, correct? And JavaScript can't inject itself between the HTTP response and the browser can it? I have checked the Response object that is a parameter to the onResponseReceived() call from the RequestCallback interface, and it doesn't contain any method to get access to the cookie except through the getHeaders() call. I have dumped the results of this call, though, and it doesn't exist there. Anyway, the browser should at least be getting access to the HTTP header before the code and should be grabbing and setting the cookie values before handing the code to GWT. Not only am I new to GWT, I am new to most HTTP client-side development, but am I really that far off track?
Thank you,
John
Edit:
Here is the code I ended up with. I didn't change anything else in the project.
public void onModuleLoad() {
final Button loginButton = new Button("Login");
final Button requestBuilderButton = new Button("Campaign Read");
final Label errorLabel = new Label();
// Add the nameField and sendButton to the RootPanel
// Use RootPanel.get() to get the entire body element
RootPanel.get("sendButtonContainer").add(loginButton);
RootPanel.get("sendButtonContainer").add(requestBuilderButton);
RootPanel.get("errorLabelContainer").add(errorLabel);
// Create the popup dialog box
final DialogBox dialogBox = new DialogBox();
dialogBox.setText("Remote Procedure Call");
dialogBox.setAnimationEnabled(true);
final Button closeButton = new Button("Close");
// We can set the id of a widget by accessing its Element
closeButton.getElement().setId("closeButton");
final Label textToServerLabel = new Label();
final HTML serverResponseLabel = new HTML();
VerticalPanel dialogVPanel = new VerticalPanel();
dialogVPanel.addStyleName("dialogVPanel");
dialogVPanel.add(new HTML("<b>Sending name to the server:</b>"));
dialogVPanel.add(textToServerLabel);
dialogVPanel.add(new HTML("<br><b>Server replies:</b>"));
dialogVPanel.add(serverResponseLabel);
dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
dialogVPanel.add(closeButton);
dialogBox.setWidget(dialogVPanel);
// Add a handler to close the DialogBox
closeButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
dialogBox.hide();
}
});
// Create a handler for the sendButton and nameField
class LoginHandler implements ClickHandler, RequestCallback {
/**
* Fired when the user clicks on the sendButton.
*/
public void onClick(ClickEvent event) {
dialogBox.show();
serverResponseLabel.setText(Cookies.getCookie("auth_token"));
final String url = "http://localhost:8080/app/user/auth_token";
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, URL.encode(url));
builder.setHeader("Content-Type", "application/x-www-form-urlencoded");
StringBuilder parameters = new StringBuilder();
parameters.append("user=username&password=password&client=gwt");
try {
builder.sendRequest(URL.encode(parameters.toString()), this);
}
catch(RequestException e) {
serverResponseLabel.setText(e.toString());
}
}
public void onError(Request request, Throwable exception) {
serverResponseLabel.setText("Failure.");
}
public void onResponseReceived(Request request, Response response) {
textToServerLabel.setText(Integer.toString(response.getStatusCode()));
serverResponseLabel.setText(serverResponseLabel.getText() + Cookies.getCookie("auth_token"));
}
};
class CampaignReadHandler implements ClickHandler, RequestCallback {
public void onClick(ClickEvent event) {
dialogBox.show();
final String url = "http://localhost:8080/app/campaign/read";
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, URL.encode(url));
builder.setHeader("Content-Type", "application/x-www-form-urlencoded");
StringBuilder parameters = new StringBuilder();
parameters.append("output_format=short&client=gwt&campaign_urn_list=urn:andwellness:nih");
try {
builder.sendRequest(URL.encode(parameters.toString()), this);
}
catch(RequestException e) {
serverResponseLabel.setText(e.toString());
}
}
public void onError(Request request, Throwable exception) {
serverResponseLabel.setText("Failure.");
}
public void onResponseReceived(Request request, Response response) {
textToServerLabel.setText(Integer.toString(response.getStatusCode()));
serverResponseLabel.setText(response.getText());
}
};
// Add a handler to send the name to the server
LoginHandler loginHandler = new LoginHandler();
loginButton.addClickHandler(loginHandler);
CampaignReadHandler campaignReadHandler = new CampaignReadHandler();
requestBuilderButton.addClickHandler(campaignReadHandler);
}
This is the expected behavior of browsers: http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders-method (GWT's Response#getHeaders simply calls getAllResponseHeaders and parses the string).
If you want to get cookies, you have to use the cookies object (Cookies class in GWT); which obviously filters out httponly cookies.
If you are using RequestBuilder to contact the RPC servlet that may be the problem. Especially if you are using a different host in your request, than what you have in your browser.
Say navigating to http://localhost/app
But your RequestBuilder builds a request for http://machinename/app/servlet.
If you are just using RPC without RequestBuilder you shouldn't have these problems.
As well if you are using RequestBuilder you may have to manually provide the cookies via setting that particular header
In browser client development cookies are handled on a host name basis.

Resources