/// <summary> /// Caches a web method definition /// </summary> /// <param name="wsType"></param> /// <param name="methods"></param> /// <param name="method"></param> private void AddMethod(Type wsType, Dictionary <string, WebMethodDef> methods, MethodInfo method) { object[] wmAttribs = method.GetCustomAttributes(typeof(WebMethodAttribute), false); if (wmAttribs.Length == 0) { return; } ScriptMethodAttribute sm = null; object[] responseAttribs = method.GetCustomAttributes(typeof(ScriptMethodAttribute), false); if (responseAttribs.Length > 0) { sm = (ScriptMethodAttribute)responseAttribs[0]; } TransactionalMethodAttribute tm = null; object[] tmAttribs = method.GetCustomAttributes(typeof(TransactionalMethodAttribute), false); if (tmAttribs.Length > 0) { tm = (TransactionalMethodAttribute)tmAttribs[0]; } WebMethodDef wmd = new WebMethodDef(this, method, (WebMethodAttribute)wmAttribs[0], sm, tm); methods[wmd.MethodName] = wmd; }
public void Dispose() { this.MethodName = null; this.Target = null; this.ServiceDef = null; this.MethodDef = null; this.Context = null; this.ExtraData = null; }
public void Dispose() { MethodName = null; Target = null; ServiceDef = null; MethodDef = null; Context = null; ExtraData = null; }
internal AsyncWebMethodState(string methodName, IDisposable target, WebServiceDef wsDef, WebMethodDef wmDef, HttpContext context, object extraData) { MethodName = methodName; Target = target; ServiceDef = wsDef; MethodDef = wmDef; Context = context; ExtraData = extraData; }
public WebMethodDef(WebServiceDef wsDef, MethodInfo method, WebMethodAttribute wmAttribute, ScriptMethodAttribute smAttribute, TransactionalMethodAttribute tmAttribute, ETagMethodAttribute emAttribute) { this.MethodType = method; this.WebMethodAtt = wmAttribute; this.ScriptMethodAtt = smAttribute; this.TransactionAtt = tmAttribute; if (null != emAttribute) { this.IsETagEnabled = emAttribute.Enabled; } if (wmAttribute != null && !string.IsNullOrEmpty(wmAttribute.MessageName)) { this.MethodName = wmAttribute.MessageName; } else { this.MethodName = method.Name; } // HTTP GET method is allowed only when there's a [ScriptMethod] attribute and UseHttpGet is true this.IsGetAllowed = (this.ScriptMethodAtt != null && this.ScriptMethodAtt.UseHttpGet); this.ResponseFormat = (this.ScriptMethodAtt != null ? this.ScriptMethodAtt.ResponseFormat : ResponseFormat.Json); MethodInfo beginMethod = wsDef.WSType.GetMethod("Begin" + method.Name, BINDING_FLAGS); if (null != beginMethod) { // The BeginXXX method must have the [ScriptMethod] attribute object[] scriptMethodAttributes = beginMethod.GetCustomAttributes(typeof(ScriptMethodAttribute), false); if (scriptMethodAttributes.Length > 0) { // Asynchronous methods found for the function this.HasAsyncMethods = true; this.BeginMethod = new WebMethodDef(wsDef, beginMethod, null, null, null, null); MethodInfo endMethod = wsDef.WSType.GetMethod("End" + method.Name, BINDING_FLAGS); this.EndMethod = new WebMethodDef(wsDef, endMethod, null, null, null, null); // get all parameters of begin web method and then leave last two parameters in the input parameters list because // last two parameters are for AsyncCallback and Async State ParameterInfo[] allParameters = beginMethod.GetParameters(); ParameterInfo[] inputParameters = new ParameterInfo[allParameters.Length - 2]; Array.Copy(allParameters, inputParameters, allParameters.Length - 2); this.BeginMethod.InputParameters = new List <ParameterInfo>(inputParameters); this.BeginMethod.InputParametersWithAsyc = new List <ParameterInfo>(allParameters); } } this.InputParameters = new List <ParameterInfo>(method.GetParameters()); }
internal AsyncWebMethodState(string methodName, IDisposable target, WebServiceDef wsDef, WebMethodDef wmDef, HttpContext context, object extraData) { this.MethodName = methodName; this.Target = target; this.ServiceDef = wsDef; this.MethodDef = wmDef; this.Context = context; this.ExtraData = extraData; }
/// <summary> /// Execute web method synchronously /// </summary> /// <param name="context"></param> /// <param name="methodDef"></param> /// <param name="serviceDef"></param> private void ExecuteMethod(HttpContext context, WebMethodDef methodDef, WebServiceDef serviceDef) { IDictionary <string, object> inputValues = GetRawParams(context, methodDef.InputParameters, serviceDef.Serializer); object[] parameters = StrongTypeParameters(inputValues, methodDef.InputParameters); object returnValue = null; using (IDisposable target = Activator.CreateInstance(serviceDef.WSType) as IDisposable) { TransactionScope ts = null; try { // If the method has a transaction attribute, then call the method within a transaction scope if (methodDef.TransactionAtt != null) { TransactionOptions options = new TransactionOptions(); options.IsolationLevel = methodDef.TransactionAtt.IsolationLevel; options.Timeout = TimeSpan.FromSeconds(methodDef.TransactionAtt.Timeout); ts = new TransactionScope(methodDef.TransactionAtt.TransactionOption, options); } returnValue = methodDef.MethodType.Invoke(target, parameters); // If transaction was used, then complete the transaction because no exception was // generated if (null != ts) { ts.Complete(); } GenerateResponse(returnValue, context, methodDef, serviceDef); } catch (Exception x) { WebServiceHelper.WriteExceptionJsonString(context, x, serviceDef.Serializer); } finally { // If transaction was started for the method, dispose the transaction. This will // rollback if not committed if (null != ts) { ts.Dispose(); } // Dispose the web service target.Dispose(); } } }
IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { // Proper Content-Type header must be present in order to make a REST call if (!IsRestMethodCall(context.Request)) { return(GenerateErrorResponse(context, "Not a valid REST call", extraData)); } string methodName = context.Request.PathInfo.Substring(1); WebServiceDef wsDef = WebServiceHelper.GetWebServiceType(context, context.Request.FilePath); WebMethodDef methodDef = wsDef.Methods[methodName]; if (null == methodDef) { return(GenerateErrorResponse(context, "Web method not supported: " + methodName, extraData)); } // GET request will only be allowed if the method says so if (context.Request.HttpMethod == "GET" && !methodDef.IsGetAllowed) { return(GenerateErrorResponse(context, "Http Get method not supported", extraData)); } context.Response.Filter = new ResponseFilter(context.Response); // If the method does not have a BeginXXX and EndXXX pair, execute it synchronously if (!methodDef.HasAsyncMethods) { // Do synchronous call ExecuteMethod(context, methodDef, wsDef); // Return a result that says method was executed synchronously return(new AsmxHandlerSyncResult(extraData)); } else { // Call the Begin method of web service IDisposable target = Activator.CreateInstance(wsDef.WSType) as IDisposable; WebMethodDef beginMethod = methodDef.BeginMethod; int allParameterCount = beginMethod.InputParametersWithAsyc.Count; IDictionary <string, object> inputValues = GetRawParams(context, beginMethod.InputParameters, wsDef.Serializer); object[] parameterValues = StrongTypeParameters(inputValues, beginMethod.InputParameters); // Prepare the list of parameter values which will also include the AsyncCallback and the state object[] parameterValuesWithAsync = new object[allParameterCount]; Array.Copy(parameterValues, parameterValuesWithAsync, parameterValues.Length); // Populate last two parameters with async callback and state parameterValuesWithAsync[allParameterCount - 2] = cb; AsyncWebMethodState webMethodState = new AsyncWebMethodState(methodName, target, wsDef, methodDef, context, extraData); parameterValuesWithAsync[allParameterCount - 1] = webMethodState; try { // Invoke the BeginXXX method and ensure the return result has AsyncWebMethodState. This state // contains context and other information which we need in oreder to call the EndXXX IAsyncResult result = beginMethod.MethodType.Invoke(target, parameterValuesWithAsync) as IAsyncResult; // If execution has completed synchronously within the BeginXXX function, then generate response // immediately. There's no need to call EndXXX if (result.CompletedSynchronously) { object returnValue = result.AsyncState; GenerateResponse(returnValue, context, methodDef, wsDef); target.Dispose(); return(new AsmxHandlerSyncResult(extraData)); } else { if (result.AsyncState is AsyncWebMethodState) { return(result); } else { throw new InvalidAsynchronousStateException("The state passed in the " + beginMethod.MethodName + " must inherit from " + typeof(AsyncWebMethodState).FullName); } } } catch (Exception x) { target.Dispose(); WebServiceHelper.WriteExceptionJsonString(context, x, wsDef.Serializer); return(new AsmxHandlerSyncResult(extraData)); } } }
private void GenerateResponse(object returnValue, HttpContext context, WebMethodDef methodDef, WebServiceDef serviceDef) { if (context.Response.Filter.Length > 0) { // Response has already been transmitted by the WebMethod. // So, do nothing return; } string responseString = null; string contentType = "application/json"; if (methodDef.ResponseFormat == System.Web.Script.Services.ResponseFormat.Json) { responseString = "{ d : (" + serviceDef.Serializer.Serialize(returnValue) + ")}"; contentType = "application/json"; } else if (methodDef.ResponseFormat == System.Web.Script.Services.ResponseFormat.Xml) { responseString = returnValue as string; contentType = "text/xml"; } context.Response.ContentType = contentType; // If we have response and no redirection happening and client still connected, send response if (responseString != null && !context.Response.IsRequestBeingRedirected && context.Response.IsClientConnected) { // Produce proper cache. If no cache information specified on method and there's been no cache related // changes done within the web method code, then default cache will be private, no cache. if (IsCacheSet(context.Response) || methodDef.IsETagEnabled) { // Cache has been modified within the code. So, do not change any cache policy } else { // Cache is still private. Check if there's any CacheDuration set in WebMethod int cacheDuration = methodDef.WebMethodAtt.CacheDuration; if (cacheDuration > 0) { // If CacheDuration attribute is set, use server side caching context.Response.Cache.SetCacheability(HttpCacheability.Server); context.Response.Cache.SetExpires(DateTime.Now.AddSeconds(cacheDuration)); context.Response.Cache.SetSlidingExpiration(false); context.Response.Cache.SetValidUntilExpires(true); if (methodDef.InputParameters.Count > 0) { context.Response.Cache.VaryByParams["*"] = true; } else { context.Response.Cache.VaryByParams.IgnoreParams = true; } } else { context.Response.Cache.SetNoServerCaching(); context.Response.Cache.SetMaxAge(TimeSpan.Zero); } } // Check if there's any need to do ETag match. If ETag matches, produce HTTP 304, otherwise // render the content along with the ETag if (methodDef.IsETagEnabled) { string etag = context.Request.Headers["If-None-Match"]; string hash = GetMd5Hash(responseString); if (!string.IsNullOrEmpty(etag)) { if (string.Compare(hash, etag, true) == 0) { // Send no body as response and we will just abort it context.Response.ClearContent(); context.Response.AppendHeader("Content-Length", "0"); context.Response.SuppressContent = true; context.Response.StatusCode = 304; // No need to produce output response body return; } } // ETag comparison did not happen or comparison did not match. So, we need to produce new ETag HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Public); HttpContext.Current.Response.Cache.AppendCacheExtension("must-revalidate, proxy-revalidate"); HttpContext.Current.Response.Cache.SetETag(hash); HttpContext.Current.Response.Cache.SetLastModified(DateTime.Now); int cacheDuration = methodDef.WebMethodAtt.CacheDuration; if (cacheDuration > 0) { context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(cacheDuration)); context.Response.Cache.SetMaxAge(TimeSpan.FromMinutes(cacheDuration)); } else { context.Response.Cache.SetMaxAge(TimeSpan.FromSeconds(10)); } } // Convert the response to response encoding, e.g. utf8 byte[] unicodeBytes = Encoding.Unicode.GetBytes(responseString); byte[] utf8Bytes = Encoding.Convert(Encoding.Unicode, context.Response.ContentEncoding, unicodeBytes); // Emit content length in UTF8 encoding string context.Response.AppendHeader("Content-Length", utf8Bytes.Length.ToString()); // Instead of Response.Write which will convert the output to UTF8, use the internal stream // to directly write the utf8 bytes context.Response.OutputStream.Write(utf8Bytes, 0, utf8Bytes.Length); } else { // Send no body as response and we will just abort it context.Response.AppendHeader("Content-Length", "0"); context.Response.ClearContent(); context.Response.StatusCode = 204; // No Content } }
/// <summary> /// Execute web method synchronously /// </summary> /// <param name="context"></param> /// <param name="methodDef"></param> /// <param name="serviceDef"></param> private void ExecuteMethod(HttpContext context, WebMethodDef methodDef, WebServiceDef serviceDef) { IDictionary<string, object> inputValues = GetRawParams(context, methodDef.InputParameters, serviceDef.Serializer); object[] parameters = StrongTypeParameters(inputValues, methodDef.InputParameters); object returnValue = null; using (IDisposable target = Activator.CreateInstance(serviceDef.WSType) as IDisposable) { TransactionScope ts = null; try { // If the method has a transaction attribute, then call the method within a transaction scope if (methodDef.TransactionAtt != null) { TransactionOptions options = new TransactionOptions(); options.IsolationLevel = methodDef.TransactionAtt.IsolationLevel; options.Timeout = TimeSpan.FromSeconds( methodDef.TransactionAtt.Timeout ); ts = new TransactionScope(methodDef.TransactionAtt.TransactionOption, options); } returnValue = methodDef.MethodType.Invoke(target, parameters); // If transaction was used, then complete the transaction because no exception was // generated if( null != ts ) ts.Complete(); GenerateResponse(returnValue, context, methodDef, serviceDef); } catch (Exception x) { WebServiceHelper.WriteExceptionJsonString(context, x, serviceDef.Serializer); } finally { // If transaction was started for the method, dispose the transaction. This will // rollback if not committed if( null != ts) ts.Dispose(); // Dispose the web service target.Dispose(); } } }
private void GenerateResponse(object returnValue, HttpContext context, WebMethodDef methodDef) { string responseString = null; string contentType = "application/json"; if (methodDef.ResponseFormat == System.Web.Script.Services.ResponseFormat.Json) { responseString = _serializer.Serialize(returnValue); contentType = "application/json"; } else if (methodDef.ResponseFormat == System.Web.Script.Services.ResponseFormat.Xml) { responseString = returnValue as string; contentType = "text/xml"; } context.Response.ContentType = contentType; // If we have response and no redirection happening and client still connected, send response if (responseString != null && !context.Response.IsRequestBeingRedirected && context.Response.IsClientConnected) { // Produce proper cache. If no cache information specified on method and there's been no cache related // changes done within the web method code, then default cache will be private, no cache. if (IsCacheSet(context.Response)) { // Cache has been modified within the code. So, do not change any cache policy } else { // Cache is still private. Check if there's any CacheDuration set in WebMethod int cacheDuration = methodDef.WebMethodAtt.CacheDuration; if (cacheDuration > 0) { // If CacheDuration attribute is set, use server side caching context.Response.Cache.SetCacheability(HttpCacheability.Server); context.Response.Cache.SetExpires(DateTime.Now.AddSeconds(cacheDuration)); context.Response.Cache.SetSlidingExpiration(false); context.Response.Cache.SetValidUntilExpires(true); if (methodDef.InputParameters.Count > 0) { context.Response.Cache.VaryByParams["*"] = true; } else { context.Response.Cache.VaryByParams.IgnoreParams = true; } } else { context.Response.Cache.SetNoServerCaching(); context.Response.Cache.SetMaxAge(TimeSpan.Zero); } } // Convert the response to response encoding, e.g. utf8 byte[] unicodeBytes = Encoding.Unicode.GetBytes(responseString); byte[] utf8Bytes = Encoding.Convert(Encoding.Unicode, context.Response.ContentEncoding, unicodeBytes); // Emit content length in UTF8 encoding string context.Response.AppendHeader("Content-Length", utf8Bytes.Length.ToString()); // Instead of Response.Write which will convert the output to UTF8, use the internal stream // to directly write the utf8 bytes context.Response.OutputStream.Write(utf8Bytes, 0, utf8Bytes.Length); } else { // Send no body as response and we will just abort it context.Response.AppendHeader("Content-Length", "0"); context.Response.ClearContent(); context.Response.StatusCode = 204; // No Content } }
/// <summary> /// Caches a web method definition /// </summary> /// <param name="wsType"></param> /// <param name="methods"></param> /// <param name="method"></param> private void AddMethod(Type wsType, Dictionary<string, WebMethodDef> methods, MethodInfo method) { object[] wmAttribs = method.GetCustomAttributes(typeof(WebMethodAttribute), false); if (wmAttribs.Length == 0) return; ScriptMethodAttribute sm = null; object[] responseAttribs = method.GetCustomAttributes(typeof(ScriptMethodAttribute), false); if (responseAttribs.Length > 0) { sm = (ScriptMethodAttribute)responseAttribs[0]; } TransactionalMethodAttribute tm = null; object[] tmAttribs = method.GetCustomAttributes(typeof(TransactionalMethodAttribute), false); if (tmAttribs.Length > 0) { tm = (TransactionalMethodAttribute)tmAttribs[0]; } ETagMethodAttribute em = null; object[] emAttribs = method.GetCustomAttributes(typeof(ETagMethodAttribute), false); if (emAttribs.Length > 0) { em = (ETagMethodAttribute)emAttribs[0]; } WebMethodDef wmd = new WebMethodDef(this, method, (WebMethodAttribute)wmAttribs[0], sm, tm, em); methods[wmd.MethodName] = wmd; }
public WebMethodDef(WebServiceDef wsDef, MethodInfo method, WebMethodAttribute wmAttribute, ScriptMethodAttribute smAttribute, TransactionalMethodAttribute tmAttribute, ETagMethodAttribute emAttribute) { this.MethodType = method; this.WebMethodAtt = wmAttribute; this.ScriptMethodAtt = smAttribute; this.TransactionAtt = tmAttribute; if( null != emAttribute ) this.IsETagEnabled = emAttribute.Enabled; if (wmAttribute != null && !string.IsNullOrEmpty(wmAttribute.MessageName)) this.MethodName = wmAttribute.MessageName; else this.MethodName = method.Name; // HTTP GET method is allowed only when there's a [ScriptMethod] attribute and UseHttpGet is true this.IsGetAllowed = (this.ScriptMethodAtt != null && this.ScriptMethodAtt.UseHttpGet); this.ResponseFormat = (this.ScriptMethodAtt != null ? this.ScriptMethodAtt.ResponseFormat : ResponseFormat.Json); MethodInfo beginMethod = wsDef.WSType.GetMethod("Begin" + method.Name, BINDING_FLAGS); if (null != beginMethod) { // The BeginXXX method must have the [ScriptMethod] attribute object[] scriptMethodAttributes = beginMethod.GetCustomAttributes(typeof(ScriptMethodAttribute), false); if (scriptMethodAttributes.Length > 0) { // Asynchronous methods found for the function this.HasAsyncMethods = true; this.BeginMethod = new WebMethodDef(wsDef, beginMethod, null, null, null, null); MethodInfo endMethod = wsDef.WSType.GetMethod("End" + method.Name, BINDING_FLAGS); this.EndMethod = new WebMethodDef(wsDef, endMethod, null, null, null, null); // get all parameters of begin web method and then leave last two parameters in the input parameters list because // last two parameters are for AsyncCallback and Async State ParameterInfo[] allParameters = beginMethod.GetParameters(); ParameterInfo[] inputParameters = new ParameterInfo[allParameters.Length - 2]; Array.Copy(allParameters, inputParameters, allParameters.Length - 2); this.BeginMethod.InputParameters = new List<ParameterInfo>(inputParameters); this.BeginMethod.InputParametersWithAsyc = new List<ParameterInfo>(allParameters); } } this.InputParameters = new List<ParameterInfo>(method.GetParameters()); }