?
Solved

Change Input Content Type on Webservice

Posted on 2012-09-12
26
Medium Priority
?
2,004 Views
Last Modified: 2012-09-23
I have a webservice that is up and running, and can accept URL encoded input using this content type:

 "application/x-www-form-urlencoded"

Unfortunately, one of the vendors who will be using this service is using CastIron, which apparently doesn't support this method, and instead is using the POST binding (apparently this program can only send "text/xml"). Every time they try to hit the service they receive the typical "Internal Server Error 500: Invalid Request Format". I built a mini-utility to test the service, and all is fine until I set the ContentType of my WebRequest to anything other than the one referenced above. When I do that, I too get the same message.

In reviewing my WSDL, I see where the POST binding is set for an InputType of "application/x-www-form-url-encoded":

- <wsdl:binding name="ProcessSOHttpPost" type="tns:ProcessSOHttpPost">
  <http:binding verb="POST" /> 
- <wsdl:operation name="SalesOrderIn">
  <http:operation location="/SalesOrderIn" /> 
- <wsdl:input>
  <mime:content type="application/x-www-form-urlencoded" /> 
  </wsdl:input>
- <wsdl:output>
  <mime:content part="Body" type="text/xml" /> 
  </wsdl:output>
  </wsdl:operation>
  </wsdl:binding>

Open in new window

Can I change that to accept "text/xml", or add a binding that will work with Text/XML? If so, how do I do that?
0
Comment
  • 13
  • 13
26 Comments
 
LVL 96

Expert Comment

by:Bob Learned
ID: 38396194
1) What type of web service are you talking about (classic .asmx or WCF)?

2) I don't quite understand how the web service is configured, but it almost sounds like you just need a web method that accepts a string argument, and you could parse the XML...
0
 
LVL 85
ID: 38396474
It's an .asmx service.

The web method does accept a string argument (which is actually an XML file), and I parse that document.

My issue is that the clientside process is using the POST method, but is sending that data using CastIron (an XML transport platform), and my POST method seems to only accept URL encoded connections - and CastIron apparently does not send URL encoded, only "text/xml".

For example, if I build a small clientside utility using a typical WebRequest, I can easily connect like this:

rq = WebRequest.Create(txWebSvc.Text)
rq.Method = "POST"
rq.ContentType = "application/x-www-form-urlencoded"
etc etc

I can connect to the webservice and everything is fine.

If I do this:

rq = WebRequest.Create(txWebSvc.Text)
rq.Method = "POST"
rq.ContentType = "text/xml"
etc etc

I get "Invalid Request Format".

The client MUST connect as in the second example, for some unknown reason. I've tried to work with them to modify their methods, but they aren't budging on it.

So I need to configure my webservice such that the POST binding can accept "text/xml", and not "application/x-www-form-urlencoded".

Clear as mud :) ?
0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 38396620
Are you allowing POST in the web.config?

<webServices>
    <protocols>
        <add name="HttpGet"/>
        <add name="HttpPost"/>
    </protocols>
</webServices>

Open in new window

0
Put Machine Learning to Work--Protect Your Clients

Machine learning means Smarter Cybersecurity™ Solutions.
As technology continues to advance, managing and analyzing massive data sets just can’t be accomplished by humans alone. It requires huge amounts of memory and storage, as well as the high-speed power of the cloud.

 
LVL 85
ID: 38396737
Yes, I have both of those in my Web.Config file.

I can use standard URL encoding, with the webrequest set to the POST method. If the client uses their method (i.e. the text/html POST method) they receive the "Invalid Format" response. I get the same thing if I  use the second example above, where the content type is "text/xml".
0
 
LVL 96

Assisted Solution

by:Bob Learned
Bob Learned earned 2000 total points
ID: 38397057
Do you have any attributes on the web method, like ScriptMethodAttribute with RequestFormat and/or ResponseFormat values?

Using the ASP.NET AJAX ScriptMethodAttribute to Return XML Data
http://weblogs.asp.net/dwahlin/archive/2007/07/23/using-the-asp-net-ajax-scriptmethodattribute-to-return-xml-data.aspx

[ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
[WebMethod]
public XmlElement GetRssFeed(string url)
{
    XmlDocument doc = new XmlDocument();
    doc.Load(url);
    return doc.DocumentElement;
}

Open in new window

0
 
LVL 85
ID: 38398240
Thanks for you help on this. I really appreciate it.

I don't see anything that deals with the RequestFormat value of the ScriptMethodAttribute - I followed those links, and searched quite a bit for info on that, and couldn't find anything.

My issue is not in the Response. The webservice will respond to URL encoded calls, just not text/xml call via the POST method.

So I need to somehow tell my webservice to use the "content type = "text/xml"" input when a client uses the POST method.

I know there is something basic I'm missing, so please make the assumption that I know absolutely nothing about webservices and such (and your assumption would be very, very close to true, in that case <grin>)
0
 
LVL 96

Assisted Solution

by:Bob Learned
Bob Learned earned 2000 total points
ID: 38398321
My mind is stuck in the WCF world, so it pulled RequestFormat from there:

[OperationContract]
    [WebInvoke(
        BodyStyle = WebMessageBodyStyle.Bare,
        RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json,
        UriTemplate = "x/y/z")]

I am having to dig down deep to remember classic .asmx web services...
0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 38398324
There is a lot that you need to be concerned with to get web services to act just right.

Evidence to that fact can be found here:

Request format is invalid: application/json; charset=UTF-8
http://royaltutorials.com/request-format-is-invalid-application-json-charset-utf-8/
0
 
LVL 85
ID: 38398788
I'm working with a legacy app that hasn't been used in a while. I'm trying to convince them to move to WCF, but so far no joy on that front.

I'll keep looking around ... thanks again for the help and the link, please let me know if you come up with anything else.
0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 38399160
WCF is a huge change from classic .asmx web services, and sometimes people are afraid of changed.  That is not a good thing in the software business, as it changes quite rapidly, and you need to work very hard to keep up.

You might use the fact that this may not be supportable in classic web services, and you would need a new WCF service to support any external system requirements, as leverage to get WCF up and running.
0
 
LVL 85
ID: 38403856
I think I've talked them into moving to WCF, so I've been playing around with it a bit this weekend.

The goal of the webservice is to accept XML data from their customers, and store that data on disk (we'll then pick it up from disk and do other work with it).

I've built the WCF service and published it to the server. I can use URL name/value pairs to get to the service, but the client must use POST to manage this (they're using CastIron, and apparently have very little leeway in the methods they can use). The service accepts an incoming String value, and then just writes that to disk. My code is below:
Imports System.IO
Imports System.Net
Imports System.ServiceModel
Imports System.ServiceModel.Description
Imports System.ServiceModel.Web

<ServiceContract()> _
Public Interface ISalesOrderIn

    <OperationContract()> _
    <WebInvoke(bodystyle:=WebMessageBodyStyle.Wrapped, Method:="POST", RequestFormat:=WebMessageFormat.Xml, ResponseFormat:=WebMessageFormat.Xml,
              UriTemplate:="/PODataIn?DataCardIn={DataCardIn}")> _
    Function PODataIn(ByVal DataCardIn As String) As String

End Interface

Open in new window

Here's the SalesOrderIn.vb code:
Imports System.Xml
Imports System.IO
Imports System.Net
Imports System.ServiceModel
Imports System.ServiceModel.Description
Imports System.ServiceModel.Web
Imports System.Web

Public Class SalesOrderIn
    Implements ISalesOrderIn

    Public Function PODataIn(ByVal DataCardIn As String) As String Implements ISalesOrderIn.PODataIn

         Dim sFile As String = "C:\JobImportFiles\temp_in.txt"

        Using s As New System.IO.StreamWriter(sFile) 'filePath & "\temp_in.txt")

            If DataCardIn Is Nothing Then
                s.Write("No data")
            Else
                s.Write(DataCardIn.ToString)
            End If

            s.Flush()
            s.Close()
        End Using

        Return "Received data"

    End Function
 
   'Public Function GetData(ByVal value As Integer) As String Implements ISalesOrderIn.GetData
    '    Return String.Format("You entered: {0}", value)
    'End Function

End Class

Open in new window

The service works, and will always create the file at C:\JobImport, but it does not include the data from DataCardIn, so it always creates a file with "No Data" as the only line.

What do I need to do to get the data passed in by the user via POST into my PODataIn function?

I'm using Fiddler to test this; here's the content I passed in:

POST http://12.345.678.90/DataImport/SalesOrderIn.svc/PODataIn HTTP/1.1
User-Agent: Fiddler
Host: 12.345.678.90
Content-Type: text/xml
Content-Length: 29

<XML><Head>Scott</Head></XML>

Open in new window


I also tried this:

POST http://12.345.678.90/DataImport/SalesOrderIn.svc/PODataIn HTTP/1.1
User-Agent: Fiddler
Host: 12.345.678.90
Content-Type: text/xml
Content-Length: 40

PODataIn="<XML><Head>Scott</Head></XML>"

Open in new window

And I've tried many different variations of the POST request.
0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 38403884
Cool, I am much more comfortable with WCF, even though there are still a bit of details that I need to wrap my head around.

Let me ask you some environment questions:

1) Framework version

2) WCF service type (REST, regular)?

REST services might give you what you need, but not a necessary requirement.  I am really starting to like REST services for a certain type of problem.
0
 
LVL 85
ID: 38404714
.NET 3.5 framework.

I'm building a REST service, I believe (again, I'm a novice at WCF stuff too). I used the WCF Service Application template in VS 2010 to build this, and have been following tutorials on building RESTful services - however, I'm certainly open for other suggestions (and the simpler the better, of course).
0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 38405613
I can see the evidence, now, that you are using a REST service, since UriTemplate are used with REST.

 UriTemplate:="/PODataIn?DataCardIn={DataCardIn}"

Is SalesOrderIn the service class, because I don't see any decorations to indicate that (i.e. where is the OperationContract attribute)?
0
 
LVL 85
ID: 38405816
SalesOrderIn is a separate class (see the second code block above). So I have this in the service class:

<ServiceContract(NameSpace:="http://my.namespace.com")> _
Public Interface ISalesOrderIn

'<OperationContract()> _
    '    <WebInvoke(bodystyle:=WebMessageBodyStyle.Wrapped, Method:="POST", RequestFormat:=WebMessageFormat.Xml, ResponseFormat:=WebMessageFormat.Xml,
    '      UriTemplate:="/PODataIn?DataCardIn={DataCardIn}")>
    'Function PODataIn(ByVal DataCardIn As DataCardData) As String

<OperationContract()> _
    <WebInvoke(bodystyle:=WebMessageBodyStyle.Wrapped, Method:="POST", RequestFormat:=WebMessageFormat.Xml, ResponseFormat:=WebMessageFormat.Xml)> _
    Function PODataIn(ByVal DataCardIn As String) As String

End Interface

<DataContract(Name:="MSXML", NameSpace:="http://my.namespace.com")> _
Public Class DataCardData
    Private _XMLIn As XElement ' String

    <DataMember()> _
    Property PODataIn As XElement ' String
        Get
            PODataIn = _XMLIn
        End Get
        Set(value As XElement) ' String)
            _XMLIn = value
        End Set
    End Property

End Class

Open in new window

I've tried it a few different ways (currently have removed the UriTemplate decoration).

And I have this in the SalesOrderIn class:

Public Class SalesOrderIn
    Implements ISalesOrderIn

Public Function PODataIn(ByVal DataCardIn As String) As String Implements ISalesOrderIn.PODataIn
        Dim sFile As String = "C:\JobImportFiles\temp_in.txt"

        Using s As New System.IO.StreamWriter(sFile) 'filePath & "\temp_in.txt")

            If DataCardIn Is Nothing Then
                s.Write("No data")
            Else
                s.Write(DataCardIn.ToString.Trim)
            End If

            s.Flush()
            s.Close()
        End Using

        Return "Received data"

    End Function
End Class

Open in new window


So in SalesOrderIn I'm using "Implements ISalesOrderIn" where I declare my class, and when I built SalesOrderIn.PODataIn I used Implements ISalesOrderIn.PODataIn", as the tutorials showed. If that's not right please let me know.

Also, I've made a little headway - I wasn't calling my POST correctly in Fiddler, and was consistently getting an error about malformed XML incoming. I was passing this as the POST body:

<MSMXML><User>ScottM</User></MSMXML>

and I'd consistently receive an error that the element at Root 1, Line 1 (or something to that effect) was invalid. I changed to this:

<PODataIn xmlns="http://my.namespace.com"><MSMXML><UserName>ScottM</UserName></MSMXML>
</PODataIn>

And the error stopped, and I received the "Data Received" message, which I take to indicate that I've correctly called the SalesOrderIn.PODataIn function. Unfortunately, it still consistently writes "No Data" to my text file, which I take to mean that my SalesOrderIn.PODataIn input variable "DataCardIn" is NULL.
0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 38406054
1) Are you able to debug this by stepping through the code?  

2) Try to remove the "WebMessageBodyStyle.Wrapped" from the WebInvoke attribute, as WebMessageBodyStyle.Bare is the default.
0
 
LVL 85
ID: 38406355
I can't debug on the server. If I debug on my workstation it works fine - writes the data to the file and such, as I'd expect, but I'm not sure how it's doing that (using POST, or some other method)? As I mentioned earlier, I'm stuck with the client requiring the POST method only, with a contenttype of "text" or "text/xml" only.

I removed the "WebMessageBodyStyle" decoration, but no change. I still can POST to the service, but the data I pass in via my POST body is never written on the server - it always writes "No Data" in the file.
0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 38406384
There is a way to debug on the server, but you need to have the Remote Debugger installed on the server, and the debugger Windows service up and running.  Then, you can Attach to Process, and find the IIS worker process (i.e. w3wp.exe), and step through that code.
0
 
LVL 85
ID: 38406460
I'm not sure they'll let me do that ... I'll check and see. Do you have a link to a tutorial on using the Remote Debugger?
0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 38406679
I can't verify whether one resource is any better than the other without going through each of the steps, but you might try here:

Debugging with Visual Studio 2005/2008: Remote Debugging
http://www.cprogramming.com/tutorial/visual_studio_remote_debugging.html
0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 38406685
Another resource for 2010:

Remote debugging with Visual Studio 2010
http://www.codeproject.com/Articles/146838/Remote-debugging-with-Visual-Studio-2010
0
 
LVL 85
ID: 38407573
I couldn't get the remote debugger to work (firewall issues on their side, and they're not changing them).

Frustrating. My service is fine all the way up to the point of writing the file, and it just can't "see" the data that I'm POSTing in.

I did find that if I changed the URI to this:

UriTemplate:="/{DataCardIn}"

Then it will write whatever text is after the backslash ... for example, if I POST this:

mywebservice.com/theservice.svc/mydataisrighthere

It will write "mydataisrighthere" in the text file.

Any other thoughts? I have no problem with starting over, if you know of any good tutorials about writing POST data through the service. Seems nearly all of the ones I find are where the service is returning data, whereas mine needs to accept it.
0
 
LVL 85

Accepted Solution

by:
Scott McDaniel (Microsoft Access MVP - EE MVE ) earned 0 total points
ID: 38409216
Finally - Success!

I changed my PODataIn function to accept a Stream, and was able to get the data in from that  Stream like this:

 <OperationContract()> _
        <WebInvoke(bodystyle:=WebMessageBodyStyle.Wrapped, Method:="POST", RequestFormat:=WebMessageFormat.Xml, ResponseFormat:=WebMessageFormat.Xml)> _
        Function PODataIn(DataCardIn As Stream) As String

And changed the Function in the other class to this:

Public Function MyPODataIn(DataCardIn As Stream) As String Implements ISalesOrderIn.PODataIn

I have to use a Content-Type of "text" or "text/xml", but that shouldn't be a problem.

I also made sure that everything was in the same namespace, although I'm not sure that is required.

Since the client could send in data >65k (which is, I found, the default message limit) I modified the web.config to increase the maxReceivedMessageSize and maxBufferSize:

<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
	<bindings>
		<wsHttpBinding>
			<binding name="SampleServiceBinding">
				<security />
			</binding>
		</wsHttpBinding>
	
[b]	<webHttpBinding>
		<binding name="BigDataBinding" maxReceivedMessageSize="67108864" maxBufferSize="67108864" />[/b]
	</webHttpBinding>

	</bindings>

Open in new window

And modified my endpoint to use that configuration:
<endpoint address="" binding="webHttpBinding" bindingConfiguration="BigDataBinding" contract="MSMFG_DataImport.ISalesOrderIn" behaviorConfiguration="RestBehavior" />

Open in new window


Given my difficulty with this, I'm going to writeup a blog post for others who might be floundering with things like this.

Thank you for your efforts - I truly appreciate it.

FYI, I posted a very similar question on StackOverflow, and was very disappointed in the level of help I received there (which is odd, considering their constants slams on EE). All I really received were snide comments about my choice of backend code mechanism (apparently all code must be written in C), instructions on "proper" posting methods, and thin-lipped criticism of my code efforts thus far. A real eye-opener on the differences between the two sites (even from some of the MVPs over there).
0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 38410327
I believe that everything is what it is.  I had a discussion with a colleague that really hates EE, and is a big participant in SO.  I am not a big fan of starting back at the bottom to gain any reputation with SO (it took me many years with EE), so I continue to contribute to EE, although my output has dropped drastically over the years.

I am glad that you found your own solution.  I always feel that my task is not to answer directly, but to provide tips and suggestions to let someone find their own way.  I feel that is the best way to learn something new.

I also feel that "walking softly, and carrying a big stick" is my mantra.  There is no room for arrogance, as it has no place--I am often humbled by the genius of others.

Bob
Microsoft C# MVP
0
 
LVL 85
ID: 38411045
I've definitely learned a lot about WCF in specific, and about the nature of web communincations in general. It'll definitely help me as I expand my horizons outward. I'm an Access MVP, but have been working more and more in .NET, with more clients pestering me about web-based stuff, so while it was frustrating I can't say it wasn't a good experience.

Thanks again. I'm going to accept my comment as the solution with points awarded to several of your suggestions, since they most certainly pointed me in the right direction.
0
 
LVL 85
ID: 38426058
Thanks very much for all the help. I ended up moving to a WCF service, so I'm not sure of the solution to my original question regarding content types on an asmx service.
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I found this questions asking how to do this in many different forums, so I will describe here how to implement a solution using PHP and AJAX. The logical flow for the problem should be: Write an event handler for the first drop down box to get …
This article shows how to deploy dynamic backgrounds to computers depending on the aspect ratio of display
The viewer will learn the basics of jQuery including how to code hide show and toggles. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery…
Video by: Mark
This lesson goes over how to construct ordered and unordered lists and how to create hyperlinks.
Suggested Courses

807 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question