简体   繁体   中英

WCF, SOAP, Plain Old XML

I'm very confused by some things in the Web Service world, and I was hoping that someone could explain it to me.

Here's what I want: I want to HTTPPost an XML document to https://www.whatever.com/myservice (.???). In that service, I want to get that XML doc, do stuff, then respond back with an XML doc that is ultra simple -- like " <xml....><success>TRUE</success> "

I can't do SOAP or JSON or anything else. I MUST conform to what already exists. There are hundreds of different companies sending us data in exactly the format that I described, and adding a requirement to enclose it all in SOAP simply isn't possible.

When using a WCF Web Service, it appears that the request to me has to be sent like this:

 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetData xmlns="http://tempuri.org/"><value>3</value></GetData></s:Body></s:Envelope>

When using a .asmx page, the response is always sent as an XML document with a namespace (http://tempuri, or if I change it, then to whatever I change it to). This is unneeded and would likely break all the existing code.

When using a generic handler (.ashx), I can definitely control exactly what gets returned back, but I've been told to stay away from .ashx pages because they carry the full ASP.Net session type code and it is much slower.

What would I use to solve this issue and have my code be as fast as possible?

EDIT: In case anyone cares, the 10,000 requests per hour issue is our peak traffic during the year. We handle 50-60k incoming requests per day on average, but during December it can reach up to 10,000 requests per hour. And we hope to grow, but to do that, I'll add a load balancer and some more machines in Rackspace.

Our current provider of this service is overwhelmed during these times and we miss out on a lot of money because of this. So, my proxy will do one of our most basic checks on the front side (duplicate check against a database updated hourly) and that will weed out 75% of the traffic we have to send to our current provider. The current provider does underwriting of these leads for us, and I will not be replacing that, merely adding a gatekeeper to reduce stress on their servers.

Thanks for all of the help. I've asked a series of web service questions lately on StackOverflow trying to get my head around many of the issues that I'll be facing.

What you are talking about is a POX service -- also known as plain old XML.

Whoever told you IHttpHandlers carry the session code is wrong -- they don't unless you explicitly enable session state . Now, it does have the ASP.NET pipeline overhead, but unless you are using some lunatic fringe lightweight web socket server this doesn't really matter as any form of IIS hosted service will have similar overhead. And, if this is a public-facing internet service, IIS does some neat things for you that you might want. Things like SSL, request logging and integration with the other stuff operations is using to watch things.

As for how to get there, since you've got a defined format ASMX and WCF with default (SOAP) behaviors are out by definition. For flexibility, the actual logic of the service should be implemented separately leaving the external bits responsible for taking requests, translating from xml and sending the response back as properly formatted XML.

This leaves you with a few options:

  • Probably the simplest, especially if this is bolting onto an existing .NET site, would be a custom IHttpHandler. You'll have to much around a bit lower down the stack than in other places but if this is really taking one set of XML in and spitting a response back the work isn't insurmountable. One big advantage is that you have finite control over the output side, so if you've got a long-running task you can can stream responses pretty easily.

  • ASP.NET MVC web service would perhaps be simpler than a IHttpHandler inasmuch as you won't have to dive into the POST variables so much and would also makes sense if you are bolting onto a MVC site.

  • WCF with webhttp bindings could work if the XML is formatted in a manner it doesn't mind. But you will have some stack boogeymen to slay as WCF, in my experience, never wants to exactly fit some sort of XML you like to see on the wire.

  • Something alternative, such as OpenRasta , could work very well here. It is really meant for these sorts of POX style scenarios and will very easily let you use your xml formats, etc.

Insofar as speed, the real question to answer is not "is this the fastest it could be" but rather "is this fast enough?" Especially considering the nature of HTTP and remote communications means that, in most cases, any server-side efficiencies you might be able to create don't really matter because it takes hundreds of times longer for the request to transit across the wire than for it to get processed. If you do have a relatively clean implementation of the service logic, then you'll be in good position to switch communications stacks until you find the one that works the best for you.

It little depends on what kind of application do you have? If it is ASP.net WebForms, you might just have standard Page, with Page_Load method in which you can handle request and put anything you like in HttpCurrent.HttpContex.Response object.

If it is MVC, you should have an action with custom ActionResult.

But as for me I don't see anything wrong with Generic Handlers .ashx. I would go with it.

Try a "bare" repsonse: http://msdn.microsoft.com/en-us/library/system.servicemodel.web.webmessagebodystyle.aspx

[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Bare)]

Or perhaps in your case, if your request is wrapped but you want a simple unwrapped response:

[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest)]

I usually use WrappedRequest

Ashx as generic handler doesn't have session until you specify it . What you describe is sometimes called XML service. WCF by default allows SOAP and REST services. I'm not sure if you can use unique URL per resource (message type) which would allow you using REST service. It means your url would look like https://www.whatever.com/myservice/something where something would be used to map to operation marked with WebInvoke attribute as:

[ServiceContract]
public interface IService
{
    [OperationContract]
    [WebInvoke(Method = "POST", UriTemplate="Something", 
               BodyStyle = WebMessageBodyStyle.Bare, 
               RequestFormat = WebMessageFormat.Xml,
               ResponseFormat = WebMessageFormat.Xml)]
    CompositeTypeMessage GetData(CompositeTypeMessage composite);
}

You can also use native WCF without REST to define XML service but it requires a lot of effort:

First you have to define custom binding where the important part is messageVersion=None .

<bindings>
  <customBinding>
    <binding name="XmlService">
      <textMessageEncoding messageVersion="None"/>
      <httpTransport/>
    </binding>
  </customBinding>
</bindings>
<services>
  <service name="XmlService.Service">
    <endpoint address="" binding="customBinding" bindingConfiguration="XmlService" contract="XmlService.IService"/>
    <host>
      <baseAddresses>
        <add baseAddress="http://localhost:8732/XmlService"/>
      </baseAddresses>
    </host>
  </service>
</services>

Then you have to define contracts and remove default namespace:

[ServiceContract(Namespace = "")]
public interface IService
{
    [OperationContract]
    CompositeTypeMessage GetData(CompositeTypeMessage composite);
}

// Now here you can make main decission. Do you want wrapper for data
// or not. If you don't use message contract and use data contract directly
// each request will be wrapped in the element with the same name as operation
// and response will be wrapped in the element with the name of operation + 
// Response suffix. Message contract allows you defining custom wrapper name
// or turning it off.
[MessageContract (WrapperNamespace = "", IsWrapped = false)]
public class CompositeTypeMessage
{
    [MessageBodyMember(Namespace = "")]
    public CompositeType Data { get; set; }
}

[DataContract(Namespace = "")]
public class CompositeType
{
    bool boolValue = true;
    string stringValue = "Hello ";

    [DataMember]
    public bool BoolValue
    {
        get { return boolValue; }
        set { boolValue = value; }
    }

    [DataMember]
    public string StringValue
    {
        get { return stringValue; }
        set { stringValue = value; }
    }
}

The last point is implementing custom IDispatchOperationSelector behavior. Removing all SOAP related features breaks current WCF ability to select correct operation to process a message. You must define a new one which will for example select operation by some convention based on root request element. Here is MSDN sample how to build and use similar behavior.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM