Проблема вызова сервисной библиотеки WCF из jQuery - PullRequest
7 голосов
/ 22 октября 2009

У меня есть библиотека служб WCF, доступная через мой сайт ASPX следующим образом

[System.ServiceModel.OperationContract]
[System.ServiceModel.Web.WebInvoke(
Method= "POST",
RequestFormat=System.ServiceModel.Web. WebMessageFormat .Json,
ResponseFormat=System.ServiceModel.Web.WebMessageFormat .Json)]
LogonResponse Logon(LogonRequest logonRequest);


[System.Runtime.Serialization.DataContract]
[ Serializable()]
public class LogonRequest
{
[System.Runtime.Serialization.DataMember]
public string EMailAddress;
[System.Runtime.Serialization.DataMember]
public string Password;
}

На моей тестовой странице я могу позвонить через MS Ajax: -

<asp:ScriptManager ID ="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="~/testService.svc" />
</Services>
</asp:ScriptManager>
.
.
.
function clsLogonRequest(eMailAddress, password) {
this .EMailAddress = eMailAddress;
this .Password = password;
}

function login(eMailAddress, password) {
var LogonRequest = new clsLogonRequest(eMailAddress, password);
name.API.IAPI.Logon(LogonRequest, onSuccess, onFailure);
}

function onSuccess(result) {
$( "#txtSessionId").val(result.SessionId);
$( "#txtUserName").val(result.Name);
$( "#txtUserId").val(result.UserId);
}

, который работает нормально, или через вызов jQuery $ .ajax: -

$(document).ready(function() {
$( "#Button1").click(function() {
var LogonRequest = new clsLogonRequest( '*************' , '***********');
$.ajax({
type: "POST",
url: "testService.svc/Logon",
data: "{'logonRequest':" + JSON.stringify(LogonRequest) + "}" ,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg) {
alert(msg.d);
}
});
});
});

Что нет - под FireBug я вижу сообщение 500 Internal Server Error, и исключение начинается

{"ExceptionDetail":{"HelpLink":null,"InnerException":null,"Message":"The token '\"' was expected but found '''.","StackTrace":"   at System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader, String res, String arg1, String arg2, String arg3)\u000d\u000a   at System.Xml.XmlExceptionHelper .ThrowTokenExpected(XmlDictionaryReader reader, String expected, Char found)\u000d\u000a   at System.Runtime...

Почему кажется, что вызов jQuery передает XML, когда я специально говорю не делать этого (и как я могу остановить его, чтобы вызовы из всех источников обрабатывались настолько естественно, насколько это возможно)?

Заранее спасибо.

РЕДАКТИРОВАТЬ:

Спасибо за предложения, я посмотрел на то, что вы сказали, и думаю, что мое объяснение проблемы было недостаточно ясным.

Проблема не в том, что JSON не отправляется, он отправляется и имеет правильный формат, я вижу это из firebug. Проблема в том, что библиотека служб WCF ожидает XML и падает при получении JSON.

Просто чтобы быть уверенным, что я не упустил из виду предлагаемое решение, вот распаковка web.Config - я попытался удалить из поведения и удалить атрибут квартирыConfiguration из тега services / service / endpoint, и это просто делает все не получится, не вылезая со страницы.

<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name ="MyServiceTypeBehaviors ">
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name ="AspNetAjaxBehavior">
<enableWebScript/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled= "true " />
<services>
<service name ="test.API.API" behaviorConfiguration = " MyServiceTypeBehaviors">
<endpoint behaviorConfiguration="AspNetAjaxBehavior" binding = " webHttpBinding" contract="test.API.IAPI" />
<endpoint contract ="IMetadataExchange" binding= " mexHttpBinding" address="mex" />
</service>
</services>
</system.serviceModel>

Заранее спасибо

1 Ответ

13 голосов
/ 22 октября 2009

Я предполагаю, что вы применили WebScriptEnablingBehavior (элемент enableWebScript в .config)? Это приводит к тому, что все запросы будут помещены в объект JSON с одним полем с именем «d», которое затем содержит объект с вашими именованными параметрами. Поэтому вам нужно изменить данные jQuery на:

data: "{d:{logonRequest:" + JSON.stringify(LogonRequest) + "}}"

Либо это, либо удалите поведение enableWebScript, но, если вы это сделаете, вы должны вместо этого применить поведение webHttp. Теперь, к сожалению, кодировка по умолчанию - это XML, и поведение не предлагает никаких переключателей для управления кодировкой для всего сервиса (плохой дизайн MSFT). Итак, либо вы должны жениться на отдельной кодировке для каждого метода, установив свойства Request / ResponseFormat, либо, как я уже делал это в прошлом, я создал EnhancedWebHttpElement, который применяет тот же WebHttpBehavior но дает контроль уровня конфигурации над его различными свойствами. Преимущество использования этой конфигурации состоит в том, что теперь вы можете предоставлять одну и ту же службу WCF через разные конечные точки, используя ASP.NET AJAX-кодирование в одной, простой JSON-код в другой и, возможно, даже POX в другой.

Вот код:

public sealed class EnhancedWebHttpElement : BehaviorExtensionElement
{
    #region Type specific properties

    [ConfigurationProperty("defaultBodyStyle", DefaultValue=WebMessageBodyStyle.Bare)]
    public WebMessageBodyStyle DefaultBodyStyle
    {
        get
        {
            return (WebMessageBodyStyle)this["defaultBodyStyle"];
        }

        set
        {
            this["defaultBodyStyle"] = value;
        }
    }

    [ConfigurationProperty("defaultOutgoingRequestFormat", DefaultValue=WebMessageFormat.Xml)]
    public WebMessageFormat DefaultOutgoingRequestFormat
    {
        get
        {
            return (WebMessageFormat)this["defaultOutgoingRequestFormat"];
        }

        set
        {
            this["defaultOutgoingRequestFormat"] = value;
        }
    }

    [ConfigurationProperty("defaultOutgoingResponseFormat", DefaultValue=WebMessageFormat.Xml)]
    public WebMessageFormat DefaultOutgoingResponseFormat
    {
        get
        {
            return (WebMessageFormat)this["defaultOutgoingResponseFormat"];
        }

        set
        {
            this["defaultOutgoingResponseFormat"] = value;
        }
    }       

    #endregion

    #region Base class overrides

    protected override object CreateBehavior()
    {
        WebHttpBehavior result = new WebHttpBehavior();

        result.DefaultBodyStyle = this.DefaultBodyStyle;
        result.DefaultOutgoingRequestFormat = this.DefaultOutgoingRequestFormat;
        result.DefaultOutgoingResponseFormat = this.DefaultOutgoingResponseFormat;

        return result;
    }

    public override Type BehaviorType
    {
        get
        {
            return typeof(WebHttpBehavior);
        }
    }

    #endregion
}

И тогда вы должны зарегистрировать его:

<system.serviceModel>
    <extensions>
        <behaviorExtensions>
            <add name="enhancedWebHttp" type="MyNamespace.EnhancedWebHttpElement, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
            </behaviorExtensions>
    </extensions>

И тогда вы используете это так:

<behavior name="MyBehavior">
    <enhancedWebHttp defaultOutgoingRequestFormat="JSON" defaultOutgoingResponseFormat="JSON" />
</behavior>

UPDATE:

Этот ответ выше относится исключительно к .NET 3.x. Если вы используете .NET 4.x, то теперь вы можете использовать свойство DefaultOutgoingResponseFormat , которое выставлено на WebHttpElement для управления этим. Более того, вы можете выставить одну конечную точку и установить новое AutomaticFormatSelectionEnabled свойство , которое будет отвечать правильным форматом в зависимости от формата исходного запроса.

...