5 // Konstantin Triger <kostat@mainsoft.com>
7 // (C) 2007 Mainsoft, Inc. http://www.mainsoft.com
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Collections.Generic;
33 using System.Web.Script.Serialization;
34 using System.Collections.Specialized;
36 using System.Web.SessionState;
37 using System.Reflection;
39 namespace System.Web.Script.Services
41 sealed class RestHandler : IHttpHandler
43 #region SessionWrappers
45 class SessionWrapperHandler : IHttpHandler, IRequiresSessionState
47 readonly IHttpHandler _handler;
49 public SessionWrapperHandler (IHttpHandler handler) {
53 public bool IsReusable {
54 get { return _handler.IsReusable; }
57 public void ProcessRequest (HttpContext context) {
58 _handler.ProcessRequest (context);
62 sealed class ReadOnlySessionWrapperHandler : SessionWrapperHandler, IReadOnlySessionState
64 public ReadOnlySessionWrapperHandler (IHttpHandler handler) : base (handler) { }
69 #region NameValueCollectionDictionary
71 sealed class NameValueCollectionDictionary : JavaScriptSerializer.LazyDictionary
73 readonly NameValueCollection _nmc;
74 public NameValueCollectionDictionary (NameValueCollection nmc) {
77 protected override IEnumerator<KeyValuePair<string, object>> GetEnumerator () {
78 for (int i = 0, max = _nmc.Count; i < max; i++)
79 yield return new KeyValuePair<string, object> (_nmc.GetKey (i), JavaScriptSerializer.DefaultSerializer.DeserializeObjectInternal (_nmc.Get (i)));
85 #region ExceptionSerializer
87 sealed class ExceptionSerializer : JavaScriptSerializer.LazyDictionary
89 readonly Exception _e;
90 public ExceptionSerializer (Exception e) {
93 protected override IEnumerator<KeyValuePair<string, object>> GetEnumerator () {
94 yield return new KeyValuePair<string, object> ("Message", _e.Message);
95 yield return new KeyValuePair<string, object> ("StackTrace", _e.StackTrace);
96 yield return new KeyValuePair<string, object> ("ExceptionType", _e.GetType ().FullName);
102 readonly LogicalTypeInfo.LogicalMethodInfo _logicalMethodInfo;
104 private RestHandler (HttpContext context, Type type, string filePath) {
105 LogicalTypeInfo logicalTypeInfo = LogicalTypeInfo.GetLogicalTypeInfo (type, filePath);
106 HttpRequest request = context.Request;
107 string methodName = request.PathInfo.Substring (1);
108 if (logicalTypeInfo == null || String.IsNullOrEmpty(methodName))
109 ThrowInvalidOperationException (methodName);
111 _logicalMethodInfo = logicalTypeInfo [methodName];
112 if (_logicalMethodInfo == null)
113 ThrowInvalidOperationException (methodName);
116 static void ThrowInvalidOperationException (string pathInfo) {
117 throw new InvalidOperationException (
118 string.Format ("Request format is unrecognized unexpectedly ending in '{0}'.", pathInfo));
121 static readonly Type IRequiresSessionStateType = typeof (IRequiresSessionState);
122 static readonly Type IReadOnlySessionStateType = typeof (IReadOnlySessionState);
123 public static IHttpHandler GetHandler (HttpContext context, Type type, string filePath) {
124 RestHandler handler = new RestHandler (context, type, filePath);
125 LogicalTypeInfo.LogicalMethodInfo mi = handler._logicalMethodInfo;
126 if (mi.MethodInfo.IsStatic) {
127 if (IRequiresSessionStateType.IsAssignableFrom (type))
128 return IReadOnlySessionStateType.IsAssignableFrom (type) ?
129 new ReadOnlySessionWrapperHandler (handler) : new SessionWrapperHandler (handler);
132 if (mi.WebMethod.EnableSession)
133 return new SessionWrapperHandler (handler);
138 #region IHttpHandler Members
140 public bool IsReusable {
141 get { return false; }
144 public void ProcessRequest (HttpContext context) {
145 HttpRequest request = context.Request;
146 HttpResponse response = context.Response;
147 response.ContentType =
148 _logicalMethodInfo.ScriptMethod.ResponseFormat == ResponseFormat.Json ?
149 "application/json" : "text/xml";
150 response.Cache.SetCacheability (HttpCacheability.Private);
151 response.Cache.SetMaxAge (TimeSpan.Zero);
153 IDictionary<string, object> @params =
154 "GET".Equals (request.RequestType, StringComparison.OrdinalIgnoreCase)
155 ? new NameValueCollectionDictionary (request.QueryString) :
156 (IDictionary<string, object>) JavaScriptSerializer.DefaultSerializer.DeserializeObjectInternal
157 (new StreamReader (request.InputStream, request.ContentEncoding));
160 _logicalMethodInfo.Invoke (@params, response.Output);
162 catch (TargetInvocationException e) {
163 response.AddHeader ("jsonerror", "true");
164 response.ContentType = "application/json";
165 response.StatusCode = 500;
166 JavaScriptSerializer.DefaultSerializer.Serialize (new ExceptionSerializer (e.GetBaseException ()), response.Output);