2 // Methods.cs: Information about a method and its mapping to a SOAP web service.
6 // Lluis Sanchez Gual (lluis@ximian.com)
8 // (C) 2003 Ximian, Inc.
11 using System.Reflection;
12 using System.Collections;
14 using System.Xml.Serialization;
15 using System.Web.Services;
16 using System.Web.Services.Description;
18 namespace System.Web.Services.Protocols {
21 // This class represents all the information we extract from a MethodInfo
22 // in the SoapHttpClientProtocol derivative stub class
24 internal class SoapMethodStubInfo : MethodStubInfo
26 internal string Action;
27 internal string Binding;
29 // The name/namespace of the request
30 internal string RequestName;
31 internal string RequestNamespace;
33 // The name/namespace of the response.
34 internal string ResponseName;
35 internal string ResponseNamespace;
38 internal SoapParameterStyle ParameterStyle;
39 internal SoapBindingStyle SoapBindingStyle;
40 internal SoapBindingUse Use;
42 internal HeaderInfo[] Headers;
43 internal SoapExtensionRuntimeConfig[] SoapExtensions;
45 internal XmlMembersMapping InputMembersMapping;
46 internal XmlMembersMapping OutputMembersMapping;
48 private int requestSerializerId;
49 private int responseSerializerId;
51 internal XmlSerializer RequestSerializer
53 get { return TypeStub.GetSerializer (requestSerializerId); }
56 internal XmlSerializer ResponseSerializer
58 get { return TypeStub.GetSerializer (responseSerializerId); }
64 public SoapMethodStubInfo (TypeStubInfo typeStub, LogicalMethodInfo source, object kind, XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter)
65 : base (typeStub, source)
67 SoapTypeStubInfo parent = (SoapTypeStubInfo) typeStub;
68 XmlElementAttribute optional_ns = null;
73 RequestNamespace = "";
75 ResponseNamespace = "";
76 ParameterStyle = parent.ParameterStyle;
77 SoapBindingStyle = parent.SoapBindingStyle;
80 else if (kind is SoapDocumentMethodAttribute){
81 SoapDocumentMethodAttribute dma = (SoapDocumentMethodAttribute) kind;
84 if (Use == SoapBindingUse.Default) {
85 if (parent.SoapBindingStyle == SoapBindingStyle.Document)
88 Use = SoapBindingUse.Literal;
92 Binding = dma.Binding;
93 RequestName = dma.RequestElementName;
94 RequestNamespace = dma.RequestNamespace;
95 ResponseName = dma.ResponseElementName;
96 ResponseNamespace = dma.ResponseNamespace;
97 ParameterStyle = dma.ParameterStyle;
98 if (ParameterStyle == SoapParameterStyle.Default)
99 ParameterStyle = parent.ParameterStyle;
101 SoapBindingStyle = SoapBindingStyle.Document;
103 SoapRpcMethodAttribute rma = (SoapRpcMethodAttribute) kind;
104 Use = SoapBindingUse.Encoded; // RPC always use encoded
107 Binding = rma.Binding;
108 RequestName = rma.RequestElementName;
109 RequestNamespace = rma.RequestNamespace;
110 ResponseNamespace = rma.ResponseNamespace;
111 ResponseName = rma.ResponseElementName;
112 ParameterStyle = SoapParameterStyle.Wrapped;
114 SoapBindingStyle = SoapBindingStyle.Rpc;
116 // For RPC calls, make all arguments be part of the empty namespace
117 optional_ns = new XmlElementAttribute ();
118 optional_ns.Namespace = "";
122 if (source.ReturnType != typeof (void))
123 throw new Exception ("OneWay methods should not have a return value");
124 if (source.OutParameters.Length != 0)
125 throw new Exception ("OneWay methods should not have out/ref parameters");
128 if (Binding == null || Binding == "") Binding = parent.DefaultBinding;
129 BindingInfo binfo = parent.GetBinding (Binding);
130 if (binfo == null) throw new InvalidOperationException ("Type '" + parent.Type + "' is missing WebServiceBinding attribute that defines a binding named '" + Binding + "'");
132 string serviceNamespace = binfo.Namespace;
134 if (RequestNamespace == "") RequestNamespace = parent.LogicalType.GetWebServiceNamespace (serviceNamespace, Use);
135 if (ResponseNamespace == "") ResponseNamespace = parent.LogicalType.GetWebServiceNamespace (serviceNamespace, Use);
136 if (RequestName == "") RequestName = Name;
137 if (ResponseName == "") ResponseName = Name + "Response";
138 if (Action == null || Action == "")
139 Action = RequestNamespace.EndsWith("/") ? (RequestNamespace + Name) : (RequestNamespace + "/" + Name);
141 bool hasWrappingElem = (ParameterStyle == SoapParameterStyle.Wrapped);
142 bool writeAccessors = (SoapBindingStyle == SoapBindingStyle.Rpc);
144 XmlReflectionMember [] in_members = BuildRequestReflectionMembers (optional_ns);
145 XmlReflectionMember [] out_members = BuildResponseReflectionMembers (optional_ns);
147 if (Use == SoapBindingUse.Literal) {
148 InputMembersMapping = xmlImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem);
149 OutputMembersMapping = xmlImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem);
152 InputMembersMapping = soapImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem, writeAccessors);
153 OutputMembersMapping = soapImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem, writeAccessors);
156 requestSerializerId = parent.RegisterSerializer (InputMembersMapping);
157 responseSerializerId = parent.RegisterSerializer (OutputMembersMapping);
159 object[] o = source.GetCustomAttributes (typeof (SoapHeaderAttribute));
160 Headers = new HeaderInfo[o.Length];
161 for (int i = 0; i < o.Length; i++) {
162 SoapHeaderAttribute att = (SoapHeaderAttribute) o[i];
163 MemberInfo[] mems = source.DeclaringType.GetMember (att.MemberName);
164 if (mems.Length == 0) throw new InvalidOperationException ("Member " + att.MemberName + " not found in class " + source.DeclaringType.FullName);
166 Type headerType = (mems[0] is FieldInfo) ? ((FieldInfo)mems[0]).FieldType : ((PropertyInfo)mems[0]).PropertyType;
167 Headers [i] = new HeaderInfo (mems[0], att);
168 parent.RegisterHeaderType (headerType, serviceNamespace, Use);
171 SoapExtensions = SoapExtension.GetMethodExtensions (source);
174 XmlReflectionMember [] BuildRequestReflectionMembers (XmlElementAttribute optional_ns)
176 ParameterInfo [] input = MethodInfo.InParameters;
177 XmlReflectionMember [] in_members = new XmlReflectionMember [input.Length];
179 for (int i = 0; i < input.Length; i++)
181 XmlReflectionMember m = new XmlReflectionMember ();
182 m.IsReturnValue = false;
183 m.MemberName = input [i].Name;
184 m.MemberType = input [i].ParameterType;
186 m.XmlAttributes = new XmlAttributes (input[i]);
187 m.SoapAttributes = new SoapAttributes (input[i]);
189 if (m.MemberType.IsByRef)
190 m.MemberType = m.MemberType.GetElementType ();
191 if (optional_ns != null)
192 m.XmlAttributes.XmlElements.Add (optional_ns);
198 XmlReflectionMember [] BuildResponseReflectionMembers (XmlElementAttribute optional_ns)
200 ParameterInfo [] output = MethodInfo.OutParameters;
201 bool has_return_value = !(OneWay || MethodInfo.ReturnType == typeof (void));
202 XmlReflectionMember [] out_members = new XmlReflectionMember [(has_return_value ? 1 : 0) + output.Length];
203 XmlReflectionMember m;
206 if (has_return_value)
208 m = new XmlReflectionMember ();
209 m.IsReturnValue = true;
210 m.MemberName = RequestName + "Result";
211 m.MemberType = MethodInfo.ReturnType;
213 m.XmlAttributes = new XmlAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
214 m.SoapAttributes = new SoapAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
216 if (optional_ns != null)
217 m.XmlAttributes.XmlElements.Add (optional_ns);
222 for (int i = 0; i < output.Length; i++)
224 m = new XmlReflectionMember ();
225 m.IsReturnValue = false;
226 m.MemberName = output [i].Name;
227 m.MemberType = output [i].ParameterType;
228 m.XmlAttributes = new XmlAttributes (output[i]);
229 m.SoapAttributes = new SoapAttributes (output[i]);
231 if (m.MemberType.IsByRef)
232 m.MemberType = m.MemberType.GetElementType ();
233 if (optional_ns != null)
234 m.XmlAttributes.XmlElements.Add (optional_ns);
235 out_members [i + idx] = m;
240 public HeaderInfo GetHeaderInfo (Type headerType)
242 foreach (HeaderInfo headerInfo in Headers)
243 if (headerInfo.HeaderType == headerType) return headerInfo;
248 internal class HeaderInfo
250 internal MemberInfo Member;
251 internal SoapHeaderAttribute AttributeInfo;
252 internal Type HeaderType;
254 public HeaderInfo (MemberInfo member, SoapHeaderAttribute attributeInfo)
257 AttributeInfo = attributeInfo;
258 if (Member is PropertyInfo) HeaderType = ((PropertyInfo)Member).PropertyType;
259 else HeaderType = ((FieldInfo)Member).FieldType;
262 public object GetHeaderValue (object ob)
264 if (Member is PropertyInfo) return ((PropertyInfo)Member).GetValue (ob, null);
265 else return ((FieldInfo)Member).GetValue (ob);
268 public void SetHeaderValue (object ob, object value)
270 if (Member is PropertyInfo) ((PropertyInfo)Member).SetValue (ob, value, null);
271 else ((FieldInfo)Member).SetValue (ob, value);
274 public SoapHeaderDirection Direction
276 get { return AttributeInfo.Direction; }
280 // FIXME: this class should be internal, but it needs to be public in
281 // order to be serialized using XmlSerializer.
282 [XmlRoot (Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
287 public Fault (SoapException ex)
290 faultstring = ex.Message;
291 faultactor = ex.Actor;
295 [XmlElement (Namespace="")]
296 public XmlQualifiedName faultcode;
298 [XmlElement (Namespace="")]
299 public string faultstring;
301 [XmlElement (Namespace="")]
302 public string faultactor;
305 public XmlNode detail;
309 // Holds the metadata loaded from the type stub, as well as
310 // the metadata for all the methods in the type
312 internal class SoapTypeStubInfo : TypeStubInfo
314 Hashtable[] header_serializers = new Hashtable [3];
315 Hashtable[] header_serializers_byname = new Hashtable [3];
316 Hashtable methods_byaction = new Hashtable ();
319 internal SoapParameterStyle ParameterStyle;
320 internal SoapServiceRoutingStyle RoutingStyle;
321 internal SoapBindingUse Use;
322 internal int faultSerializerId = -1;
323 internal SoapExtensionRuntimeConfig[][] SoapExtensions;
324 internal SoapBindingStyle SoapBindingStyle;
325 internal XmlReflectionImporter xmlImporter;
326 internal SoapReflectionImporter soapImporter;
328 public SoapTypeStubInfo (LogicalTypeInfo logicalTypeInfo)
329 : base (logicalTypeInfo)
331 xmlImporter = new XmlReflectionImporter ();
332 soapImporter = new SoapReflectionImporter ();
336 o = Type.GetCustomAttributes (typeof (WebServiceBindingAttribute), false);
337 foreach (WebServiceBindingAttribute at in o)
338 AddBinding (new BindingInfo (at, LogicalType.WebServiceNamespace));
340 o = Type.GetCustomAttributes (typeof (SoapDocumentServiceAttribute), false);
342 SoapDocumentServiceAttribute a = (SoapDocumentServiceAttribute) o [0];
344 ParameterStyle = a.ParameterStyle;
345 RoutingStyle = a.RoutingStyle;
347 SoapBindingStyle = SoapBindingStyle.Document;
349 o = Type.GetCustomAttributes (typeof (SoapRpcServiceAttribute), false);
351 SoapRpcServiceAttribute srs = (SoapRpcServiceAttribute) o [0];
353 ParameterStyle = SoapParameterStyle.Wrapped;
354 RoutingStyle = srs.RoutingStyle;
355 Use = SoapBindingUse.Encoded;
356 SoapBindingStyle = SoapBindingStyle.Rpc;
358 ParameterStyle = SoapParameterStyle.Wrapped;
359 RoutingStyle = SoapServiceRoutingStyle.SoapAction;
360 Use = SoapBindingUse.Literal;
361 SoapBindingStyle = SoapBindingStyle.Document;
365 if (ParameterStyle == SoapParameterStyle.Default) ParameterStyle = SoapParameterStyle.Wrapped;
366 if (Use == SoapBindingUse.Default) Use = SoapBindingUse.Literal;
368 SoapExtensions = SoapExtension.GetTypeExtensions (Type);
371 public override XmlReflectionImporter XmlImporter
373 get { return xmlImporter; }
376 public override SoapReflectionImporter SoapImporter
378 get { return soapImporter; }
381 public override string ProtocolName
383 get { return "Soap"; }
386 protected override MethodStubInfo CreateMethodStubInfo (TypeStubInfo parent, LogicalMethodInfo lmi, bool isClientProxy)
388 SoapMethodStubInfo res = null;
389 object [] ats = lmi.GetCustomAttributes (typeof (SoapDocumentMethodAttribute));
390 if (ats.Length == 0) ats = lmi.GetCustomAttributes (typeof (SoapRpcMethodAttribute));
392 if (ats.Length == 0 && isClientProxy)
394 else if (ats.Length == 0)
395 res = new SoapMethodStubInfo (parent, lmi, null, xmlImporter, soapImporter);
397 res = new SoapMethodStubInfo (parent, lmi, ats[0], xmlImporter, soapImporter);
399 if (faultSerializerId == -1)
401 XmlReflectionImporter ri = new XmlReflectionImporter ();
402 XmlTypeMapping tm = ri.ImportTypeMapping (typeof(Fault));
403 faultSerializerId = RegisterSerializer (tm);
405 methods_byaction [res.Action] = res;
409 public XmlSerializer GetFaultSerializer ()
411 return GetSerializer (faultSerializerId);
414 internal void RegisterHeaderType (Type type, string serviceNamespace, SoapBindingUse use)
416 Hashtable serializers = header_serializers [(int)use];
417 if (serializers == null) {
418 serializers = new Hashtable ();
419 header_serializers [(int)use] = serializers;
420 header_serializers_byname [(int)use] = new Hashtable ();
423 if (serializers.ContainsKey (type))
427 if (use == SoapBindingUse.Literal) {
428 XmlReflectionImporter ri = new XmlReflectionImporter ();
430 // MS.NET reflects header classes in a weird way. The root element
431 // name is the CLR class name unless it is specified in an XmlRootAttribute.
432 // The usual is to use the xml type name by default, but not in this case.
434 XmlRootAttribute root;
435 XmlAttributes ats = new XmlAttributes (type);
436 if (ats.XmlRoot != null) root = ats.XmlRoot;
437 else root = new XmlRootAttribute (type.Name);
439 if (root.Namespace == null) root.Namespace = LogicalType.GetWebServiceLiteralNamespace (serviceNamespace);
440 if (root.ElementName == null) root.ElementName = type.Name;
442 tm = ri.ImportTypeMapping (type, root);
445 SoapReflectionImporter ri = new SoapReflectionImporter ();
446 tm = ri.ImportTypeMapping (type, LogicalType.GetWebServiceEncodedNamespace (serviceNamespace));
449 int sid = RegisterSerializer (tm);
451 serializers [type] = sid;
452 header_serializers_byname [(int)use] [new XmlQualifiedName (tm.ElementName, tm.Namespace)] = sid;
455 internal XmlSerializer GetHeaderSerializer (Type type, SoapBindingUse use)
457 Hashtable table = header_serializers [(int)use];
458 if (table == null) return null;
460 return GetSerializer ((int) table [type]);
463 internal XmlSerializer GetHeaderSerializer (XmlQualifiedName qname, SoapBindingUse use)
465 Hashtable table = header_serializers_byname [(int)use];
466 if (table == null) return null;
468 return GetSerializer ((int) table [qname]);
471 public SoapMethodStubInfo GetMethodForSoapAction (string name)
473 return (SoapMethodStubInfo) methods_byaction [name.Trim ('"',' ')];