GreenEggs
GreenEggs

Reputation: 527

.Net Webservice won't take parameters for XML call

For some reason my webservice does not like the data I'm sending it. I keep getting the following error:

System.InvalidOperationException: Request format is invalid: text/xml; charset=utf-8.

at System.Web.Services.Protocols.HttpServerProtocol.ReadParameters() at System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()

Any ideas?

Here's my code:

$.ajax({
    type: "POST",
    url: "/wsCheckout.asmx/loginUser",
    data: "userName=" + userName + "&pw=" + pw,
    contentType: "text/xml; charset=utf-8",
    dataType: "xml",
    cache: false,
    beforeSend: function(n){ showLoading(); },
    complete: function(n){ hideLoading(); },
    success: function(r) {        
      if( checkResponse(r) == true ){ 
        closeBox(aspxIdPrefix + "login");
        hideBox(aspxIdPrefix + "login");        
        openBox("#shippingAddress");     
      }           
    } // end success
}); //end AJAX

[WebMethod(EnableSession = true)]
[System.Web.Script.Services.ScriptMethod(ResponseFormat = System.Web.Script.Services.ResponseFormat.Xml)]
public DataTable loginUser(string userName, string pw)
{
......
}

Upvotes: 0

Views: 5781

Answers (5)

Saurabh
Saurabh

Reputation: 1

Try this:

contentType: "text/xml; charset=\"utf-8\"",

Upvotes: 0

jrista
jrista

Reputation: 32950

You are not actually sending XML data to your web service. At the moment, given your example code snippet, your sending in standard HTML forms encoded format:

field1=value1&field2=value2&field3=value3

You probably need to change your data to be xml, along the lines of :

'<data><userName>' + userName + '</userName><pw>' + pw + '</pw></data>'

To do the latter, you will also need to change your web service signature to take a single string, which is later deserialized:

[XmlRoot("data")]
public class UserRequestData
{
    public string userName { get; set; }
    public string pw { get; set; }
}

[WebMethod(EnableSession = true)]
[System.Web.Script.Services.ScriptMethod(ResponseFormat = System.Web.Script.Services.ResponseFormat.Xml)]
public DataTable loginUser(string xmlUserRequest)
{
    XmlSerializer serializer = new XmlSerializer(typeof(UserRequestData));
    StringReader reader = new StringReader(xmlUserRequest);
    UserRequestData data = serializer.Deserialize(reader);

    string userNme = data.UserName;
    string pw = data.Pw;
......
}

It is probably also important to note that the attribute you have decorated your service method with:

[System.Web.Script.Services.ScriptMethod(ResponseFormat = System.Web.Script.Services.ResponseFormat.Xml)]

Has to do with the return value of your service method, not the input data. By decorating with that, your RESPONSE will be formatted with xml. That doesn't affect the input to your service.

Hope this helps.

Upvotes: 1

ScottD
ScottD

Reputation: 304

If you want to return serialized .Net objects via JSON you'll need to do a few things. Assuming you're using jQuery ajax calls it should just work (after making the changes to your service mentioned below) given that jQuery appends the callback param for you. If you aren't using jQuery just append the callback param yourself, pointing to whatever js function you'd like to be called on success.

  • Decorate your Operation Contract setting the ResponseFormat.WebMessageForm.Json (e.g. [WebGet(BodyStyle = WebMessageBodyStyle.WrappedRequest,ResponseFormat = WebMessageFormat.Json,RequestFormat = WebMessageFormat.Json)])
  • create a class that inherits from Stream (see below):

    public class JSONCallbackStream : Stream
    

    { private Stream _stream;

    private string _callbackFunction = string.Empty;
    
    public JSONCallbackStream(Stream stream)
    {
        _stream = stream;
    }
    
    public override bool CanRead
    {
        get { return _stream.CanRead; }
    }
    
    public override bool CanSeek
    {
        get { return _stream.CanSeek; }
    }
    
    public override bool CanWrite
    {
        get { return _stream.CanWrite; }
    }
    
    public override long Length
    {
        get { return _stream.Length; }
    }
    
    public override long Position
    {
        get { return _stream.Position; }
        set { _stream.Position = value; }
    }
    
    public string CallbackFunction
    {
        get { return _callbackFunction; }
        set { _callbackFunction = value; }
    }
    
    public override void Flush()
    {
        _stream.Flush();
    }
    
    public override int Read(byte[] buffer, int offset, int count)
    {
        return _stream.Read(buffer, offset, count);
    }
    
    public override long Seek(long offset, SeekOrigin origin)
    {
        return _stream.Seek(offset, origin);
    }
    
    public override void SetLength(long value)
    {
        _stream.SetLength(value);
    }
    
    public override void Write(byte[] buffer, int offset, int count)
    {
        if (CallbackFunction != string.Empty)
        {
            // This MUST be a one-time write to the underlying stream - any more than 1 write means
            // that the stream will be truncated/an exception could be thrown
            string content = CallbackFunction + "(" + Encoding.UTF8.GetString(buffer) + ");";
            byte[] contentBytes = Encoding.UTF8.GetBytes(content);
            _stream.Write(contentBytes, 0, Encoding.UTF8.GetMaxCharCount(contentBytes.Length));
        }
        else
        {
            _stream.Write(buffer, offset, count);
        }
    }
    

    }

  • create a class that inherits from IHttpModule and be sure that you have entries in web.config for this under system.web -> httpModules (and also system.webServer -> modules if on IIS 7), see class below:

    public class JSONCallback : IHttpModule
    

    { public void Dispose() { }

    //looks for a callback parameter, if found it wraps the return in the callback string
    public void Init(HttpApplication app)
    {
        app.BeginRequest += delegate
        {
            HttpContext ctx = HttpContext.Current;
    
            if ((ctx.Request.RequestType.ToUpper() == "GET"))
            {
                string[] parameters = ctx.Request.QueryString.GetValues("callback");
    
                if (parameters != null && parameters.Length == 1)
                {
                    JSONCallbackStream _captureStream = new JSONCallbackStream(ctx.Response.Filter);
                    _captureStream.CallbackFunction = parameters[0];
    
                    ctx.Response.Filter = _captureStream;
                }
            }
        };
    
    }
    

    }

Upvotes: 0

Jose Basilio
Jose Basilio

Reputation: 51468

The data option is passing the parameters as querystring (GET) instead of post and the content-type needs to be application/json. Here's the complete syntax.

$.ajax({
    type: "POST",
    url: "/wsCheckout.asmx/loginUser",
    data: "{userName:'" + userName + "',pw:'" + pw+"'}",
    contentType: "application/json; charset=utf-8",
    dataType: "xml",
    cache: false,
    beforeSend: function(n){ showLoading(); },
    complete: function(n){ hideLoading(); },
    success: function(r) {        
      if( checkResponse(r) == true ){ 
        closeBox(aspxIdPrefix + "login");
        hideBox(aspxIdPrefix + "login");        
        openBox("#shippingAddress");     
      }           
    } // end success
});

Upvotes: 2

John Saunders
John Saunders

Reputation: 161773

I suggest you try soapUI. Use it to send a request, and watch the response. See what the request looks like that soapUI sends. Then try to send the same thing.

Upvotes: 1

Related Questions