WebInvoke Parameter is NULL

2019-08-23 12:42发布

问题:

I have a service where the operation contract looks like the following. I have a WebInvoke attribute and the method is set to POST. I do have a UriTemplate. The actual service method name is SaveUser. I am trying to pass in a User object (a data contract with properties annotated as data member attributes).

[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "SaveUser", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json)]
User SaveUser(User user);

The client looks like the following. For simplicity I have excluded the token and authorization etc.:

using (WebClient webClient = new WebClient())
{
    try
    {
        Random r = new Random();
        var partitionKey = Guid.NewGuid().ToString();
        var rowKey = r.Next(999900, 999999).ToString();

        User u = new User()
        {
            UserId = partitionKey,
            FirstName = "First-" + DateTime.Now.Ticks.ToString(),
            LastName = "Last-" + DateTime.Now.Ticks.ToString(),
            LoginName = rowKey,
            Password = "password1",
            PayPalEmailAddress = "First" + DateTime.Now.Ticks.ToString() + "@verascend.com",
            PhoneNumber = "+1206" + r.Next(1234567, 9999999).ToString()
        };

        string url = serviceBaseUrl + "/SaveUser";

        webClient.Headers["Content-type"] = "application/json; charset=utf-8";
        // webClient.Headers[HttpRequestHeader.Authorization] = authToken;

        DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(User));

        using (var memStream = new MemoryStream())
        {
            ser.WriteObject(memStream, u);

            Debug.WriteLine("-------------> "+ByteArrayToString(memStream.ToArray()));

            webClient.UploadData(url, "POST", memStream.ToArray());

        }
    }
    catch (WebException ex)
    {
        if (ex.Status == WebExceptionStatus.ProtocolError)
        {
            string responseText = string.Empty;

            using (Stream responseStream = ((HttpWebResponse)ex.Response).GetResponseStream())
            {
                using (StreamReader streamReader = new StreamReader(responseStream))
                {
                    responseText = streamReader.ReadToEnd();
                }
            }

            throw new Exception(responseText);
        }
        else
        {
            throw new Exception(ex.Message.ToString());
        }
    }
}

Problem: The service method (actual service) is receiving the param (User) as NULL. What am I doing wrong? I tried adding the known type in the service contract but no luck.

回答1:

Your problem is that you define your operation to have a wrapped request. That means that the parameter, instead of being sent as a "plain" JSON object, must be wrapped in a JSON object, and the member name must correspond to the parameter name (in your case, user). The code below does the wrapping; you can see that with that the parameter now is properly received by the server. Another option would be to change the BodyStyle property to Bare instead of WrappedRequest as you have (in which case you'd need to send the plain object to the service operation).

public class StackOverflow_12452466
{
    [ServiceContract]
    public interface ITest
    {
        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "SaveUser", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json)]
        User SaveUser(User user);
    }

    public class Service : ITest
    {
        public User SaveUser(User user)
        {
            Console.WriteLine("User: {0}", user);
            return user;
        }
    }

    public class User
    {
        public string UserId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string LoginName { get; set; }
        public string Password { get; set; }
        public string PayPalEmailAddress { get; set; }
        public string PhoneNumber { get; set; }

        public override string ToString()
        {
            return string.Format("Id={0},First={1},Last={2},Login={3},Pwd={4},PayPal={5},Phone={6}",
                UserId, FirstName, LastName, LoginName, Password, PayPalEmailAddress, PhoneNumber);
        }
    }

    public static void Test()
    {
        string serviceBaseUrl = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(serviceBaseUrl));
        host.AddServiceEndpoint(typeof(ITest), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
        host.Open();

        Random r = new Random();
        User u = new User()
        {
            UserId = "partitionKey",
            FirstName = "First-" + DateTime.Now.Ticks.ToString(),
            LastName = "Last-" + DateTime.Now.Ticks.ToString(),
            LoginName = "rowKey",
            Password = "password1",
            PayPalEmailAddress = "First" + DateTime.Now.Ticks.ToString() + "@verascend.com",
            PhoneNumber = "+1206" + r.Next(1234567, 9999999).ToString()
        };

        string url = serviceBaseUrl + "/SaveUser";

        WebClient webClient = new WebClient();
        webClient.Headers["Content-type"] = "application/json; charset=utf-8";

        Func<byte[], string> ByteArrayToString = (b) => Encoding.UTF8.GetString(b);
        DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(User));

        try
        {
            using (var memStream = new MemoryStream())
            {
                byte[] wrappingStart = Encoding.UTF8.GetBytes("{\"user\":");
                memStream.Write(wrappingStart, 0, wrappingStart.Length);
                ser.WriteObject(memStream, u);
                byte[] wrappingEnd = Encoding.UTF8.GetBytes("}");
                memStream.Write(wrappingEnd, 0, wrappingEnd.Length);

                Debug.WriteLine("-------------> " + ByteArrayToString(memStream.ToArray()));

                webClient.UploadData(url, "POST", memStream.ToArray());
            }
        }
        catch (WebException ex)
        {
            if (ex.Status == WebExceptionStatus.ProtocolError)
            {
                string responseText = string.Empty;

                using (Stream responseStream = ((HttpWebResponse)ex.Response).GetResponseStream())
                {
                    using (StreamReader streamReader = new StreamReader(responseStream))
                    {
                        responseText = streamReader.ReadToEnd();
                    }
                }

                throw new Exception(responseText);
            }
            else
            {
                throw new Exception(ex.Message.ToString());
            }
        }
    }
}