Stephen Patten
Stephen Patten

Reputation: 6363

How to POST an xml file to ServiceStack with IRequiresRequestStream

Having read a couple of different SO Posts and the Docs on this subject, I'm still confused and unable to get past the RequestBindingException on my service endpoint. For example, I'd like to POST the following xml to the service with POSTMAN.

<LeadApplications>
  <LeadApplication>
    <Email>[email protected]</Email>
    <FirstName>Joey</FirstName>
    <MiddleName>Disney</MiddleName>
    <LastName>Duck</LastName>
    <Street1>1 Disneyland Street</Street1>
    <Street2>2 Disneyland Street</Street2>
    <City>PAUMA VALLEY</City>
    <State>CA</State>   
    <Zip>92503</Zip>
  </LeadApplication>
</LeadInformations>

[Restrict(RequestAttributes.Xml)]
public class LeadData : IRequiresRequestStream
{
    public Stream RequestStream { get; set; }
}

public object Post(ServiceModel.Types.DirectApi.Legacy.LeadData request)
{
    return null;
}

Routes.Add<ServiceModel.Types.DirectApi.Legacy.LeadData>("/Leads/LeadData/", "POST", "LMS - DirectApi")

I had hope this would be the best way to overcome the .NET deserialization property ordering issues.

Raw Request /Response

POST http://localhost/LO.Leads.Receiver/api/Leads/LeadData/ HTTP/1.1
Host: localhost
Connection: keep-alive
Content-Length: 381
Origin: chrome-extension://aejoelaoggembcahagimdiliamlcdmfm
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2289.0 Safari/537.36
Content-Type: application/xml
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: ss-pid=fhjNz7BGBBqgps6zsnUo; ss-opt=perm; X-UAId=f94092eb-fae8-4b39-b5ff-ae3b404645d8; m=34e2:|41b8:f|4a01:t|ca3:t|b12:f|47ba:t|77cb:t

<LeadApplications>
  <LeadApplication>
    <Email>[email protected]</Email>
    <FirstName>Joey</FirstName>
    <MiddleName>Disney</MiddleName>
    <LastName>Duck</LastName>
    <Street1>1 Disneyland Street</Street1>
    <Street2>2 Disneyland Street</Street2>
    <City>PAUMA VALLEY</City>
    <State>CA</State>   
    <Zip>92503</Zip>
  </LeadApplication>
</LeadApplications>

HTTP/1.1 400 Bad Request
Cache-Control: private
Content-Type: application/xml
Server: Microsoft-IIS/8.5
X-AspNet-Version: 4.0.30319
X-MiniProfiler-Ids: ["6f4255ee84fa45d1a2e05de8a268fc37","a52523648aed4dfaae96e607d07b5163","f9cf295e8e7b4aa1b0579929e61d59a5","7b4daa97fc8d427cb952b17414c4da31","707e7624fa4546c3911a9cb3ce5e6a36"]
X-Powered-By: ASP.NET
Date: Wed, 28 Jan 2015 02:19:30 GMT
Content-Length: 538

<?xml version="1.0" encoding="utf-8"?><ErrorResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.servicestack.net/types"><ResponseStatus><ErrorCode>RequestBindingException</ErrorCode><Message>Unable to bind request</Message><StackTrace>   at ServiceStack.Host.RestHandler.CreateRequest(IRequest httpReq, IRestPath restPath)
   at ServiceStack.Host.RestHandler.ProcessRequestAsync(IRequest httpReq, IResponse httpRes, String operationName)</StackTrace><Errors i:nil="true" /></ResponseStatus></ErrorResponse>

Thank you, Stephen

Upvotes: 1

Views: 804

Answers (1)

mythz
mythz

Reputation: 143319

There was an issue where since the Request Content-Type matched an existing registered Serializer (i.e. XML) that it would still attempt to deserialize the body using that Serializer. It's now fixed to ignore all Serializers for IRequiresRequestStream requests from this commit.

Which lets you now create access the stream and read it to normal XML with:

[Restrict(RequestAttributes.Xml)]
[Route("/Leads/LeadData/", "POST", Notes = "LMS - DirectApi")]
public class CustomXml : IRequiresRequestStream
{
    public Stream RequestStream { get; set; }
}

public class RawRequestService : IService
{
    public object Any(CustomXml request)
    {
        var xml = request.RequestStream.ReadFully().FromUtf8Bytes();
        return xml;
    }
}

Which you can call with HTTP Utils, e.g:

var xml = @"<LeadApplications>
                <LeadApplication>
                <Email>[email protected]</Email>
                <FirstName>Joey</FirstName>
                <MiddleName>Disney</MiddleName>
                <LastName>Duck</LastName>
                <Street1>1 Disneyland Street</Street1>
                <Street2>2 Disneyland Street</Street2>
                <City>PAUMA VALLEY</City>
                <State>CA</State>   
                <Zip>92503</Zip>
                </LeadApplication>
            </LeadApplications>";

var requestUrl = Config.ServiceStackBaseUri + "/Leads/LeadData/";
var responseXml = requestUrl.PostXmlToUrl(xml);

Assert.That(responseXml, Is.EqualTo(xml));

This change is available from v4.0.37+ that's now available on MyGet. It would also work before if you changed the request to use a different Content-Type, e.g. application/xml-lead which doesn't match any existing Serializers, but then you'll need to remove [Restrict] which is only permitting XML requests.

Upvotes: 5

Related Questions