The .NET Framework makes the creation of a Web service almost embarrassingly simple, particularly when you’re developing in Visual Studio .NET. Just about all you need to conjure the Web service spirits in an otherwise normal looking .NET code file is that magical 11-character incantation: [WebMethod]. With the [WebMethod] attribute decorating a class method, and with the class referenced in an .asmx file, ASP.NET will generate a complete service description in WSDL and begin serving up clients right away.
However, because of the unique demands of their applications, most developers typically won’t be able to stop at the code that is autogenerated by the presence of [WebMethod]. Probably the most obvious additional attribute developers add is [WebService], where you can provide a nice description of the service and, most importantly, change its namespace from the default http://tempuri.org that ASP.NET automatically assigns and then later complains about when you access the Web service from a browser. In this article, I’ll go beyond these simple tweaks and consider a few more complicated changes that you might make to tailor the WSDL and SOAP that ASP.NET generates for you on the fly.
No SOAP header
Our first example overcomes the fact that ASP.NET, by default, generates no SOAP header element. If you need your clients to pass you a user id and password, for instance, such information really should go into the header to keep it separate from the functional requests that appear in the SOAP body. You need to tell .NET what such a header would look like, which messages might include it, and whether it is absolutely required for any of those messages. First create a small class in the same namespace as your main Web service class.
This new class will represent the header elements, and it must:
- Inherit System.Web.Services.Protocols.SoapHeader; and
- Be scoped with the public access-modifier.
You can put whatever public fields you’d like into the class. Using the authentication example, you might include UserID and Password fields, as shown in Listing A. You also need to create a public instance variable of this header class inside your main Web service class. You can name this instance variable any way you’d like; I called it auth in Listing A. Don’t include any instantiation code for the variable; ASP.NET will take care of that.
Having created a class and instance variable that can be used as a SOAP header element, you now need to tell ASP.NET which of the Web service methods use the header. As Listing A shows, just use the [SoapHeader] attribute of the System.Web.Services.Protocols namespace to decorate those methods that you want to have the header in their SOAP messages. Note that the first property in the [SoapHeader] attribute is the name of the instance variable of the special header class, which I called auth in Listing A. The value given to the Direction property indicates that only requests (inbound messages) use this header, and the Required property mandates its use; a request message that doesn’t contain a required header will result in the server returning a SOAP fault. If you now build the service and visit its .asmx file to take a look at the sample SOAP, you’ll see that the request message contains the header element with two child elements, each representing one of the public fields of the special header class.
Since you’ve told ASP.NET to expect the header, you can actually use that instance variable inside the code for your Web method. Listing A accomplishes this by passing the auth variable to another function to validate the user. ASP.NET instantiates the auth variable, so you do not see any instantiation code such as auth = new AuthenticationHeader() in Listing A.
A class with overloaded methods
Next, let’s consider a class with overloaded methods—multiple methods of the same name but with different signatures—all of which you want to turn into Web methods. Perhaps you have two versions of GetEmployeeInfo, one that accepts the employee id as a parameter, and another that accepts the last name and first name:
- EmployeeInfoStruct GetEmployeeInfo(int nEmployeeID){…}
- EmployeeInfoStruct GetEmployeeInfo(string sLastName, string sFirstName){…}
If I simply decorate both of them with the [WebMethod] attribute, ASP.NET will complain that two messages can’t share the same name. Having no desire to change the class method names just to make ASP.NET happy, I can instead add the MessageName property to the [WebMethod] attribute, as in Listing B. This specifies SOAP message names of GetEmployeeByID and GetEmployeeByName for the overloaded methods. Although the WSDL will still have two different operations named GetEmployeeInfo within its SOAP binding, the operations will now have different soapAction attributes and different input and output message names.













