gregpakes
gregpakes

Reputation: 4535

How to get ServiceStack to receive XML request from TFS with XML namespaces

I am trying to wire up TFS 2012 event notifications to ServiceStack, but I just keep getting the error:

{
    "responseStatus": {
        "errorCode": "RequestBindingException",
        "message": "Unable to bind request",
        "stackTrace": "at ServiceStack.WebHost.Endpoints.RestHandler.GetRequest(IHttpRequest httpReq, IRestPath restPath)\r\n   
                       at ServiceStack.WebHost.Endpoints.RestHandler.ProcessRequest (IHttpRequest httpReq, IHttpResponse httpRes, String operationName)"
    }
}

Here is my DTO (It contains some bits that I have tried to make it work).

[Route("/releasemanagement/tfsevents/buildcomplete", "POST")]
[DataContract(Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public class TfsEventBuildComplete : IReturn<BuildCompleteResponse>
{
    [DataMember]
    public string BuildUri { get; set; }

    [DataMember]
    public string TeamFoundationServerUrl { get; set; }

    [DataMember]
    public string TeamProject { get; set; }

    [DataMember]
    public string Title { get; set; }

    [DataMember]
    public string Id { get; set; }

    [DataMember]
    public TfsEventStatusChange StatusChange { get; set; }

    [DataMember]
    public string ChangedBy { get; set; }
}

public class TfsEventStatusChange
{
    public string FieldName { get; set; }

    public string NewValue { get; set; }
}

Here is the XML that comes from TFS:

<BuildStatusChangeEvent xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <BuildUri>vstfs:///Build/Build/4799</BuildUri>
    <TeamFoundationServerUrl>http://TFSURL:8080/tfs/collection</TeamFoundationServerUrl>
    <TeamProject>iSAMS</TeamProject>
    <Title>iSAMS Build Release_20130814.1 Quality Changed To Publish</Title>
    <Subscriber>[02]\Project Collection Service Accounts</Subscriber>
    <Id>Release_20130814.1</Id>
    <Url>http://TFSURL:8080/tfs/web/build.aspx?pcguid=GUID&amp;builduri=vstfs:///Build/Build/4799</Url>
    <TimeZone>GMT Daylight Time</TimeZone>
    <TimeZoneOffset>+01:00:00</TimeZoneOffset>
    <ChangedTime>8/14/2013 9:33:30 AM</ChangedTime>
    <StatusChange>
        <FieldName>Quality</FieldName>
        <NewValue>Publish</NewValue>
    </StatusChange>
    <ChangedBy>DOMAIN\USER</ChangedBy>
</BuildStatusChangeEvent>

I think ServiceStack is effectively failing to deserialise the request due to the namespacing, but I can't figure it out.

Most of what I have tried has come from here:

XML deserializing only works with namespace in xml

Upvotes: 4

Views: 722

Answers (1)

Mike Mertsock
Mike Mertsock

Reputation: 12015

Here's what I did to get the sample request XML to work:

  1. Set the Name property of your DataContract attribute to BuildStatusChangeEvent. This controls the expected name of the top-level XML element. Otherwise ServiceStack will expect the XML to look like <TfsEventBuildComplete><BuildUri>...
  2. Set the Namespace property of your DataContract attribute to an empty string, so that no specific xmlns attributes are required in the request
  3. Set the Order property of your DataMember attributes to specify the order in which the XML elements are sorted in the XML you are getting from TFS. By default, the XML serializer will expect the elements to be sorted alphabetically, so you must override this or some portion of your properties won't get deserialized correctly
[Route("/releasemanagement/tfsevents/buildcomplete", "POST")]
[DataContract(Namespace = "", Name = "BuildStatusChangeEvent")]
public class TfsEventBuildComplete : IReturn<BuildCompleteResponse>
{
    [DataMember(Order=1)]
    public string BuildUri { get; set; }

    [DataMember(Order=2)]
    public string TeamFoundationServerUrl { get; set; }

    [DataMember(Order=3)]
    public string TeamProject { get; set; }

    [DataMember(Order=4)]
    public string Title { get; set; }

    [DataMember(Order=5)]
    public string Id { get; set; }

    [DataMember(Order=6)]
    public TfsEventStatusChange StatusChange { get; set; }

    [DataMember(Order=7)]
    public string ChangedBy { get; set; }
}

Upvotes: 5

Related Questions