.NET ASMX - 返回纯JSON?

5
我现在非常烦恼。我查看了以下条目,没有一个能够解决我看到的异常行为:

我还查看并确认了我的设置:http://www.asp.net/AJAX/documentation/live/ConfiguringASPNETAJAX.aspx

这是我的代码(ASMX代码后台):

namespace RivWorks.Web.Services
{
    /// <summary>
    /// Summary description for Negotiate
    /// </summary>
    [WebService(Namespace = "http://rivworks.com/webservices/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
    [ScriptService]
    public class Negotiate : System.Web.Services.WebService
    {
        [WebMethod]
        [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
        public RivWorks.Data.Objects.rivProduct GetSetup(string jsonInput)
        {
            // Deserialize the input and get all the data we need...
            // TODO:  This is a quick hack just to work with this for now...
            char[] tokens = { '(', '{', '}', ')', ',', '"' };
            string[] inputs = jsonInput.Split(tokens);
            string inputRef = "";
            string inputDate = "";
            string inputProductID = "";
            for (int i = 0; i < inputs.Length; i++)
            {
                if (inputs[i].Equals("ref", StringComparison.CurrentCultureIgnoreCase))
                    inputRef = inputs[i+2];
                if (inputs[i].Equals("dt", StringComparison.CurrentCultureIgnoreCase))
                    inputDate = inputs[i+2];
                if (inputs[i].Equals("productid", StringComparison.CurrentCultureIgnoreCase))
                    inputProductID = inputs[i+2];
            }

            Guid pid = new Guid(inputProductID);
            RivWorks.Data.Objects.rivProduct product = RivWorks.Data.rivProducts.GetProductById(pid);

            return product;
        }
    }

当我从本地主机运行时,我得到了这个结果集:
  <ResultSet>
    <uiType>modal</uiType>
    <width>775</width>
    <height>600</height>
    <swfSource>
    http://localhost.rivworks.com/flash/negotiationPlayer.swf
    </swfSource>
    <buttonConfig>
    http://cdn1.rivworks.com/Element/Misc/734972de-40ae-45f3-9610-5331ddd6e8f8/apple-logo-2.jpg
    </buttonConfig>
  </ResultSet>

我错过了什么???


注意:我正在使用3.5框架(或者至少我认为我在用,因为我的web.config中的所有内容都标记为3.5.0.0)


更新:我正在浏览服务并使用该页面上的输入框。您可以在此处尝试:http://dev.rivworks.com/services/Negotiate.asmx?op=GetSetup。我们还尝试从在另一个站点上运行的基于JS的Web应用程序访问它(这个特定服务的主要目的)。我这里没有那个代码。(抱歉,测试表单仅在本地主机上可用。)


更新:我添加了以下测试页面(JsonTest.htm)以尝试查看来回传输的内容。但是我只收到500错误!我甚至尝试附加到进程并中断我的服务。但是在ASP管道进入我的代码之前就会抛出500错误。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Untitled Page</title>
    <script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.js" type="text/javascript"></script>

    <script language="javascript" type="text/javascript">
        function sendReq() {
            alert("Before AJAX call");
            $.ajax(
            {
                type: "POST"
                , url: "http://kab.rivworks.com/Services/Negotiate.asmx/GetSetup"
                , data: "{ \"ref\":\"http://www.rivworks.com/page.htm\", \"dt\":\"Mon Dec 14 2009 10:45:25 GMT-0700 (MST)\", \"productId\":\"5fea7947-251d-4779-85b7-36796edfe7a3\" }"
                , contentType: "application/json; charset=utf-8"
                , dataType: "json"
                , success: GetMessagesBack
                , error: Failure
            }
            );
            alert("After AJAX call");
        }
        function GetMessagesBack(data, textStatus) {
            alert(textStatus + "\n" + data);
        }
        function Failure(XMLHttpRequest, textStatus, errorThrown) {
            alert(textStatus + "\n" + errorThrown + "\n" + XMLHttpRequest);
        }
    </script>
</head>
<body>
    <div id="test">Bleh</div>
    <a href="javascript:sendReq()">Test it</a>
</body>
</html>

为什么这么痛苦难懂?!?! :)


更新:通过WCF服务进行工作。这是我的设置: 接口:

namespace RivWorks.Web.Services
{
    [ServiceContract(Name = "Negotiater", Namespace = "http://www.rivworks.com/services")]
    public interface INegotiaterJSON
    {
        //[WebMethod]
        [OperationContract]
        [WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        [ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Json)]
        ResultSet GetSetup(string jsonInput);
    }
}

类:

namespace RivWorks.Web.Services
{
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class Negotiater : INegotiaterJSON
    {
        public ResultSet GetSetup(string jsonInput)
        {
            //code removed for brevity - see ASMX code above if you are really interested.
            return resultSet;
        }
    }


    [DataContract()]
    public class ResultSet
    {
        [DataMember]
        public string uiType = "modal";
        [DataMember]
        public int width = 775;
        [DataMember]
        public int height = 600;
        [DataMember]
        public string swfSource = "";
        [DataMember]
        public string buttonConfig = "";
    }
}

web.config

  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name ="soapBinding">
          <security mode="None" />
        </binding>
      </basicHttpBinding>
      <webHttpBinding>
        <binding name="webBinding">
          <security mode="None" />
        </binding>
      </webHttpBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name="poxBehavior">
          <webHttp/>
        </behavior>
        <behavior name="jsonBehavior">
          <enableWebScript  />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="defaultBehavior">
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="RivWorks.Web.Services.Negotiater" behaviorConfiguration="defaultBehavior">
        <endpoint address="json"
                  binding="webHttpBinding"
                  bindingConfiguration="webBinding"
                  behaviorConfiguration="jsonBehavior"
                  contract="RivWorks.Web.Services.INegotiaterJSON" />
      </service>
    </services>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true">
      <baseAddressPrefixFilters>
        <add prefix="http://dev.rivworks.com" />
      </baseAddressPrefixFilters>
    </serviceHostingEnvironment>
  </system.serviceModel>

简单测试页面

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Untitled Page</title>
    <script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.js" type="text/javascript"></script>

    <script language="javascript" type="text/javascript">
        function sendReq() {
            alert("Before AJAX call");
            $.ajax(
            {
                type: "POST"
                , url: "http://dev.rivworks.com/Services/Negotiater.svc/GetSetup"
                , data: "{ \"ref\":\"http://www.rivworks.com/page.htm\", \"dt\":\"Mon Dec 14 2009 10:45:25 GMT-0700 (MST)\", \"productId\":\"5fea7947-251d-4779-85b7-36796edfe7a3\" }"
                , contentType: "application/json; charset=utf-8"
                , dataType: "json"
                , success: GetMessagesBack
                , error: Failure
            }
            );
            alert("After AJAX call");
        }
        function GetMessagesBack(data, textStatus) {
            alert(textStatus + "\n" + data);
        }
        function Failure(XMLHttpRequest, textStatus, errorThrown) {
            alert(textStatus + "\n" + errorThrown + "\n" + XMLHttpRequest);
        }
    </script>

</head>
<body>
    <div id="test">Bleh</div>
    <!--<button onclick="javascript:sendReq()">TEST IT</button>-->
    <a href="javascript:sendReq()">Test it</a>
</body>
</html>

现在我遇到了这个错误:

IIS指定的身份验证方案为“IntegratedWindowsAuthentication,Anonymous”,但绑定只支持指定一个身份验证方案。有效的身份验证方案包括Digest、Negotiate、NTLM、Basic或Anonymous。更改IIS设置,仅使用单个身份验证方案。

我该如何处理? <state emotion='wrung out' physical='beat up' />


你能否发布一下你正在使用的调用服务的代码? - Simon Fox
Keith,关于你最新的更新,为什么不尝试自托管你的WCF应用程序来进行测试呢?这将会排除IIS的影响。使用控制台应用程序进行自托管非常容易。(请查看我的答案中的WCFHost.cs文件)。 - Daniel Vassallo
通过取消集成安全性(我们本来就没有使用)解决了IIS问题。现在SVC出现500错误。将自托管转换为Web托管不是什么大问题吧?我们的管理层希望上线并运行上周。"只需要10分钟"。也许我现在太深入问题了。 - Keith Barrows
Daniel Vassallo - 我想说一声非常感谢你的耐心、催促和支持。似乎 ASMX 或带有 JSON 的 WCF 并没有 清晰地 记载在任何地方。我发现许多东西都无法正常运作,现在可能会有冲突的类/方法修饰符了。{叹气 /} - Keith Barrows
Keith,我相信很多人都能理解你最开始进入WCF API大局的困难。WCF看起来很复杂,因为它非常庞大。此外,JSON是最近才加入的(在.NET 3.5中)... 我真正建议的是从一个非常小的项目开始,最好是在控制台应用程序中进行自我托管。然后逐步构建起来... 最后一个建议:如果你愿意投资$69,可以在http://www.learndevnow.com/Topic.aspx?id=98上找到一份非常有价值的10小时视频教程。它将从非常基础的知识开始讲解,并且一切都讲得非常清楚。强烈推荐。 - Daniel Vassallo
我已成功创建了一个WCF JSONP服务!http://www.irovr.com/rivjs/ajax4.htm是测试页面,它正在跨域调用我们的开发服务器。查看页面源代码以查看我们正在传递输入并返回JSON对象。现在,扩展我的类以适应我们的工作。希望明天我们将拥有一个完全功能的Web服务! - Keith Barrows
2个回答

8

为什么不将您的ASMX Web服务迁移到WCF?

在.NET Framework 3.5中,WCF API原生支持JSON Web服务。

此外,微软已将ASMX声明为“遗留技术”,并建议“现在应使用Windows Communication Foundation(WCF)创建Web服务和XML Web服务客户端”。(来源)。

您可能想查看以下链接以开始使用:

此外,您还可以阅读以下示例,该示例是我从自己托管的WCF项目中“提取”的。自托管的WCF服务不需要IIS,而可以从任何托管的.NET应用程序中提供。此示例正在一个非常简单的C#控制台应用程序中托管:

IContract.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace MyFirstWCF
{
    [ServiceContract] 
    public interface IContract
    {
        [OperationContract]
        [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "/CustomerName/{CustomerID}")]
        string GET_CustomerName(string CustomerID);
    }
}

Service.cs

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Syndication;
using System.ServiceModel.Web;

namespace MyFirstWCF
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.NotAllowed)]
    public class Service : IContract
    {
        public string GET_CustomerName(string CustomerID)
        {
            return "Customer Name: " + CustomerID;
        }
    }
}

WCFHost.cs(控制台应用程序)

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.ServiceModel.Description;
using System.Threading;
using System.Text;

namespace MyFirstWCF
{
    class Program
    {
        private static WebServiceHost M_HostWeb = null;

        static void Main(string[] args)
        {
            M_HostWeb = new WebServiceHost(typeof(MyFirstWCF.Service));

            M_HostWeb.Open();

            Console.WriteLine("HOST OPEN");
            Console.ReadKey();

            M_HostWeb.Close();
        }
    }
}

app.config

<?xml version="1.0" encoding="utf-8" ?>

<configuration>
  <system.serviceModel>
    <services>
      <service name="MyFirstWCF.Service">

        <endpoint address="http://127.0.0.1:8000/api"
                  binding="webHttpBinding"
                  contract="MyFirstWCF.IContract" />

      </service>
    </services>

  </system.serviceModel>
</configuration>

上面的例子非常基础。如果你使用Fiddler构建一个请求到http://127.0.0.1:8000/api/CustomerName/1000,它将简单地返回"Customer Name: 1000"
请确保在请求头中设置content-type: application/json。要返回更复杂的数据结构,您需要使用数据契约。这些契约的构造方式如下:
[DataContract]
public class POSITION
{
    [DataMember]
    public int      AssetID { get; set; }

    [DataMember]
    public decimal  Latitude { get; set; }

    [DataMember]
    public decimal  Longitude { get; set; }
}

你需要为此示例项目添加.NET引用,包括System.RuntimeSerializationSystem.ServiceModelSystem.ServiceModel.Web

嗯,我的机器上似乎出了些问题。我没有创建 AJAX WFC 服务的选项。我需要在 VS2008 上安装什么吗? - Keith Barrows
你可以创建一个普通的WCF服务;你可以使用属性修饰符来指示响应格式为JSON。 - Jan Jongboom
1
好的。如果走这个路线,我可以选择一个WCF项目。有没有关于构建.NET WCF JSON启用的WS 从一个单独的(非.NET)网站调用它的参考资料?也许是一个纯HTML站点,并使用JS作为调用方法? - Keith Barrows
我建议你查看我在回答中发布的链接,并在WCF RESTful web服务上进行一些谷歌搜索。你应该使用webHttpBinding。如果它们的接口被修饰为[WebGet(ResponseFormat = WebMessageFormat.Json)],方法将以JSON格式进行响应。 - Daniel Vassallo
Keith,请看一下我在答案中包含的示例。这是来自一个自托管的WCF项目,因此它并不是从IIS提供的服务。 - Daniel Vassallo
显示剩余6条评论

3
请求方法中“Content-Type”的设置是什么?
根据我使用ASP.NET的经验,如果设置为"text/xml",您将收到XML;但如果设置为"application/json",您将收到JSON。

在JS中使用application/json。注意:JS不在我们的页面上!WS需要响应外部JS请求。 - Keith Barrows
我认为重点在于它会以请求的类型返回响应,就像你的例子中似乎是“text/xml”。即使这不是你编写的某个东西的请求,你也可以轻松编写一个小的JS脚本来测试它,使用“application/json”。 - Chris Waters
内容类型非常重要。谢谢! - Keith Barrows

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接