需要对 JSONP 与 JQuery/WCF 进行一些澄清

发布于 2024-10-31 12:44:11 字数 5542 浏览 1 评论 0原文

目标:公开一个简单的 WCF 服务,该服务将用户名字符串作为参数,并查看它是否已存在。该服务将在我正在开发的网站的注册页面上使用,因此当用户输入用户名时,它会自动检查该用户名是否被占用。

我实际上已经让此功能正常工作,但我必须为 WCF 服务启用 crossDomainScripting。我的问题是“为什么?”

给代码!

我的 WCF 服务托管在我的 MVC3 站点中(我最初将它放在一个单独的站点中,但为了消除对跨站点描述的所有担忧,我将其移至本地。)

WCF 服务实现的接口

    [ServiceContract]
public interface IMembershipServices
{
    [OperationContract]
    [WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json,
        UriTemplate = "IsUsernameAvailable/{username}",
        BodyStyle = WebMessageBodyStyle.Bare)]
    MembershipUserAvailability IsUsernameAvailable(string username);

    [OperationContract]
    [WebGet(UriTemplate = "helloto/{name}", ResponseFormat = WebMessageFormat.Json)]
    string Hello(string name);
}

    [AspNetCompatibilityRequirements(RequirementsMode
    = AspNetCompatibilityRequirementsMode.Allowed)]
public class MembershipServices : IMembershipServices
{
    #region IMembershipServices Members

    public string Hello(string name)
    {
        return String.Format("Hello:{0}", name);
    }


    public MembershipUserAvailability IsUsernameAvailable(string username)
    {
        if (String.IsNullOrWhiteSpace(username))
        {
            throw new ArgumentException(username);
        }

        MembershipUser membershipUser = Membership.GetUser(username, false);

        var membershipUserAvailability = new MembershipUserAvailability
                                             {
                                                 UserName = username,
                                                 IsAvailable = membershipUser == null ? true : false
                                             };

        return membershipUserAvailability;
    }

    #endregion
}

Web.config

<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
    <services>
        <!-- This section is optional with the default configuration
    model introduced in .NET Framework 4 -->
        <service name="MedicalPracticeWeb.Services.MembershipServices" behaviorConfiguration="MembershipServicesBehaviors">

            <!-- This endpoint is exposed at the base address provided by host: http://localhost/servicemodelsamples/service.svc  -->
            <endpoint address="" binding="webHttpBinding" bindingConfiguration="webHttpBindingJsonP" contract="MedicalPracticeWeb.Services.IMembershipServices" behaviorConfiguration="EndpBehavior" />

            <!-- The mex endpoint is exposed at http://localhost/servicemodelsamples/service.svc/mex -->
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        </service>
    </services>
    <behaviors>
        <serviceBehaviors>
            <behavior name="MembershipServicesBehaviors">
                <!-- Add the following element to your service behavior configuration. -->
                <serviceMetadata httpGetEnabled="true" />
                <serviceDebug includeExceptionDetailInFaults="true" />
            </behavior>
        </serviceBehaviors>
        <endpointBehaviors>
            <behavior name="EndpBehavior">
                <webHttp />
            </behavior>
        </endpointBehaviors>
    </behaviors>
    <bindings>
        <webHttpBinding>
            <binding name="webHttpBindingJsonP" crossDomainScriptAccessEnabled="true"></binding>
        </webHttpBinding>
    </bindings>
</system.serviceModel>

Jquery 来实现调用:

$(document).ready(function () {

var usernameTextbox = $('#username');
var usernameUnavailableRow = $('#usernameUnavailableRow');
var availabilityMessage = $('#availabilityMessage');

usernameTextbox.blur(function () {
    if ($(this).val()) {

        $.getJSON('/Services/MembershipServiceHost.svc/IsUsernameAvailable/' + escape($(this).val()), function (results) {
            if (results.IsAvailable) {
                if (usernameUnavailableRow.is(':visible')) {
                    availabilityMessage.html('This username is available.');
                    availabilityMessage.addClass('usernameAvailable');
                    availabilityMessage.removeClass('usernameTaken');
                }
            }
            else {
                usernameUnavailableRow.show();
                availabilityMessage.html('This username is already taken!');
                availabilityMessage.addClass('usernameTaken');
                availabilityMessage.removeClass('usernameAvailable');
            }
        });

    }
});

} );

现在这一切都很好。我可以在注册表单的文本框中输入用户名,当我离开该字段时,onBlur 事件会触发并调用 WCF 服务,按预期返回结果...但如果我有 crossDomainScriptAccessEnabled="true “在我的 web.config 中。这让我很困惑。也许是缺乏对 JSON 和 JSONP 的理解(完全有可能。我对使用 JSON 还很陌生),但由于该服务托管在同一个域中,为什么我需要这个集合?

我查看了 Fiddler 发送的内容:

GET /Services/MembershipServiceHost.svc/IsUsernameAvailable/Tim?callback=jQuery1510988704698288691_1302393437642&_=1302393485350 HTTP/1.1

还有我在 JQuery 文档中看到的回调参数。

http://api.jquery.com/jQuery.getJSON/

如果 URL 包含字符串“callback=?” (或类似的,由服务器端 API 定义),请求将被视为 JSONP。有关更多详细信息,请参阅 $.ajax() 中对 jsonp 数据类型的讨论。

这是否意味着成功回调的简单行为将迫使 GetJSON 使用 JSONP?

任何意见表示赞赏。

Goal: Expose a simple WCF service that would take as a parameter a string that was a username and see if it already existed. This service would be used on the registration page of the site I'm working on so when a user typed a username it would automatically check to see if it was taken.

I've actually gotten this functionality to work, but I had to enable crossDomainScripting for the WCF service. My question is "why?"

To the code!

My WCF service is hosted within my MVC3 site (I originally had it in a separate site but to remove any and all concern about cross site scriptiong I moved it local.)

Interface for WCF Service

    [ServiceContract]
public interface IMembershipServices
{
    [OperationContract]
    [WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json,
        UriTemplate = "IsUsernameAvailable/{username}",
        BodyStyle = WebMessageBodyStyle.Bare)]
    MembershipUserAvailability IsUsernameAvailable(string username);

    [OperationContract]
    [WebGet(UriTemplate = "helloto/{name}", ResponseFormat = WebMessageFormat.Json)]
    string Hello(string name);
}

Implementation:

    [AspNetCompatibilityRequirements(RequirementsMode
    = AspNetCompatibilityRequirementsMode.Allowed)]
public class MembershipServices : IMembershipServices
{
    #region IMembershipServices Members

    public string Hello(string name)
    {
        return String.Format("Hello:{0}", name);
    }


    public MembershipUserAvailability IsUsernameAvailable(string username)
    {
        if (String.IsNullOrWhiteSpace(username))
        {
            throw new ArgumentException(username);
        }

        MembershipUser membershipUser = Membership.GetUser(username, false);

        var membershipUserAvailability = new MembershipUserAvailability
                                             {
                                                 UserName = username,
                                                 IsAvailable = membershipUser == null ? true : false
                                             };

        return membershipUserAvailability;
    }

    #endregion
}

Web.config

<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
    <services>
        <!-- This section is optional with the default configuration
    model introduced in .NET Framework 4 -->
        <service name="MedicalPracticeWeb.Services.MembershipServices" behaviorConfiguration="MembershipServicesBehaviors">

            <!-- This endpoint is exposed at the base address provided by host: http://localhost/servicemodelsamples/service.svc  -->
            <endpoint address="" binding="webHttpBinding" bindingConfiguration="webHttpBindingJsonP" contract="MedicalPracticeWeb.Services.IMembershipServices" behaviorConfiguration="EndpBehavior" />

            <!-- The mex endpoint is exposed at http://localhost/servicemodelsamples/service.svc/mex -->
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        </service>
    </services>
    <behaviors>
        <serviceBehaviors>
            <behavior name="MembershipServicesBehaviors">
                <!-- Add the following element to your service behavior configuration. -->
                <serviceMetadata httpGetEnabled="true" />
                <serviceDebug includeExceptionDetailInFaults="true" />
            </behavior>
        </serviceBehaviors>
        <endpointBehaviors>
            <behavior name="EndpBehavior">
                <webHttp />
            </behavior>
        </endpointBehaviors>
    </behaviors>
    <bindings>
        <webHttpBinding>
            <binding name="webHttpBindingJsonP" crossDomainScriptAccessEnabled="true"></binding>
        </webHttpBinding>
    </bindings>
</system.serviceModel>

Jquery to imlement call:

$(document).ready(function () {

var usernameTextbox = $('#username');
var usernameUnavailableRow = $('#usernameUnavailableRow');
var availabilityMessage = $('#availabilityMessage');

usernameTextbox.blur(function () {
    if ($(this).val()) {

        $.getJSON('/Services/MembershipServiceHost.svc/IsUsernameAvailable/' + escape($(this).val()), function (results) {
            if (results.IsAvailable) {
                if (usernameUnavailableRow.is(':visible')) {
                    availabilityMessage.html('This username is available.');
                    availabilityMessage.addClass('usernameAvailable');
                    availabilityMessage.removeClass('usernameTaken');
                }
            }
            else {
                usernameUnavailableRow.show();
                availabilityMessage.html('This username is already taken!');
                availabilityMessage.addClass('usernameTaken');
                availabilityMessage.removeClass('usernameAvailable');
            }
        });

    }
});

});

Now this all works great. I can type a username into the textbox on my registration form and when I leave the field the onBlur event fires and calls the WCF service returning results as expected...but only if I have crossDomainScriptAccessEnabled="true" in my web.config. This puzzles me. Perhaps it's a lack of understanding of JSON and JSONP (entirely possible. I'm fairly new to working with JSON) but since the service is hosted in the same domain why would I need this set?

I took a look at what Fiddler was sending:

GET /Services/MembershipServiceHost.svc/IsUsernameAvailable/Tim?callback=jQuery1510988704698288691_1302393437642&_=1302393485350 HTTP/1.1

And there's the callback parameter that I see mentioned in the JQuery docs.

http://api.jquery.com/jQuery.getJSON/

If the URL includes the string "callback=?" (or similar, as defined by the server-side API), the request is treated as JSONP instead. See the discussion of the jsonp data type in $.ajax() for more details.

Does this mean the simple act of having an on success callback will force GetJSON to utilize JSONP?

Any input appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

九公里浅绿 2024-11-07 12:44:11

这是否意味着简单的行为
有一个成功回调将
强制 GetJSON 使用 JSONP?

简单的答案是肯定的。

如果您添加参数或将类型设置为 jsonp,jQuery 将自动神奇地为您完成所有艰苦的工作。您还需要您的服务足够智能,能够在将数据发送给您之前将数据包装在函数调用中 - 但如果给定参数,.net 就会执行此操作。

SO 上还有许多其他答案讨论如何通过 jQuery 使用 jsonp。

Does this mean the simple act of
having an on success callback will
force GetJSON to utilize JSONP?

The simple answer is yes.

If you add in the parameter or set the type to jsonp, jQuery will auto-magically do all the hard work for you. You also need your service to be smart enough to wrap the data in a function call before it sends it to you -- but .net does this if given the parameter.

There are a number of other answers on SO that talk about how to use jsonp via jQuery.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文