2 // WebScriptEnablingBehavior.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2008 Novell, Inc (http://www.novell.com)
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System
.ServiceModel
;
30 using System
.ServiceModel
.Channels
;
31 using System
.ServiceModel
.Dispatcher
;
32 using System
.ServiceModel
.Web
;
33 using System
.Web
.Script
.Services
;
35 namespace System
.ServiceModel
.Description
37 [ServiceContract (Namespace
= "")]
38 internal class InteropScriptService
44 public InteropScriptService (Type type
, string path
, bool debug
)
51 [WebGet (UriTemplate
= "*")]
55 return ProxyGenerator
.GetClientProxyScript (type
, path
, debug
);
59 public sealed class WebScriptEnablingBehavior
: WebHttpBehavior
61 public WebScriptEnablingBehavior ()
63 DefaultBodyStyle
= WebMessageBodyStyle
.WrappedRequest
;
64 DefaultOutgoingRequestFormat
= WebMessageFormat
.Json
;
65 DefaultOutgoingResponseFormat
= WebMessageFormat
.Json
;
68 public override WebMessageBodyStyle DefaultBodyStyle { get; set; }
70 public override WebMessageFormat DefaultOutgoingRequestFormat { get; set; }
72 public override WebMessageFormat DefaultOutgoingResponseFormat { get; set; }
75 protected override void AddClientErrorInspector (ServiceEndpoint endpoint
, ClientRuntime clientRuntime
)
77 base.AddClientErrorInspector (endpoint
, clientRuntime
);
81 protected override void AddServerErrorHandlers (ServiceEndpoint endpoint
, EndpointDispatcher endpointDispatcher
)
83 base.AddServerErrorHandlers (endpoint
, endpointDispatcher
);
87 public override void ApplyClientBehavior (ServiceEndpoint endpoint
, ClientRuntime clientRuntime
)
89 base.ApplyClientBehavior (endpoint
, clientRuntime
);
92 public override void ApplyDispatchBehavior (ServiceEndpoint endpoint
, EndpointDispatcher endpointDispatcher
)
94 base.ApplyDispatchBehavior (endpoint
, endpointDispatcher
);
96 // doing similar to ServiceMetadataExtension
97 BuildScriptDispatcher (endpoint
, endpointDispatcher
, "js", false);
98 BuildScriptDispatcher (endpoint
, endpointDispatcher
, "jsdebug", true);
101 void BuildScriptDispatcher (ServiceEndpoint endpoint
, EndpointDispatcher ed
, string subPath
, bool debug
)
103 var instance
= new InteropScriptService (endpoint
.Contract
.ContractType
, endpoint
.Address
.Uri
.ToString (), debug
);
105 var cdOrg
= ed
.ChannelDispatcher
;
106 var baseUriString
= endpoint
.ListenUri
.ToString ();
107 var uri
= new Uri (String
.Concat (baseUriString
, baseUriString
[baseUriString
.Length
- 1] == '/' ? String
.Empty
: "/", subPath
));
108 var listener
= endpoint
.Binding
.BuildChannelListener
<IReplyChannel
> (uri
);
109 var cd
= new ChannelDispatcher (listener
, String
.Empty
);
110 cd
.MessageVersion
= MessageVersion
.None
;
112 cd
.Endpoints
.Add (new EndpointDispatcher (new EndpointAddress (uri
), "InteropScriptService", String
.Empty
)
113 { ContractFilter = new MatchAllMessageFilter () }
);
115 var dr
= cd
.Endpoints
[0].DispatchRuntime
;
116 var dop
= new DispatchOperation (dr
, "Get", "*", "*");
117 dop
.DeserializeRequest
= false;
118 dop
.SerializeReply
= false;
119 dop
.Invoker
= new DummyInvoker (instance
);
120 dr
.UnhandledDispatchOperation
= dop
;
121 dr
.InstanceContextProvider
= new SingletonInstanceContextProvider (new InstanceContext (cdOrg
.Host
, instance
));
123 var host
= ed
.ChannelDispatcher
.Host
;
124 host
.ChannelDispatchers
.Add (cd
);
127 class DummyInvoker
: IOperationInvoker
129 InteropScriptService instance
;
131 public DummyInvoker (InteropScriptService instance
)
133 this.instance
= instance
;
136 public object [] AllocateInputs ()
138 return new object [0];
141 public object Invoke (object instance
, object [] inputs
, out object [] outputs
)
143 outputs
= new object [0];
144 var msg
= Message
.CreateMessage (MessageVersion
.None
, "*", (object) null);
145 var hp
= new HttpResponseMessageProperty ();
146 hp
.Headers
["Content-Type"] = "text/javascript";
147 msg
.Properties
.Add (HttpResponseMessageProperty
.Name
, hp
);
148 msg
.Properties
.Add (WebMessageEncoder
.ScriptPropertyName
, this.instance
.Get ());
152 public IAsyncResult
InvokeBegin (object instance
, object[] inputs
, AsyncCallback callback
, object state
)
154 throw new NotSupportedException ();
157 public object InvokeEnd (object instance
, out object [] outputs
, IAsyncResult result
)
159 throw new NotSupportedException ();
162 public bool IsSynchronous
{
167 protected override QueryStringConverter
GetQueryStringConverter (OperationDescription operationDescription
)
169 return new JsonQueryStringConverter () { CustomWrapperName = "d"}
;
172 [MonoTODO ("add non-XmlSerializer-ness check (but where?)")]
173 public override void Validate (ServiceEndpoint endpoint
)
175 if (endpoint
== null)
176 throw new ArgumentNullException ("endpoint");
177 ValidateBinding (endpoint
);
179 foreach (var od
in endpoint
.Contract
.Operations
) {
180 var wai
= od
.GetWebAttributeInfo ();
181 if (wai
.UriTemplate
!= null)
182 throw new InvalidOperationException ("UriTemplate must not be used with WebScriptEnablingBehavior");
183 var wia
= od
.Behaviors
.Find
<WebInvokeAttribute
> ();
185 switch (wia
.Method
.ToUpper ()) {
190 throw new InvalidOperationException ("Only GET and POST HTTP methods are valid used for WebScriptEnablingBehavior");
194 var style
= wai
!= null && wai
.IsBodyStyleSetExplicitly
? wai
.BodyStyle
: DefaultBodyStyle
;
195 if (style
!= WebMessageBodyStyle
.WrappedRequest
)
196 throw new NotSupportedException (String
.Format ("WebScriptEnableBehavior only allows WrappedRequest body style, but operation '{0}' uses {1}.", od
.Name
, style
));