none
XML Serialization using CORS Message Inspector is not using the Data Contract Serialization RRS feed

  • Question

  • We have a current REST service implemented that returns a list of items like this:

    <Items xmlns="http://schemas.datacontract.org/2004/07/Mns.DataProvider" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <Item i:type="CalculatedItemInfo">
    <StartDate>2017-05-01T00:00:00</StartDate>
    <StartValue>0</StartValue>
    <Type>Absolute</Type>
    <Units>None</Units>
    <UserKey1>1</UserKey1>
    <UserKey2/>
    <UserKey3/>
    <Calculation/>
    </Item>
    <Item i:type="StandardItemInfo">
    <Name>AW Swat|Welland|Subcatchment|LCALC</Name>
    <Calculation/>
    </Item>

    The list has a DataContractAttribute on it as follows:

        [CollectionDataContract(Name = "Items")]
        public class BaseItemInfos : List<BaseItemInfo>
        {
        }

    which all works well. I have now added a CORS message inspector as we need to support CORS which is fairly standard:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Web;
    using System.Text;
    
    namespace Mns.WebService
    {
        public class CorsMessageInspector : IDispatchMessageInspector
        {
            private static readonly HashSet<string> s_helpPageUris = new HashSet<string>(new string[] { "GetHelpPage", "GetOperationHelpPage" }, StringComparer.OrdinalIgnoreCase);
    
            private const string BasicPrefix = "basic ";
            private static readonly int s_basicPrefixLength = BasicPrefix.Length;
            private readonly bool m_enforceBasicAuthentication;
    
            public CorsMessageInspector(bool enforceBasicAuthentication)
            {
                m_enforceBasicAuthentication = enforceBasicAuthentication;
            }
    
            public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
            {
                // Check if the client sent a preflight request for CORS
                HttpRequestMessageProperty httpRequest = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
                if (httpRequest == null) return null;
    
                string origin = httpRequest.Headers["Origin"];
                if (!String.IsNullOrEmpty(origin)) OperationContext.Current.Extensions.Add(new CorsOperationContext(origin));
    
                if (String.Equals(httpRequest.Method, "options", StringComparison.OrdinalIgnoreCase))
                    OperationContext.Current.Extensions.Add(new PreflightOperationContext(httpRequest.Headers["Access-Control-Request-Headers"])); // CORS Preflight
                else if (m_enforceBasicAuthentication)
                {
                    if (PasswordValidator == null) throw new WebFaultException<string>("Access denied", HttpStatusCode.Unauthorized);
    
                    // Allow help pages through without authentication
                    if ((WebOperationContext.Current.IncomingRequest.UriTemplateMatch != null) && s_helpPageUris.Contains(WebOperationContext.Current.IncomingRequest.UriTemplateMatch.Data.ToString())) return null;
    
                    // If not then authorize
                    string authorizationHeader = WebOperationContext.Current.IncomingRequest.Headers[HttpRequestHeader.Authorization];
                    if (!String.IsNullOrEmpty(authorizationHeader))
                    {
                        int basicIndex = authorizationHeader.IndexOf(BasicPrefix, StringComparison.OrdinalIgnoreCase);
                        if (basicIndex != -1)
                        {
                            string encodedUserNamePassword = authorizationHeader.Substring(basicIndex + s_basicPrefixLength);
                            if (!String.IsNullOrEmpty(encodedUserNamePassword))
                            {
                                string userNamePassword = null;
                                try
                                {
                                    userNamePassword = ASCIIEncoding.ASCII.GetString(Convert.FromBase64String(encodedUserNamePassword));
                                }
                                catch (FormatException) { }
                                catch (ArgumentException) { }
    
                                if (!String.IsNullOrEmpty(userNamePassword))
                                {
                                    int colonIndex = userNamePassword.IndexOf(":", StringComparison.OrdinalIgnoreCase);
                                    if (colonIndex != -1)
                                    {
                                        string userName = userNamePassword.Substring(0, colonIndex);
                                        string password = userNamePassword.Substring(colonIndex + 1);
    
                                        if (!String.IsNullOrEmpty(userName))
                                        {
                                            PasswordValidator.Validate(userName, password);
                                            AuthorizationOperationContext.Current.UserName = userName;
                                            AuthorizationOperationContext.Current.ContentType = httpRequest.Headers["Content-Type"];
    
                                            return null;
                                        }
                                    }
                                }
                            }
                        }
                    }
    
                    WebOperationContext.Current.OutgoingResponse.Headers.Add("WWW-Authenticate", @"Basic realm=""MCE""");
                    throw new WebFaultException<string>("Access denied", HttpStatusCode.Unauthorized);
                }
    
                return null;
            }
    
            public void BeforeSendReply(ref Message reply, object correlationState)
            {
                PreflightOperationContext preflightContext = OperationContext.Current.Extensions.Find<PreflightOperationContext>();
                HttpResponseMessageProperty httpHeader = null;
    
                if (preflightContext != null)
                {
                    httpHeader = new HttpResponseMessageProperty() { StatusCode = HttpStatusCode.OK };
                    reply.Properties[HttpResponseMessageProperty.Name] = httpHeader;
                }
                else if (reply != null)
                {
                    httpHeader = reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;
                    httpHeader.Headers["Content-Type"] = AuthorizationOperationContext.Current.ContentType;
                    reply.Properties[WebBodyFormatMessageProperty.Name] = new WebBodyFormatMessageProperty(AuthorizationOperationContext.Current.BodyFormat);
                }
     
                if (httpHeader != null)
                {
                    CorsOperationContext corsContext = OperationContext.Current.Extensions.Find<CorsOperationContext>();
    
                    WebHeaderCollection headers = httpHeader.Headers;
    
                    if (preflightContext != null)
                    {
                        string requestedHeaders = preflightContext.RequestedHeaders;
                        if (!String.IsNullOrEmpty(requestedHeaders)) requestedHeaders += ", ";
                        requestedHeaders += "content-Type, accept, authorization";
    
                        headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
                        headers.Add("Access-Control-Allow-Headers", requestedHeaders);
                    }
    
                    headers.Add("Access-Control-Allow-Credentials", "true");
                    headers.Add("Access-Control-Allow-Origin", (corsContext != null) ? corsContext.Origin : "*");
                }
            }
    
            public static PasswordValidator PasswordValidator { get; set; }
        }
    }
    

    Now we are using a CORS end point the XML is coming back as follows:

    <root type="array">
    <item __type="CalculatedItemInfo:#Mns.DataProvider" type="object">
    <StartDate>/Date(1493593200000+0100)/</StartDate>
    <StartValue type="number">0</StartValue>
    <Type type="number">0</Type>
    <Units>None</Units>
    <UserKey1>1</UserKey1>
    <UserKey2/>
    <UserKey3/>
    <Calculation/>
    </item>-

    So I have lost the name override for the root item list, start date is coming out in some wierd format. It does not seem to be using the DataContractSerializer as it should be. 

    Is there a way to set the serializer to use when using the CORS message inspector?

    Thanks

    Ian

    Wednesday, May 10, 2017 11:50 AM

All replies