Overloading actions wcfservice - c#

Maybe i am mixing up some things but i can not find any questions or documentation on overloading actions for a wcf service recieving SOAP messages.
The goal: I have 3 SOAP messages coming in to my wcf service with the same actionname on the same endpoint. This is fixed and i can not change this.
I would excpect the following wcf interface would work:
[OperationContract(Action = "urn:oasis:names:tc:SPML:2:0:req/active", Name = "addRequest")]
void Add(data data);
[OperationContract(Action = "urn:oasis:names:tc:SPML:2:0:req/active", Name = "modifyRequest")]
void Modify(psoID psoID, modification modification);
[OperationContract(Action = "urn:oasis:names:tc:SPML:2:0:req/active", Name = "deleteRequest")]
void Delete(psoID psoID);
The problem: If i only have one operationalcontract like this my service works but if i have multiple operationalcontracts the following error will popup: `
500System.ServiceModel.ServiceActivationException
I believe it can not have multiple operational contracts with the same actionname. I also believe this should be possible because i am replacing a soap service that does handle all 3 messages with the same actionname. (wcf and soap shouldn't be that far appart?)
I added the operational names in order to fix the problem but without luck.
Any help would be appriciated. Thanks!

The Action property indicates the address the client request, which will be sent to the server and determines the method to be called on the server-side.
Here is a client request captured by the Fiddler.
POST http://10.157.13.69:21011/ HTTP/1.1
Content-Type: text/xml;charset=utf-8
SOAPAction: "urn:oasis:names:tc:SPML:2:0:req/active"
Host: 10.157.13.69:21011
Content-Length: 162
Expect: 100-continue
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
The SOAPAction HTTP header is the Action name of the operation.
The Name property determined the name of the practical method on the client-side.
ServiceReference1.ServiceClient client = new ServiceClient();
client.addRequest(23);
Thereby, unless we change the WCF web service from SOAP web service to Rest API, otherwise, this feature cannot be achieved because the SOAP web service addressing style depends on the Action field.
Namely, we need to change the service to Restful API by using Webhttpbinding.
[OperationContract(Action = "urn:oasis:names:tc:SPML:2:0:req/active", Name = "addRequest")]
[WebGet]
void Add(int data);
Feel free to let me know if there is anything I can help with.

Related

Remove Host header in HttpClient request

I'm using the HttpClient class to send some data to specific host. I just want to send a pure header without any additional lines in it like ("Host: http"). So this line is the last to be removed from the header, but I don't know how.
The code:
HttpRequestMessage msg = new HttpRequestMessage(HttpMethod.Post, aUrl);
msg.Headers.Clear();
msg.Headers.Remove("Host");
msg.Headers.ExpectContinue = false;
Encoding encoding = ConfiguratorASUST.Instance.Encoding ?? Encoding.GetEncoding(ConfiguratorASUST.ENCODING_DEFAULT);
msg.Content = new StringContent(aStr, encoding);
_client.SendAsync(msg);
The result header in Fiddler:
POST http://http//localhost.fiddler:60001/POS/POSTELESPIS HTTP/1.1
Content-Type: text/plain; charset=windows-1251
Host: http
This line Host: http needs to be removed from the message's header. But how on earth can I do that?! I tried the following:
msg.Headers.Clear();
msg.Headers.Remove("Host");
To no avail. Actually I also see the header Proxy-Connection: Keep-Alive being added.
If you carefully inspect your URL, it looks like your it is wrong anyway: http://http// - is your host really named http, and do you really need two slashes after it? Anyway if you fix that, the Host header will carry localhost.fiddler:60001.
By removing the Host header, you're essentially downgrading your request to HTTP/1.0.
You can set the HTTP version in the HttpRequestMessage as explained in Set HTTP protocol version in HttpClient:
msg.Version = HttpVersion.Version10;
But when using Fiddler, it acts as a proxy, and forwards your request as an HTTP/1.1 request - including the host header again. You can also alter the request in Fiddler. This is explained in How do I prevent fiddler from insering "Host" HTTP header?, but note the bold text, emphasis mine:
Per the RFC, as a HTTP/1.1 proxy, Fiddler is required to add a Host header.
It's not clear why this is problematic-- any server that has a problem with this is, by definition, buggy and should be fixed.
You can remove the header if you'd like (although doing so can cause problems elsewhere). Click Rules > Customize Rules. Scroll to OnBeforeRequest and add the following:
if (oSession.oRequest.headers.HTTPVersion == "HTTP/1.0")
{
oSession["x-overridehost"] = oSession.host;
oSession.oRequest.headers.Remove("Host");
}

BrokerProperties REST header values not showing up in .NET client code

I'm writing two small pieces of C# code. The first is for a client-side Portable Class Library. All it does is send messages to an Azure Service Bus topic via the Azure Service Bus REST API, using HttpClient.
I populate the BrokerProperties header on the REST call with valid JSON, and I expect that on the server side, when I receive the message through a subscription, that I'll get my instance of BrokeredMessage.Properties populated with the values I sent from the client.
The one problem I've had on this side is that the documentation says to set Content-Type to application/atom+xml;type=entry;charset=utf-8, but even when I do I get application/json; charset=utf-8, so I'm just using application/json.
With that aside, as far as I can tell, this does what it's supposed to do. It creates the client and the request message, sets the headers, and sends the message. I get a 201 Created every time. Here's all of it:
private async static void SendServiceBusMessage(Command command)
{
// Create the HttpClient and HttpRequestMessage objects
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, topicUri);
// Add the authorization header (CreateAuthToken does the SHA256 stuff)
request.Headers.Add("Authorization", CreateAuthToken(topicUri, authSasKeyName, authSasKey));
// Add the content (command is a normal POCO)
// I've tried application/atom+xml;type=entry;charset=utf-8, always see application/json in the request
request.Content = new StringContent(JsonConvert.SerializeObject(command), Encoding.UTF8, "application/json");
// Add the command name and SessionId as BrokeredMessage properties
var brokeredMessageProperties = new Dictionary<string, string>();
brokeredMessageProperties.Add("CommandName", command.GetType().Name);
brokeredMessageProperties.Add("SessionId", Guid.NewGuid().ToString());
// Add the BrokerProperties header to the request
request.Content.Headers.Add("BrokerProperties", JsonConvert.SerializeObject(brokeredMessageProperties));
// I've also tried adding it directly to the request, nothing seems different
// request.Headers.Add("BrokerProperties", JsonConvert.SerializeObject(brokeredMessageProperties));
// Send it
var response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
// Do some error-handling
}
}
and here's an example of the HTTP request it sends. Compare it to the example at the bottom of Send Message documentation... aside from the Content-Type, it looks (functionally) identical to me.
POST https://myawesomesbnamespace.servicebus.windows.net/commands/messages HTTP/1.1
Authorization: SharedAccessSignature sr=https%3A%2F%2Fmyawesomesbnamespace.servicebus.windows.net%2Fcommands%2Fmessages&sig=SomeValidAuthStuffHere
Content-Type: application/json; charset=utf-8
BrokerProperties: {"CommandName":"CreateJob_V1","SessionId":"94932660-54e9-4867-a020-883a9bb79fa1"}
Host: myawesomesbnamespace.servicebus.windows.net
Content-Length: 133
Expect: 100-continue
Connection: Keep-Alive
{"JobId":"6b76e7e6-9499-4809-b762-54c03856d5a3","Name":"Awesome New Job Name","CorrelationId":"47fc77d9-9470-4d65-aa7d-690b65a7dc4f"}
However, when I receive the message on the server, the .Properties are empty. This is annoying.
The server code looks like this. It just gets a batch of messages and does a foreach loop.
private async Task ProcessCommandMessages()
{
List<BrokeredMessage> commandMessages = (await commandsSubscriptionClient.ReceiveBatchAsync(serviceBusMessageBatchSize, TimeSpan.FromMilliseconds(waitTime_ms))).ToList();
foreach (BrokeredMessage commandMessage in commandMessages)
{
// commandMessage.Properties should have CommandName and SessionId,
// like I sent from the client, but it's empty
// that's not good
if (commandMessage.Properties.ContainsKey("CommandName"))
{
string commandName = commandMessage.Properties["CommandName"] as string;
// Do some stuff
}
else
{
// This is bad, log an error
}
}
}
So, I'm a bit stuck. Can anyone spot something I'm doing wrong here? Maybe it's the Content-Type problem and there's a way around it?
Thanks!
Scott
Seattle, WA, USA
OK, finally getting back to this. What I misunderstood (and I'd argue the documentation isn't clear about) is that arbitrary properties cannot be passed through the BrokerProperties header. Only named properties from the BrokeredMessage class (like SessionId, Label, etc.) will come through Service Bus to the server.
For properties to show up in BrokeredMessage.Properties, they have to be passed as custom headers on the request. So, in my case,
request.Headers.Add("CommandName", command.GetType().Name);
gets the CommandName property to show up on the server after the message is passed through Service Bus.
And to pass the SessionId value, I'll still want to pass it through BrokerProperties header.

Setting MessageHeaders.To field gets overwritten

Here's the scenario: I'm trying to send a SOAP message to an intermediary router service. That service only cares about my SOAP message headers, and uses the WS-Addressing To header to forward along my message.
I need to basically POST a request like the following to the router service:
POST http://gatewayRouter/routingService HTTP/1.1
Content-Type: application/soap+xml; charset=utf-8
Host: gatewayRouter
Content-Length: 8786
Expect: 100-continue
Connection: Keep-Alive
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header> <!-- ... -->
<a:To s:mustUnderstand="1">http://actualDestination</a:To>
</s:Header> <!-- ... body, /envelope, etc --->
I'm currently able to set other custom headers that the routing service requires by using Custom Behaviors without a problem:
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
request.Headers.To = new Uri("http://actualDestination");
request.Headers.Add(new CustomHeader());
return null;
}
The above code works fine to add my CustomHeader to the message, but fails to modify the outgoing WS-Addressing To field - it always gets set back to the same URI as the HTTP POST value. In fact, I used .NET Reflector to debug when this field gets set- and sure enough, it is getting overwritten (screenshot of the stack trace and breakpoint).
Is there some other way for me to change the To SOAP header that I'm not understanding correctly?
I figured it out on my own with a hint from here. Programatically, I can set the Via on the ClientRuntime inside the custom behavior. This allows the POST to differ from the actual endpoint address that gets set automatically due to my usage of WSHttpBinding.
public void ApplyClientBehavior
(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
CustomMessageInspector inspector = new CustomMessageInspector();
clientRuntime.MessageInspectors.Add(inspector);
clientRuntime.Via = new Uri("http://gatewayRouter/routingService");
}

Creating a WCF PUT Rest service

I have a simple REST web service in WCF that is declared as follows:
[WebInvoke(Method = "PUT",UriTemplate = "comptatge/add",RequestFormat = WebMessageFormat.Json,ResponseFormat = WebMessageFormat.Json)]
public void GravaComptatge(MyDataContract contract)
{
...
}
where MyDataContract is a class with simple properties.
Now I send a put Request using fiddler to see if it works OK. In the body I send JSON with the same properties as MyDataContract but it's not working. I get a 400 Bad Request HTTP error. What am I doing wrong here? The service is hosted in a MVC app using ServiceRoute. Other GET services work OK.
This happened to me before, and adding Content-Type: application/json in the request headers was the solution.

Does the Content-Type of a POST or PUT request need to be 'application/x-www-form-urlencoded' in a WCF ReSTful service?

What I am trying to do is, seemingly, simple: send some POX via the request body, have the WCF service process it, and return a 201 status code. In my ServiceContract I have defined the following method:
[WebInvoke(Method = "PUT", UriTemplate = "/content/add", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml, RequestFormat=WebMessageFormat.Xml)]
[OperationContract]
Stream AddContent(Stream input);
The verb here doesn't matter; I could easily replace 'PUT' with 'POST' and wind up with the same result. The implementation of the above method is as follows:
public Stream AddContent(Stream input)
{
WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.Created;
}
Since what this method actually does is of little consequence I have omitted all of the procedural code. To test this functionality I fired up Fiddler and issued the following request:
User-Agent: Fiddler
Host: myhost.com
Content-Length: 771
Content-Type: text/xml && application/xml; charset: utf8
<xmlDataGoesHere></xmlDataGoesHere>
The supplied value for Content-Type is incorrect, I know, I am just using it to illustrate the Content-Type's that I have tried. If I click on 'Execute' in Fiddler the response code from my service is 400 Bad Request. It is worth noting that my service method is not actually getting hit in this secnario, the request dies before it even gets there. So after copious amounts of reading and process of elimination I changed the Content-Type to :
Content-Type: application/x-www-form-urlencoded
If I execute the Fiddler request now the status code returned from my service is 201 Created. Is there something I am missing as to why I can't set the Content-Type to anything but URLEncoded? I have tried tweaking the BodyStyle and RequestFormat properties in the ServiceContract but those did not have any impact on the outcome. Can anyone shed any light on why this is happening?
I think the problem is related to the fact that when you send XML to a WebHttpBinding, it detects that you are sending XML and tries to deserialize it. It ignores the fact that your signature is expecting a stream. There are workarounds that I have seen but I can't find the links at the moment. If I find them I'll update the post.
Take a look at this http://wcfrestcontrib.codeplex.com/
Ok I have figured this out and the issue I am having is "by design". I found the post 'WCF "Raw" programming model - receiving arbitrary data' over at http://blogs.msdn.com/carlosfigueira/ which led to the solution. The solution here was to create my own customBinding that will return the raw encoder all the time as the blog pointed out. Thanks for all the help.

Categories

Resources