예제 #1
0
        /// <summary>
        /// Called after an inbound message has been received but before the message is dispatched to the intended operation.
        /// </summary>
        /// <param name="request">The request message.</param>
        /// <param name="channel">The incoming channel.</param>
        /// <param name="instanceContext">The current service instance.</param>
        /// <returns>The object used to correlate state. This object is passed back in the <see cref="M:System.ServiceModel.Dispatcher.IDispatchMessageInspector.BeforeSendReply(System.ServiceModel.Channels.Message@,System.Object)" /> method.</returns>
        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            if (!request.Properties.ContainsKey("Via"))
            {
                return(request);                                        // Nothing much we can do here
            }
            if (!request.Properties.ContainsKey("httpRequest"))
            {
                return(request);                                                // Same here
            }
            var httpRequest = request.Properties["httpRequest"] as HttpRequestMessageProperty;

            if (httpRequest == null)
            {
                return(request);
            }
            var httpMethod = httpRequest.Method.ToUpper();

            var uri = request.Properties["Via"] as Uri;

            if (uri == null)
            {
                return(request);             // Still nothing much we can do
            }
            var url         = uri.AbsoluteUri;
            var urlFragment = url;

            if (urlFragment.ToLower().StartsWith(_rootUrlLower))
            {
                urlFragment = urlFragment.Substring(_rootUrlLower.Length);
            }
            var operationInfo = RestHelper.GetMethodNameFromUrlFragmentAndContract(urlFragment, httpMethod, _contractType);
            var urlParameters = RestHelper.GetUrlParametersFromUrlFragmentAndContract(urlFragment, httpMethod, _contractType);

            if (httpMethod == "GET")
            {
                // TODO: Support GET if at all possible
                throw new Exception("REST-GET operations are not currently supported in the chosen hosting environment. Please use a different HTTP Verb, or host in a different environment (such as WebApi). We hope to add this feature in a future version.");

                // This is a REST GET operation. Therefore, there is no posted message. Instead, we have to decode the input parameters from the URL
                var parameters = operationInfo.GetParameters();
                if (parameters.Length != 1)
                {
                    throw new NotSupportedException("Only service methods/operations with a single input parameter can be mapped to REST-GET operations. Method " + operationInfo.Name + " has " + parameters.Length + " parameters. Consider changing the method to have a single object with multiple properties instead.");
                }
                var parameterType     = parameters[0].ParameterType;
                var parameterInstance = Activator.CreateInstance(parameterType);
                foreach (var propertyName in urlParameters.Keys)
                {
                    var urlProperty = parameterType.GetProperty(propertyName);
                    if (urlProperty == null)
                    {
                        continue;
                    }
                    urlProperty.SetValue(parameterInstance, urlParameters[propertyName], null);
                }

                // Seralize the object back into a new request message
                // TODO: We only need to do this for methods OTHER than GET
                var format = GetMessageContentFormat(request);
                switch (format)
                {
                case WebContentFormat.Xml:
                    var xmlStream     = new MemoryStream();
                    var xmlSerializer = new DataContractSerializer(parameterInstance.GetType());
                    xmlSerializer.WriteObject(xmlStream, parameterInstance);
                    var xmlReader     = XmlDictionaryReader.CreateTextReader(StreamHelper.ToArray(xmlStream), XmlDictionaryReaderQuotas.Max);
                    var newXmlMessage = Message.CreateMessage(xmlReader, int.MaxValue, request.Version);
                    newXmlMessage.Properties.CopyProperties(request.Properties);
                    newXmlMessage.Headers.CopyHeadersFrom(request.Headers);
                    if (format == WebContentFormat.Default)
                    {
                        if (newXmlMessage.Properties.ContainsKey(WebBodyFormatMessageProperty.Name))
                        {
                            newXmlMessage.Properties.Remove(WebBodyFormatMessageProperty.Name);
                        }
                        newXmlMessage.Properties.Add(WebBodyFormatMessageProperty.Name, WebContentFormat.Xml);
                    }
                    request = newXmlMessage;
                    break;

                case WebContentFormat.Default:
                case WebContentFormat.Json:
                    var jsonStream = new MemoryStream();
                    var serializer = new DataContractJsonSerializer(parameterInstance.GetType());
                    serializer.WriteObject(jsonStream, parameterInstance);
                    var jsonReader = JsonReaderWriterFactory.CreateJsonReader(StreamHelper.ToArray(jsonStream), XmlDictionaryReaderQuotas.Max);
                    var newMessage = Message.CreateMessage(jsonReader, int.MaxValue, request.Version);
                    newMessage.Properties.CopyProperties(request.Properties);
                    newMessage.Headers.CopyHeadersFrom(request.Headers);
                    if (format == WebContentFormat.Default)
                    {
                        if (newMessage.Properties.ContainsKey(WebBodyFormatMessageProperty.Name))
                        {
                            newMessage.Properties.Remove(WebBodyFormatMessageProperty.Name);
                        }
                        newMessage.Properties.Add(WebBodyFormatMessageProperty.Name, WebContentFormat.Json);
                    }
                    request = newMessage;
                    break;

                default:
                    throw new NotSupportedException("Mesage format " + format.ToString() + " is not supported form REST/JSON operations");
                }
            }

            return(null);
        }
예제 #2
0
        /// <summary>
        /// Inspects the URL fragment, trims the method name (if appropriate) and returns the remaining parameters as a dictionary
        /// of correlating property names and their values
        /// </summary>
        /// <param name="urlFragment">The URL fragment.</param>
        /// <param name="httpMethod">The HTTP method.</param>
        /// <param name="contractType">Service contract types.</param>
        /// <returns>Dictionary of property values</returns>
        public static Dictionary <string, object> GetUrlParametersFromUrlFragmentAndContract(string urlFragment, string httpMethod, Type contractType)
        {
            if (urlFragment.StartsWith("/"))
            {
                urlFragment = urlFragment.Substring(1);
            }
            var firstParameter = string.Empty;

            if (urlFragment.IndexOf("/", StringComparison.Ordinal) > -1)
            {
                firstParameter = urlFragment.Substring(0, urlFragment.IndexOf("/", StringComparison.Ordinal));
            }
            else if (!string.IsNullOrEmpty(urlFragment))
            {
                firstParameter = urlFragment;
            }

            var        methods     = ObjectHelper.GetAllMethodsForInterface(contractType);
            MethodInfo foundMethod = null;

            foreach (var method in methods)
            {
                var restAttribute       = GetRestAttribute(method);
                var httpMethodForMethod = restAttribute.Method.ToString().ToUpper();

                if (!string.Equals(httpMethod, httpMethodForMethod, StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }
                var methodName = method.Name;
                if (!string.IsNullOrEmpty(restAttribute.Name))
                {
                    methodName = restAttribute.Name;
                }
                if (!string.Equals(methodName, firstParameter, StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }
                urlFragment = urlFragment.Substring(methodName.Length);
                if (urlFragment.StartsWith("/"))
                {
                    urlFragment = urlFragment.Substring(1);
                }
                foundMethod = method;
                break; // We found our methoid
            }

            if (foundMethod == null) // We haven't found our method yet. If there is a default method (a method with an empty REST name) that matches the HTTP method, we will use that instead
            {
                foreach (var method in methods)
                {
                    var restAttribute = GetRestAttribute(method);
                    if (restAttribute.Name != "")
                    {
                        continue;
                    }
                    var httpMethodForMethod = restAttribute.Method.ToString().ToUpper();

                    if (!string.Equals(httpMethod, httpMethodForMethod, StringComparison.OrdinalIgnoreCase))
                    {
                        continue;
                    }
                    foundMethod = method;
                    break; // We found our methoid
                }
            }

            if (foundMethod == null)
            {
                return(new Dictionary <string, object>());                    // We didn't find a match, therefore, we can't map anything
            }
            var foundMethodParameters = foundMethod.GetParameters();

            if (foundMethodParameters.Length != 1)
            {
                return(new Dictionary <string, object>());                                  // The method signature has multiple parameters, so we can't handle it (Note: Other code in the chain will probably throw an exception about it, so we just return out here as we do not want duplicate exceptions)
            }
            var firstParameterType = foundMethodParameters[0].ParameterType;

            // Ready to extract the parameters
            var inlineParameterString = string.Empty;
            var namedParameterString  = string.Empty;
            var separatorPosition     = urlFragment.IndexOf("?", StringComparison.Ordinal);

            if (separatorPosition > -1)
            {
                inlineParameterString = urlFragment.Substring(0, separatorPosition);
                namedParameterString  = urlFragment.Substring(separatorPosition + 1);
            }
            else
            {
                if (urlFragment.IndexOf("=", StringComparison.Ordinal) > -1)
                {
                    namedParameterString = urlFragment;
                }
                else
                {
                    inlineParameterString = urlFragment;
                }
            }

            var dictionary = new Dictionary <string, object>();

            // Parsing the inline parameters
            if (!string.IsNullOrEmpty(inlineParameterString))
            {
                var inlineParameters = inlineParameterString.Split('/');
                var inlineProperties = RestHelper.GetOrderedInlinePropertyList(firstParameterType);
                for (var propertyCounter = 0; propertyCounter < inlineParameters.Length; propertyCounter++)
                {
                    if (propertyCounter >= inlineProperties.Count)
                    {
                        break;                                            // We overshot the available parameters for some reason
                    }
                    var parameterString = HttpHelper.UrlDecode(inlineParameters[propertyCounter]);
                    var parameterValue  = ConvertValue(parameterString, inlineProperties[propertyCounter].PropertyType);
                    dictionary.Add(inlineProperties[propertyCounter].Name, parameterValue);
                }
            }

            // Parsing the named parameters
            if (!string.IsNullOrEmpty(namedParameterString))
            {
                var parameterElements = namedParameterString.Split('&');
                foreach (var parameterElement in parameterElements)
                {
                    var parameterNameValuePair = parameterElement.Split('=');
                    if (parameterNameValuePair.Length != 2)
                    {
                        continue;
                    }
                    var currentProperty = firstParameterType.GetProperty(parameterNameValuePair[0]);
                    if (currentProperty == null)
                    {
                        continue;
                    }
                    var currentPropertyString = HttpHelper.UrlDecode(parameterNameValuePair[1]);
                    var currentPropertyValue  = ConvertValue(currentPropertyString, currentProperty.PropertyType);
                    dictionary.Add(parameterNameValuePair[0], currentPropertyValue);
                }
            }

            return(dictionary);
        }