Gutemberg Ribeiro
Gutemberg Ribeiro

Reputation: 1533

Null parameter with MVC Web-API in OWIN Self-Host

I'm hosting the Web-API using OWIN on a Azure Worker Role following this tutorials:

http://www.asp.net/web-api/overview/hosting-aspnet-web-api/use-owin-to-self-host-web-api http://www.asp.net/web-api/overview/hosting-aspnet-web-api/host-aspnet-web-api-in-an-azure-worker-role

The requests are being routed right. The break point stops as expected inside the controller method but the method parameter that is decorated with [FromBody] attribute, is always coming null.

Bellow are details of my methods and classes:

Request Header:

User-Agent: Fiddler
Content-Type: text/xml; charset=utf-8
Host: localhost:81
Content-Length: 365

Request body:

<?xml version="1.0"?>
<Unit name="ShopActiVID" serialNumber="00123" macAddress="40:d8:55:aa:aa:aa" tzOffset="-0400" useDst="True">
  <Door name="Shop Main" ID="146">
    <count date="2014-06-19T16:58:31.0000000Z" in="0" out="0"/>
  </Door>
  <Door name="Conf. Area" ID="147">
    <count date="2014-06-19T16:58:31.0000000Z" in="2" out="0"/>
  </Door>
</Unit>

Class used as payload for the body:

[DataContract(Name="Unit", IsReference=true)]
    public class AddPeopleCountRequest
    {
        [DataMember(Name="name")]
        public string Name { get; set; }

        [DataMember(Name = "serialNumber")]
        public string SerialNumber { get; set; }

        [DataMember(Name = "macAddress")]
        public string MacAddress { get; set; }

        [DataMember(Name = "tzOffset")]
        public string TzOffset { get; set; }

        [DataMember(Name = "useDst")]
        public bool UseDst { get; set; }

        public List<Door> Doors { get; set; }
    }

    [DataContract(Name="Door", IsReference=true)]
    public class Door 
    {
        [DataMember(Name = "name")]
        public string Name { get; set; }

        [DataMember(Name = "ID")]
        public int Id { get; set; }
        public List<Count> Counts { get; set; }
    }

    [DataContract(Name="count", IsReference=true)]
    public class Count 
    {
        [DataMember(Name = "date")]
        public DateTime Time { get; set; }

        [DataMember(Name = "in")]
        public int In { get; set; }

        [DataMember(Name = "out")]
        public int Out { get; set; }
    }

Controller Method:

[RoutePrefix("api/v1")]
public class CollectorController : ApiController
{
    [HttpPost]
    [Route("count")]
    public async Task<IHttpActionResult> AddPeopleCountRecord([FromBody]AddPeopleCountRequest addCountRequest) 
    {
        ...
        return Ok(); 
    }
}

OWIN Startup class:

public class Startup
    {
        public void Configuration(IAppBuilder app) 
        {
            var config = new HttpConfiguration();
            config.MapHttpAttributeRoutes();
            app.UseWebApi(config);
        }
    }

So, every time I'm making the request on fiddler posting the XML, it never deserialize right into the Action parameter on the controller.

So what am I doing wrong?

Thanks! Really appreciate the help!

Upvotes: 1

Views: 1590

Answers (2)

Gutemberg Ribeiro
Gutemberg Ribeiro

Reputation: 1533

Ok finally got it working.

First into the Startup.cs, I've changed the serializer to:

  public class Startup
    {
        public void Configuration(IAppBuilder app) 
        {
            var config = new HttpConfiguration();
            config.Formatters.XmlFormatter.UseXmlSerializer = true;
            config.MapHttpAttributeRoutes();
            app.UseWebApi(config);
        }
    }

Then, since we are using the old XmlSerializar, we can't keep the DataContract/Member attributes over the properties. We should use instead XmlAttribute/XmlMember as showing in the follow code and the parameter is deserialized right:

[XmlRoot("Unit")]
    public class AddPeopleCountRequest
    {
        [XmlAttribute("name")]
        public string Name { get; set; }

        [XmlAttribute("serialNumber")]
        public string SerialNumber { get; set; }

        [XmlAttribute("macAddress")]
        public string MacAddress { get; set; }

        [XmlElement(ElementName="Door", Type=typeof(Door))]
        public List<Door> Doors { get; set; }
    }


    public class Door 
    {
        [XmlAttribute("name")]
        public string Name { get; set; }

        [XmlAttribute("ID")]
        public int Id { get; set; }

        [XmlElement(ElementName = "count", Type = typeof(Count))]
        public List<Count> Counts { get; set; }
    }

    public class Count 
    {
        [XmlAttribute("date")]
        public DateTime Time { get; set; }

        [XmlAttribute("in")]
        public int In { get; set; }

        [XmlAttribute("out")]
        public int Out { get; set; }
    }

Thanks everybody for the help!

Upvotes: 1

Kiran
Kiran

Reputation: 57949

The issue is with using data (example: name, serialNumber, macAddress) as attributes in xml. By default, Web API's Xml formatter uses DataContractSerializer as the serializer. This serializer does not support having data as attributes in the Xml. You would need to use XmlSerializer for this. You can change the settings of the default formatter to use this serializer.

Also in general, if you do not see your action's parameter to be populated, then you can check for ModelState validity to find errors, if any.

if (!ModelState.IsValid)
{
    return BadRequest(this.ModelState);
}

Upvotes: 2

Related Questions