3

my js code:

var a = ["asdfa", "asdfa", "aaa"];
var data = [];
for (var i in a) data.push({ name: 'keys', value: a[i] });
$.post('<%=ResolveUrl("~/svc/aja.svc/GetMultiple") %>', $.param(data), function(d) {
//do stuff
});

my ajax enabled wcf

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Aja
{
    [WebInvoke(Method="POST")]
    [OperationContract]
    public IEnumerable<IdContent> GetMultiple(string[] keys)
    {
        return keys.Select(o => new IdContent { Id = o, Content = o + o });
    }

I tried debugging and the Method GetMultiple doesn't get hit,(I get error 500)

if I do this by sending a simple string not array, than it works

this the message that I get as a result in firebug :

{"ExceptionDetail":{"HelpLink":null,"InnerException":null,"Message":"The incoming message has an unexpected message format 'Raw'. The expected message formats for the operation are 'Xml'; 'Json'. This can be because a WebContentTypeMapper has not been configured on the binding. See the documentation of WebContentTypeMapper for more details.","StackTrace":" at System.ServiceModel.Dispatcher.DemultiplexingDispatchMessageFormatter.DeserializeRequest(Message message, Object[] parameters)\u000d\u000a at System.ServiceModel.Dispatcher.UriTemplateDispatchFormatter.DeserializeRequest(Message message, Object[] parameters)\u000d\u000a at System.ServiceModel.Dispatcher.CompositeDispatchFormatter.DeserializeRequest(Message message, Object[] parameters)\u000d\u000a at System.ServiceModel.Dispatcher.DispatchOperationRuntime.DeserializeInputs(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)","Type":"System.InvalidOperationException"},"ExceptionType":"System.InvalidOperationException","Message":"The incoming message has an unexpected message format 'Raw'. The expected message formats for the operation are 'Xml'; 'Json'. This can be because a WebContentTypeMapper has not been configured on the binding. See the documentation of WebContentTypeMapper for more details.","StackTrace":" at System.ServiceModel.Dispatcher.DemultiplexingDispatchMessageFormatter.DeserializeRequest(Message message, Object[] parameters)\u000d\u000a at System.ServiceModel.Dispatcher.UriTemplateDispatchFormatter.DeserializeRequest(Message message, Object[] parameters)\u000d\u000a at System.ServiceModel.Dispatcher.CompositeDispatchFormatter.DeserializeRequest(Message message, Object[] parameters)\u000d\u000a at System.ServiceModel.Dispatcher.DispatchOperationRuntime.DeserializeInputs(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)"}

3
  • is the service on the same domain Commented Jul 21, 2011 at 8:49
  • @3nigma yes, any other method that has normal parameters like string/ int works Commented Jul 21, 2011 at 8:56
  • I've edit my answer, hope some helps~ Commented Jul 21, 2011 at 10:01

5 Answers 5

5
+50

The problem you're having: $.post sends data encoded in application/x-www-form-urlencoded, and this format is not supported natively by WCF. That's the issue.

Now for solutions: as often is the case with WCF, there are quite a few things you can do:

1) Change the client side to send some format which the service can understand. WCF understands JSON, so you can use a JSON stringifier (i.e., JSON.stringify from json2.js from Crockford's implementation). With this, you'd need to change the code to use $.ajax instead of the "shortcut" $.post:

$.ajax({
    type: "POST",
    url: '<%=ResolveUrl("~/svc/aja.svc/GetMultiple") %>',
    contentType: "application/json",
    data: JSON.stringify({ keys: a }),
    success: function (result) {
        alert(result);
    }
});

2) "Teach" WCF how to understand the native format for the $.params (or $.post) calls. There are two ways to do that:

2.1) Do it "by hand". It's not very trivial, but it can be done. You'll need an IDispatchMessageFormatter which can convert from the forms/encoded into the string[] parameter for your operation. You'd then need a behavior to hook this new format up and either a new service host factory (to hook the behavior via code) or a behavior config extension (if you want to do it in config). The following links are for posts explaining in more details the message formatters, the endpoint behaviors, the service host factory and the behavior config extensions. The code below shows one possible implementation for a dispatch formatter which knows how to deal with string arrays.

public class AjaServiceHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        return new AjaServiceHost(serviceType, baseAddresses);
    }

    public class AjaServiceHost : ServiceHost
    {
        public AjaServiceHost(Type serviceType, Uri[] baseAddresses)
            : base(serviceType, baseAddresses) { }

        protected override void OnOpening()
        {
            base.OnOpening();
            ServiceEndpoint endpoint = this.AddServiceEndpoint(typeof(Aja), new WebHttpBinding(), "");
            endpoint.Behaviors.Add(new WebScriptEnablingBehavior());
            endpoint.Behaviors.Add(new MyFormsUrlEncodedAwareBehavior());
        }
    }
}
class MyFormsUrlEncodedAwareBehavior : IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        foreach (var operation in endpoint.Contract.Operations)
        {
            var dispatchOperation = endpointDispatcher.DispatchRuntime.Operations[operation.Name];
            dispatchOperation.Formatter = new MyFormsUrlEncodedAwareFormatter(operation, dispatchOperation.Formatter);
        }
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }
}
class MyFormsUrlEncodedAwareFormatter : IDispatchMessageFormatter
{
    private OperationDescription operationDescription;
    private IDispatchMessageFormatter originalFormatter;

    public MyFormsUrlEncodedAwareFormatter(OperationDescription operationDescription, IDispatchMessageFormatter originalFormatter)
    {
        this.operationDescription = operationDescription;
        this.originalFormatter = originalFormatter;
    }

    public void DeserializeRequest(Message message, object[] parameters)
    {
        if (message.Properties.ContainsKey(WebBodyFormatMessageProperty.Name))
        {
            var bodyFormat = (WebBodyFormatMessageProperty)message.Properties[WebBodyFormatMessageProperty.Name];
            if (bodyFormat.Format == WebContentFormat.Raw)
            {
                if (message.Properties.ContainsKey(HttpRequestMessageProperty.Name))
                {
                    var httpReq = (HttpRequestMessageProperty)message.Properties[HttpRequestMessageProperty.Name];
                    if (httpReq.Headers[HttpRequestHeader.ContentType] == "application/x-www-form-urlencoded")
                    {
                        var requestBodyParts = operationDescription.Messages[0].Body.Parts;
                        if (requestBodyParts.Count == 1 && requestBodyParts[0].Type == typeof(string[]))
                        {
                            string body = GetRawMessageBodyAsString(message);
                            NameValueCollection pairs = HttpUtility.ParseQueryString(body);
                            parameters[0] = pairs.GetValues(requestBodyParts[0].Name);
                            return;
                        }
                    }
                }
            }
        }

        this.originalFormatter.DeserializeRequest(message, parameters);
    }

    private string GetRawMessageBodyAsString(Message message)
    {
        XmlDictionaryReader reader = message.GetReaderAtBodyContents();
        reader.ReadStartElement("Binary");
        byte[] bytes = reader.ReadContentAsBase64();
        return Encoding.UTF8.GetString(bytes);
    }

    public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
    {
        return this.originalFormatter.SerializeReply(messageVersion, parameters, result);
    }
}

2.2) Use the new "jQuery support for WCF" which is published on http://wcf.codeplex.com. Those are some behaviors / formatters which add support for forms/urlencoded requests. But you'll need to change the operation to receive the data in an untyped way (i.e., as a JsonArray), and in the code you'd enumerate the items yourself.

Sign up to request clarification or add additional context in comments.

Comments

1

I think you should create a JSON string using the array you are trying to pass as a parameter to the wcf application. Try the following code

Javascript section,

 var list = ["a", "b", "c", "d"];
 var jsonText = JSON.stringify({ list: list });

$.ajax({
  type: "POST",
  url: "WebService1.svc/methodName",
  data: jsonText,
  contentType: "application/json; charset=utf-8",
  dataType: "json",
 success: function() { alert("it worked"); },
 failure: function() { alert("Uh oh"); }

});

Hope this helps...

1 Comment

this would mean that at the server side I'm going to get a (string list), and than I have to deserialize it myself
1

It is not possible.

You cannot map array of values from URL to parameter.

If you want to pass array use HTTP POST.


Edited:

Demo for you~

use mvc2~

The key to success is traditional

set the traditional parameter to true

$(function(){
    var a = [1, 2];
    $.ajax({
       type: "POST",
       url: "<%= ResolveUrl("~/Home/PostArray/") %>",
       data: {orderedIds: a},
       dataType: "json",
       traditional: true,
       success: function(msg){alert(msg)}
    });
})

Since jquery 1.4 this parameter exists because the mechanism to serialize objects into query parameters has changed.

and action is~

    [HttpPost]
    public ActionResult PostArray(int[] orderedIds)
    {
        return Content(orderedIds.Length.ToString());
    }

hope helps~`

2 Comments

I tried post, $.post and [WebInvoke] instead of [WebGet] and still doesn't work. I've edit my question, switched to post and added more details
I would very easily do this with MVC, but I can't I have to use ASP.NET web-forms, and ajax enabled wcf
1

Try keeping your server side code the same and changing your client side code to:

$(function(){
    var a = ["asdfa", "asdfa", "aaa"];
var data = [];
for (var i in a) data.push({ name: 'keys', value: a[i] });
    $.ajax({
       url: "echo/json/",
       data: data,
       dataType: "json",
       traditional: true,
       success: function(msg){alert(msg)}
    });
})

I think your problem may be that the content type is not specified properly when using the $.post method.

Comments

0

You Can also Try this

var objSearch = [

        { "Key": "Key_Name1", "Value": "asdfa"},
        { "Key": "Key_Name2", "Value": "asdfa"},
         { "Key": "Key_Name3", "Value": "aaa"},
    ];

Here Key_Name Means that u get in wcf Lke (Key=>"Keyname1", Value=>"asdfa")

$.ajax({

        type: "POST",
        url: svcCertificateUrl + '/Fetch',
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        async: false,
        data: JSON.stringify({"objSearch": JSON.stringify(objSearch) }),
        success: function (data) {});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.