9
Attributes
This chapter rivets around those
attributes, which can be placed upon entities, such as- functions, classes,
properties etc. in both, the asmx file, as well as, the proxy program.
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
using System.Xml.Serialization;
public class zzz
{
[WebMethod]
public void abc(yyy a)
{
}
}
public class yyy
{
public int i;
public xxx k;
public int j;
}
public class xxx {
public int x1,x2;
}
a.cs
class aaa
{
public static void Main()
{
zzz a= new zzz();
yyy b = new yyy();
xxx c = new xxx();
b.i = 10;b.j = 20;
b.k = c;
c.x1 = 100; c.x2 = 200;
a.abc(b);
}
}
z.bat
del *.exe
del *.dll
del zzz.cs
wsdl aa.wsdl
csc /t:library zzz.cs
csc a.cs /r:zzz.dll
a
SOAP request
<abc xmlns="http://tempuri.org/">
<a>
<i>10</i>
<k>
<x1>100</x1>
<x2>200</x2>
</k>
<j>20</j>
</a>
</abc>
In the asmx file, the function
abc in class zzz accepts a parameter 'a', of type yyy. The class yyy has two
int variables, i and j, and a third variable 'k', of type xxx. The class xxx
contains two variables, x1 and x2.
In the browser, enter the url as
http://localhost/a.asmx?WSDL and then, save the output in a file named aa.wsdl.
Since, our very core of concern is the data encompassed in the SOAP packet that
flows to and fro, we alter the port number of localhost to 8080, and banish the
inane or worthless characters. Thereafter, the client proxy is generated,
courtesy - the wsdl program. As the WSDL file and proxy introduce nothing that
is really innovative, we give them the slip for a while.
The client program named a.cs,
creates three objects, viz. xxx, yyy and zzz. The creation of a yyy object does
not result in the creation of an xxx object automatically. In fact, it has to be created expressly.
Thereafter, using object 'b' of
type yyy, the 'k' member of type xxx is initialized to 'c', which is an xxx
object. The other members of the yyy object are also initialized. The object
'b' is then passed as a parameter to the function abc.
As is customary, the SOAP
request originates with the parameter 'a', which encloses its three members
viz. i, j and k. Since the variable 'k' is also an object, it becomes the
parent tag of the two members x1 and x2, which it embodies. Therefore, objects
that are enclosed within other objects, carry their children together with
them.
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
using System.Xml.Serialization;
public class zzz
{
[WebMethod]
public void abc(yyy a)
{
}
}
public class yyy
{
public int i;
public xxx [] k;
public int j;
}
public class xxx
{
public int x1,x2;
}
a.cs
class aaa
{
public static void Main()
{
zzz a= new zzz();
yyy b = new yyy();
b.i = 10;b.j = 20;
xxx [] c = new xxx[2];
c[0] = new xxx();
c[0].x1= 20;c[0].x2= 21;
c[1] = new xxx();
c[1].x1= 200;c[1].x2= 201;
b.k = c;
a.abc(b);
}
}
SOAP request
<abc xmlns="http://tempuri.org/">
- <a>
<i>10</i>
- <k>
- <xxx>
<x1>20</x1>
<x2>21</x2>
</xxx>
- <xxx>
<x1>200</x1>
<x2>201</x2>
</xxx>
</k>
<j>20</j>
</a>
</abc>
In the asmx file, the variable
'k' is an array of xxx objects. Earlier, it had been declared as a simple
variable. With the exception of this modification, everything else remains
unaltered. The WSDL file and the client proxy are both generated as before.
Firstly, in the client program,
an array called 'c', of xxx objects, is created. It has a size of 2. Both the
arrays are initialized, by being referred to as c[0] and c[1]. The data
displayed in the SOAP request is comprised of the three variables i, j and k.
The variable 'k', being an array of two objects, contains two child elements
called xxx. It is these child elements, which accommodate the array contents.
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
using System.Xml.Serialization;
public class zzz
{
[WebMethod]
public void abc(yyy a)
{
}
}
public class yyy
{
public int i;
[XmlAttribute()]
public int j;
}
a.cs
class aaa
{
public static void Main()
{
zzz a= new zzz();
yyy b = new yyy();
b.i = 10;b.j = 20;
a.abc(b);
}
}
aa.wsdl
<s:complexType name="yyy">
<s:sequence>
<s:element
minOccurs="1" maxOccurs="1" name="i"
type="s:int" />
</s:sequence>
<s:attribute name="j" type="s:int" />
</s:complexType>
zzz.cs
public class yyy
{
public int i;
[System.Xml.Serialization.XmlAttributeAttribute()]
public int j;
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool jSpecified;
}
SOAP request
<a>
<i>10</i>
</a>
In the world of XML
Serialization, we retain the prerogative to elect and settle upon the
methodology, by means of which, the XML data shall be sent across the wire. By
placing an attribute named XmlAttribute on the variable j in the wsdl file, it
becomes evident that the complexType yyy has only one element called i. The
variable j assumes the form of an attribute, instead of an element.
Thus, the WSDL honours the
attribute. The a.cs file is not really plagued about whether 'j' is an
attribute or an element. The client program remains absolutely unchanged. The
proxy that is generated, has the attribute XmlAttributeAttribute placed on it,
along with another member called jSpecified, with an attribute of
XmlIgnoreAttribute on it.
Despite of our having assigned a
value to variable 'j', it never gets reflected in the SOAP request. We were of
the notion, that the value contained in 'j', would be sent out as an attribute.
However, at this stage, it has completely been snubbed by the System.
Now, we just add another line to
the a.cs file.
a.cs
b.i = 10;b.j = 20;
b.jSpecified = true;
a.abc(b);
SOAP request
<a j="20">
<i>10</i>
</a>
On initializing the jSpecified
member to 'true', the SOAP request that is sent across, displays 'j' as an
attribute to the parameter name 'a', with a value of 20. The default value
assigned to the jSpecified variable is 'false'. Hence, it is for this reason
that 'j' was not sent over the wire earlier on; in other words, it was not
serialized.
If you replace the XmlAttribute
with the attribute of SoapAttribute, the attribute would throw the following
error:
Output
a.cs(8,1): error CS0117: 'yyy' does not contain a definition
for 'jSpecified'
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
using System.Xml.Serialization;
public class zzz {
[WebMethod]
public void abc(yyy a) { }
}
public class yyy
{
[SoapAttribute()]
public int i;
[XmlAttribute(AttributeName="ddd")]
public int j;
}
aa.wsdl
<s:attribute name="ddd" type="s:int"
/>
zzz.cs
public class yyy
{
public int i;
[System.Xml.Serialization.XmlAttributeAttribute()]
public int ddd;
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool dddSpecified;
}
a.cs
class aaa
{
public static void Main()
{
zzz a= new zzz();
yyy b = new yyy();
b.i = 10;
b.ddd = 20;
b.dddSpecified = true;
a.abc(b);
}
}
SOAP request
<a ddd="20">
<i>10</i>
</a>
The AttributeName property is
employed here, since our intention is to change the name of the variable, from
'j' to 'ddd'. Therefore, on generating the WDSL file, the name assigned to the
attribute is 'ddd', and not 'j'.
In the proxy that is generated,
the SoapAttribute is not affected. However, the name assigned to the bool
variable is 'dddSpecified', thereby, reaffirming the change of the variable
name to 'ddd'.
Both, the client program and the
SOAP payload, refer to the variable as 'ddd', and not as 'j'. Thus, we can
exercise absolute authority over the variables' names written in the SOAP
payload.
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
public class zzz {
[WebMethod(Description="vijay mukhi")]
public void abc()
{
}
}
aa.wsdl
<portType
name="zzzSoap">
<operation name="abc">
<documentation>vijay mukhi</documentation>
<input
message="s0:abcSoapIn" />
<output
message="s0:abcSoapOut" />
</operation>
</portType>
a.cs
class aaa
{
public static void Main()
{
zzz a= new zzz();
a.abc();
}
}
The WebMethod attribute that
remotes a function, contains a property called Description, which facilitates
certain amount of documentation in the function abc. The wsdl file generated
from the asmx file, has the portType element, which contains the operation
element of abc. This is followed by the element documentation, which carries
the name of 'vijay mukhi'.
In essence, this is what the
Description property is all about. However, there is no mention of Description
in the proxy that is generated, since the wsdl utility disregards the element
completely.
Application and Session
Objects
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
public class zzz : WebService
{
[WebMethod()]
public int abc()
{
if (Application["zzz"] == null)
Application["zzz"] = 1;
else
Application["zzz"] = ((int)
Application["zzz"]) + 1;
return (int)
Application["zzz"];
}
}
In the above asmx file, the
class zzz is derived from the WebService class. This class has a property
called Application, which is of type HttpApplicationState. The variables are
created using the indexer. These are not variables in the real sense of the term,
but they boast of all functionalities of a variable.
Thus,
Application["zzz"] creates a variable named 'zzz'. Since it is not
initialized, it has a value of null. When the abc function is invoked for the
first time, the value of zzz gets set to 1. This may be ascertained by
specifying the address http://localhost/a.asmx/abc? in the browser window.
The question mark at the end of
the URL is optional, and is used when parameters are required to be supplied to
the function abc. Now, click on the refresh button in the browser. This invokes
the same function again.Since the Application object has the value of 1, it
saves this last value of the zzz state variable, increments it by 1, and then,
restores the new value. Therefore, the number 2 is displayed in the browser.
Leave the browser window as it
is and open a new copy of IE. Then, enter the same Url. Now, the value that
would be displayed is 3.
The HTTP protocol is referred to
as 'stateless'. This is so because, as soon as the connection between the
server and the client cleaves, all the activities and information related to
the client and the server get obliterated. As a consequence, when we request
the server for a new page, it entails re-establishment of connection between
the two ends.
Thus, the process starts anew
each time, proving to be irksome. The Application object is primarily utilized
to maintain 'state', thereby, enabling the web server to keep track of the
events that have transpired at its end.
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
public class zzz : WebService
{
[WebMethod(CacheDuration=6000,EnableSession=true)]
public int abc()
{
if (Session["zzz"] == null)
{
Session["zzz"] = 1;
}
else
{
Session["zzz"] = ((int) Session["zzz"]) + 1;
}
return (int)
Session["zzz"];
}
}
In the case of the Application
property, the state is maintained as per the requirement of the machine, and
not as per the browser. Consequently, the session object, with the variables by
its side, is used to start the browser afresh each time. When we initiate a new
copy of the browser, the count also starts with 1.
The state object zzz is restored
with the copy of the browser. So, when a new browser instance is loaded, the
variable zzz begins with the value of 1. The lacuna in this approach is that,
the web server is bound to run into rough weathers and slow down considerably.
The speed of the web server is trammeled because, there would be innumerable
copies of the browser interacting with the server, and the server would have no
choice but to maintain an equal number of copies of the variable zzz.
Thus, by default, the Session
property is disabled. To enable it, we utilize the property of the WebMethod
attribute, and activate EnableSession. The Session property uses Cookies to
maintain state, or to uniquely identify an instance of a browser. The
CacheDuration property determines the duration in seconds, for which the page
should linger in the cache, before the system reads it again from its hard
disk.
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
public class zzz : WebService
{
[WebMethod(MessageName="vijay")]
public int abc()
{
return 100;
}
[WebMethod(MessageName="mukhi")]
public int abc(int i )
{
return i;
}
}
http://localhost/a.asmx/vijay
http://localhost/a.asmx/mukhi?i=123
In an asmx file, no two
functions can share the same name. However, with the help of the MessageName
property, a new name may be assigned to a common function.
The first abc function in the
asmx file is named 'vijay', whereas, the second one is named 'mukhi'.
Therefore, the first url contains 'vijay', and not 'abc'. The second function
of 'abc' accepts a parameter. Therefore, the function abc named 'vijay' is
ignored, and the new name assigned to abc is 'mukhi'.
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
[WebServiceAttribute(Description="vijay mukhi")]
public class zzz : WebService
{
[WebMethod()]
public void abc()
{
}
}
aa.wsdl
<service name="zzz">
<documentation>vijay mukhi</documentation>
…..
</service>
Unlike the WebMethod attribute,
which is placed on a function, the WebServiceAttribute is placed on a class.
The Description property is employed to document the WebService. Therefore, at
the end of the WSDL file, we encounter the last element service, containing the
name of the class zzz. The Description property becomes the element
documentation for the zzz service.
a.asmx
[WebServiceAttribute(Name="vijay")]
Modify the parameter of the
WebServiceAttribute to the one depicted above, and observe the changes in the
WSDL file.
In the WSDL file, since the name
of the class is zzz, most elements begin with zzz. However, we would be able to
modify the name of the service to 'vijay', by using the Name property. This
would result in a change in the other elements too. The name of the proxy that
is generated, would also change to vijay.cs, with the class name displayed as
'vijay'. Thus, we would be able to infuse a considerable amount of flexibility,
by the use of attributes.
a.asmx
[WebServiceAttribute(Namespace="vijay")]
aa.wsdl
xmlns:s0="vijay" targetNamespace="vijay"
The WSDL file sets out with the
definitions, which define and delineate a large number of namespace prefixes.
This situation can be suitably altered, by using the Namespace property. The
targetNamespace attribute ensures that a targetNamespace called 'vijay' is
created, which relates to all the entities created in the file. You would have
discerned that, the browser does not display any text on loading the asmx file,
unlike the case of the earlier output.
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
using
System.Collections.Specialized;
using System.Web;
[WebServiceBinding()]
public class zzz : WebService
{
[WebMethod()]
public string abc()
{
string s = "";
int i;
NameValueCollection c;
HttpRequest r = Context.Request;
c=r.Headers;
string[] s1 = c.AllKeys;
for (i = 0; i<s1.Length; i++)
{
s = s + "Key: " + s1[i] + "=";
string[] s2=c.GetValues(s1[i]);
s = s + s2[0] +
".";
}
return s;
}
}
Output
Key: Connection=Keep-Alive.
Key: Accept=*/*.Key: Accept-Encoding=gzip, deflate.
Key: Accept-Language=en-us.
Key: Host=localhost.
Key: Referer=http://localhost/a.asmx?op=abc.
Key: User-Agent=Mozilla/4.0 (compatible; MSIE 6.0b; Windows
NT 5.0; .NET CLR 1.0.2914).
This book primarily converges
around the concepts of SOAP and XML. Hence, we do not submerge ourselves any
deeper into the details of the Webservice class. Nonetheless, we have presented
an artless yet effective example, which highlights the influence exerted by the
properties contained in the class.
It is possible for us to access
every aspect of the web server, by using the WebService class. One such
property in this class is the Context property, which is of type HttpContext.
This property has a host of other properties of type HttpRequest, each prefixed
with the word 'Request'. Moreover, there exists a member called Headers, which
returns the headers received by a Web Server. The data type of this return
value is a NameValueCollection object.
The object c' has a property
called AllKeys, which returns an array of strings. The values contained in the
array are the keys that arrive at the web server. A key is the word, placed
before the 'equal to' sign in a name-value pair. With the assistance of a 'for'
loop, every key in the array is accessed. Thereafter, using the GetValues
function, the value of each key is returned.
Each key can have multiple
values, but we have confined ourselves merely to the first value. The key and
its corresponding value are placed in a huge string, and then, they are sent
across. These values are displayed, when the asmx file is loaded in the
browser.
Thus, by harnessing the
abilities of the properties of the Context object, the information sent to the
web server can be accessed with considerable ease.
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
public class zzz
{
[WebMethod()]
public int abc(int i , int j)
{
return i+j;
}
}
a.cs
public class zzz :
System.Web.Services.Protocols.SoapHttpClientProtocol
{
public static void Main()
{
zzz a = new zzz();
string b;
b = a.abc(10,20);
System.Console.WriteLine(b);
}
[System.Web.Services.Protocols.SoapDocumentMethodAttribute()]
public string abc(int i , int j)
{
Url = "http://localhost:8080/a.asmx";
object[] results = Invoke("abc", new object[2]{i,j});
return (string)results[0];
}
}
>csc a.cs
>a
The asmx file and the .cs file
are analogous to the ones encountered in the earlier chapters. The function abc
in the asmx file, returns the sum of the two ints that are supplied to it as
parameters. In the client program, the function abc is called with the two
values of 10 and 20. Thereafter, everything else works with clockwork
precision.
Now, carry out a minor
modification to the above program, by eliminating the attribute of
SoapDocumentMethodAttribute. On doing so, the following exception is thrown:
Output
Unhandled Exception: System.ArgumentException: abc Web
Service method name is not valid.
Thus, this corroborates our
credence that, it is the SoapDocumentMethodAttribute that introduces code, when
we are remoting functions from the client end.
a.cs
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(Action="sonal")]
Output
Unhandled Exception:
System.Web.Services.Protocols.SoapException:
System.Web.Services.Protocols.SoapException: Server did not recognize the value
of HTTP Header SOAPAction: sonal.
at
System.Web.Services.Protocols.SoapServerProtocol.Initialize()
The first property that is
employed with SoapDocumentMethodAttribute is 'Action'. It is used to set only
the Http header SOAPAction, which has been added by the SOAP specifications.
This header spells out the intentions of the SOAP request. The default value is
http://tempuri.org, followed by the function name 'abc'.
The SOAP specification is not
rigid with respect to the contents of this value. Therefore, firewalls can
exploit this function to filter out unwanted SOAP requests. In our case, the
server throws an exception, indicating the fact that it cannot recognize or
comprehend 'sonal'. It is the Invoke function that triggers off the Exception.
The server generates a SOAP fault, since it does not recognize the SOAPAction
header at all.
a.cs
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(OneWay=true)]
Output
Unhandled Exception: System.Exception: Method zzz.abc can not be
reflected. ---> System.Exception: One-way methods cannot have return values.
at
System.Web.Services.Protocols.SoapReflector.ReflectMethod(LogicalMethodInfo
methodInfo, Boolean client, XmlReflectionImporter xmlImporter,
SoapReflectionImporter soapImporter, String defaultNs)
The next property to be
implemented is the OneWay property. Its value is set to 'true', although, its
default value is 'false'.
By throwing an exception, it
acquaints us with the fact that, the OneWay function cannot have a return
value. The exception is generated by the ReflectMethod, and not by the Invoke
function. You would have observed that, no bytes are visible in the Trace
output.
a.cs
using System.Web.Services.Protocols;
using System.Web.Services.Description;
public class zzz :
System.Web.Services.Protocols.SoapHttpClientProtocol
{
public static void Main()
{
zzz a = new zzz();
a.abc(10,20);
}
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(OneWay=true)]
public void abc(int i , int j)
{
Url = "http://localhost:8080/a.asmx";
Invoke("abc", new object[2]{i,j});
}
}
SOAP response
HTTP/1.1 100 Continue
Server: Microsoft-IIS/5.0
Date: Wed, 17 Oct 2001 02:38:01 GMT
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Wed, 17 Oct 2001 02:38:01 GMT
Cache-Control: private, max-age=0
Content-Type: text/xml; charset=utf-8
Content-Length: 358
<?xml version="1.0"
encoding="utf-8"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http:/
/www.w3.org/2001/XMLSchema">
<soap:Body>
<abcResponse xmlns="http://tempuri.org/">
<abcResult>30</abcResult>
</abcResponse>
</soap:Body>
</soap:Envelope>
In place of 'formatted', set the
trace output to 'unformatted', and take a look at the bytes that contain this
header data.
The above example has no return
value in either of the two functions, i.e. abc and Main. An HTTP response
always starts with the HTTP version number 1.1 and a success or error code,
followed by the description of the status code. For instance -
• 200 is followed by 'OK'.
• 404 is followed by 'File Not
found'.
• 100 is followed by 'Continue'.
The status is followed by
headers, such as- Date, Cache-Control, Content-type and length.
The OneWay property, as
specified in the client program, indicates that the client would not wait for
the web server to complete the processing of the Web Service method. Therefore,
the client sends the header
"Expect: 100 Continue".
On receiving the request,
the server too first sends across an HTTP header. Thereafter, it sends the entire SOAP payload, together
with the reply.
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
public class zzz
{
[WebMethod()]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(OneWay=true)]
public void abc(int i , int j)
{
}
}
SOAP response
HTTP/1.1 100 Continue
Server: Microsoft-IIS/5.0
Date: Wed, 17 Oct 2001 02:51:23 GMT
HTTP/1.1 202 Accepted
Server: Microsoft-IIS/5.0
Date:Wed, 17 Oct 200 1 02:51:24 GMT
Cache-Control: private
Content-Length: 0
An alternative approach would
be, to set the Attribute OneWay on the WebService method, on the server. On
doing so, the server will first send an HTTP/1.1 100 packet, followed by a
"202 Accepted" response. This response packet, when received by the
client, will ascertain that the client does not have to wait for the server to
dispatch the result packet. However, in this case, there is no SOAP response at
all.
Thus, the WebService will first
deserialize the SOAP message, and then send the 202 response over. This is done
with the primary aim of apprising the client about the fact that, the server is
in receipt of the message, and has started processing it. Thereafter, the
client will receive no further intimation of the activities taking place on the
server. This is the reason why we have not specified any return values or
parameters. However, if an exception is thrown, a return packet specifying the
exception, would definitely be sent across.
In the first example of the asmx
file, the function abc returns an int. However, in the a.cs file, the value is
accepted in a string. By default, no data types are sent across. Thus, we need
to be exceedingly cautious about the parameters that are dispatched or
received, since an XML file is capable of incorporating only the textual data.
If you were to change the trace
design to a 'formatted' trace, no return packet would be perceptible at all.
Now, undo the changes made in the a.cs and a.asmx files, and revert them back
to their original position, i.e. at the start of the attribute. The code
contained in the .cs file is shown below.
a.cs
using System.Web.Services.Protocols;
using System.Web.Services.Description;
public class zzz :
System.Web.Services.Protocols.SoapHttpClientProtocol
{
public static void Main()
{
zzz a = new zzz();
a.abc(10,20);
}
[System.Web.Services.Protocols.SoapDocumentMethodAttribute
(ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)]
public string abc(int i , int j)
{
Url = "http://localhost:8080/a.asmx";
object[] results = Invoke("abc", new object[2]{i,j});
return (string)results[0];
}
}
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
public class zzz
{
[WebMethod()]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute()]
public int abc(int i , int j)
{
return i+j;
}
}
SOAP request
<soap:Body>
<i xmlns="http://tempuri.org/">10</i>
<j xmlns="http://tempuri.org/">20</j>
</soap:Body>
SOAP response
<soap:Body>
<abcResponse xmlns="http://tempuri.org/">
<abcResult>0</abcResult>
</abcResponse>
</soap:Body>
Earlier, when we had sent a SOAP
request, we had first specified an element called abc, which is the name of the
function, followed by the names of the parameters. This was imperative, since
the ParameterStyle property had a default value of 'Wrapped'. Moreover, the
return value was depicted as zero. This option ensures that the parameters are
wrapped within the method name, beneath the Body element.
When the value of 'Bare' is
specified in the ParameterStyle, as an outcome, the method name is wiped out
completely. These are the only two styles that are available.
a.asmx
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(ParameterStyle=
System.Web.Services.Protocols.SoapParameterStyle.Bare)]
SOAP response
<soap:Body>
<abcResult
xmlns="http://tempuri.org/">30</abcResult>
</soap:Body>
Modifying the style to 'Bare' in
the asmx file, results in an answer of 30. However, the abcResponse element is
not visible in the output. You may repeat this process in the client program
too.
a.cs
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
RequestElementName="abc1")]
SOAP request
<abc1 xmlns="http://tempuri.org/">
SOAP response
<abcResult>0</abcResult>
The RequestElementName property
alters the name of the function. So, the function abc is now known as 'abc1'.
Resultantly, the element name in the SOAP request packet also becomes abc1.
Since there exists no function called abc1, no error is generated, but the
answer has a value of zero.
a.cs
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
RequestNamespace="mukhi")]
SOAP request
<abc xmlns="mukhi">
SOAP response
<abcResult>0</abcResult>
Next, we alter only the
RequestNamespace property to 'mukhi'. Yet again, no answer is obtained, since
the function abc belongs to the namespace tempuri.org, in the asmx file.
a.asmx
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
RequestNamespace="mukhi")]
SOAP response
<abcResult>30</abcResult>
Now, if we were to modify the
attribute in the asmx file, so that it may contain the same value, the correct
answer would be obtained. It is so because, both the XML entities, i.e. the
functions abc, belong to the same namespace of 'mukhi'.
a.asmx
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
ResponseNamespace="mukhi",ResponseElementName="vijay")]
SOAP response
<vijay xmlns="mukhi">`
<abcResult>0</abcResult>
</vijay>
The same set of rules is
applicable to the return packet too. In the asmx file, the namespace and the
name are both set to 'mukhi' and 'vijay', using the ResponseNamespace and
ResponseElementName, respectively.
Ensure that the same attribute
values are specified in the .cs file also. If this is not done, the SOAP
response goes on to confirm that the changes have been implemented, however,
the client gets thoroughly confused. It happens because, the client expects an
abcResult within an abcResponse parent, in a particular namespace.
a.cs
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
ResponseNamespace="mukhi",ResponseElementName="vijay")]
Output
30
On introducing the same
attribute to the client, the result of '30' is obtained. Thus, it would be a
judicious move to effect similar amendments on both sides, the client side and
the server, since they are generally located on disparate machines, which could
be geographically separated by millions of miles.
a.asmx
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
Use=System.Web.Services.Description.SoapBindingUse.Encoded)]
a.cs
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
Use=System.Web.Services.Description.SoapBindingUse.Encoded)]
SOAP request
<?xml version="1.0" encoding="utf-8"
?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="http://tempuri.org/"
xmlns:types="http://tempuri.org/encodedTypes "
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body
soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<types:abc>
<i xsi:type="xsd:int">10</i>
<j
xsi:type="xsd:int">20</j>
</types:abc>
</soap:Body>
</soap:Envelope>
SOAP response
<?xml version="1.0" encoding="utf-8"
?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="http://tempuri.org/"
xmlns:types="http://tempuri.org/encodedTypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body
soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<types:abcResponse>
<abcResult
xsi:type="xsd:int">30</abcResult>
</types:abcResponse>
</soap:Body>
</soap:Envelope>
The client program and the asmx
file on the server are now introduced to a property called Use, which is
initialized to a value of 'Encoded', using the enum SoapBindingsUse. The only
other value that can be assigned is 'Literal'.
The two values, 'Literal' and 'Encoded',
have been determined by the WSDL standard. The Use property sets the parameter
encoding for the SOAP payload.
The 'Literal' option encodes
parameters, using a predefined XSD schema for each parameter, whereas, the
'Encoded' method uses the rules mentioned in Section 5 of the SOAP
specification. To cut the story short, the Envelope element now has 6
namespaces, instead of the 3 earlier ones. The attribute encodingStyle, from
the soap namespace, explicitly specifies a style encoding. We do not bump into
any method with the name of abc, however, we do come across the method name,
containing the 'types' namespace prefix, which refers to the URI of
http://tempuri.org/encodedTypes.
Within the child elements i and
j, we overtly specify a type, using the type attribute from either the xsd or
XML Schema namespaces. The individual types that are used, are defined by the
XML Schema world. Thus, by employing the encoding style, the process of
transmitting the data types and the contents of the parameters, gets
simplified. The return value also comports itself in a similar fashion. This
encoding behavior is preferred over the default behaviour. It is because, the
other end becomes cognisant of the data type that it has received.
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
public class zzz
{
[WebMethod()]
public void abc()
{
}
}
a.cs
public class zzz :
System.Web.Services.Protocols.SoapHttpClientProtocol
{
public static void Main()
{
zzz a = new zzz();
yyy b = new yyy();
b.i=10;b.k=20;
xxx c = new xxx();
c.x = 100;
b.j = c;
a.abc(b);
}
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
Use=System.Web.Services.Description.SoapBindingUse.Encoded)]
public void abc(yyy a)
{
Url = "http://localhost:8080/a.asmx";
Invoke("abc", new object[1]{a});
}
}
public class yyy {
public int i;
public xxx j;
public int k;
}
public class xxx
{
public int x;
}
SOAP request
<soap:Body
soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<types:abc>
<a href="#id1" />
</types:abc>
<types:yyy id="id1"
xsi:type="types:yyy">
<i xsi:type="xsd:int">10</i>
<j href="#id2" />
<k xsi:type="xsd:int">20</k>
</types:yyy>
<types:xxx id="id2"
xsi:type="types:xxx">
<x xsi:type="xsd:int">100</x>
</types:xxx>
</soap:Body>
In the above C# program, a yyy
object that contains two int members, i and k, is sent across. It also contains
a class xxx member j. The xxx class has only one int x.
The yyy object is now passed as
a parameter to the function abc. The asmx file neither accepts any parameters,
nor does it return any values. As was mentioned earlier, this does not result
in the generation of any exceptions.
The SOAP request is much more
complex than before. A type abc is created, which uses a href to a type id1.
The type yyy has an id of id1, and its type attribute refers to the type yyy,
in other words, it refers to itself. In addition to this, there are three
variables i, j, k, which are followed by their data types and content.
The salient point to remember is
that, since j is of another type, there is an href to id2. The 'id2' is an id,
i.e. a name for an xxx type, enclosing a parameter of type int, and
encompassing the value of 100. This is akin to the methodology used to store
types in a WSDL file. Using the Encoded style, the information about types can
be sent over. Send an array over, and scrutinize the SOAP packet that is
generated.
a.asmx
<%@ WebService Language="C#" Class="zzz"
%>
using System.Web.Services;
public class zzz
{
[WebMethod()]
[System.Web.Services.Protocols.SoapRpcMethodAttribute()]
public int abc(int i)
{
return i;
}
}
a.cs
public class zzz :
System.Web.Services.Protocols.SoapHttpClientProtocol
{
public static void Main()
{
zzz a = new zzz();
int j = a.abc(10);
System.Console.WriteLine(j);
}
[System.Web.Services.Protocols.SoapRpcMethodAttribute()]
public int abc(int i)
{
Url = "http://localhost:8080/a.asmx";
object[] results = Invoke("abc", new object[1]{i});
return (int)results[0];
}
}
SOAP request
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:abc>
<i xsi:type="xsd:int">10</i>
</tns:abc>
</soap:Body>
The last attribute that we touch upon is, the SoapRpcMethodAttribute. This attribute behaves in a manner similar to the attribute mentioned above, apart from the encoding style, which is 'Encoded' in this case. Thus, it is the SOAP payload that carries the type information and the content. Most of the explanations rendered above, are equally relevant in this context too.