3
votes

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)"}

5
is the service on the same domainRafay
@3nigma yes, any other method that has normal parameters like string/ int worksOmu
I've edit my answer, hope some helps~Monday

5 Answers

5
votes

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.

1
votes

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
votes

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~`

1
votes

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.

0
votes

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