views:

863

answers:

4

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)
{
......
}
+1  A: 

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.

John Saunders
+2  A: 

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
});
Jose Basilio
Thing is I want a response back in xml. I would use JSON but I'm having a hard time getting back proper serialized .Net Objects.
GreenEggs
Just change the parameter "dataType" to "xml". Please try the updated script above.
Jose Basilio
A: 

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;
            }
        }
    };
    
    }

    }

ScottD
Will your solution work on serializing a data table to JSON? That's my main issue. I tried implementing other people's suggestions, but have yet to get the desired results.
GreenEggs
Also, will it work with a .Net 2.0 site?
GreenEggs
The code above is used in a .Net 2.0 WCF service. As for serializing a DataTable you'd probably need to play with it a bit. I would guess the GetXML() method of the DataSet would help you achieve your goal.
ScottD
+1  A: 

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.

jrista
That helps. Especially the last part. Thanks.
GreenEggs