什么是服务参考的正确url?

我有两个项目,一个是WCF服务,它在文本框中说出文本/句子。

public class Service1 : IService1 { public string RunTts(string text) { using (SpeechSynthesizer synth = new SpeechSynthesizer()) { // Configure the audio output. synth.SetOutputToDefaultAudioDevice(); synth.Speak(text); return ""; } } } 

然后我在第二个项目的_Layout.cshtml页面中用ajax调用它,它是asp.net mvc。

  function ttsFunction() { serviceUrl = "Service1.svc/RunTts"; $.ajax({ type: "POST", url: serviceUrl, data: '{"text": "' + $('#speak').val() + '"}', contentType: "text/xml; charset=utf-8", dataType: "text/xml", error: function (xhr,status,error) { console.log("Status: " + status); // got "error" console.log("Error: " + error); // got "Not Found" console.log("xhr: " + xhr.readyState); // got "4" }, statusCode: { 404: function() { console.log("page not found"); // got } } }); }  

因为我收到404错误,所以我觉得url错了。 请参阅文件的结构,我猜这个Web引用叫做’ServiceReference1’。 图片

如屏幕截图所示,该服务未托管在您的Web应用程序中。 您无法直接从客户端访问此类服务(在Web应用程序之外托管),因为您违反了相同的原始策略限制。 它是信任的基本概念之一,Web安全基于此(例如,保护aganist XSS ) – 您无法发送跨域AJAX请求。 这基本上表明,如果来自一个站点(例如https://bank.ny.com )的内容被授予访问系统上的资源的权限,那么来自该站点的任何内容将共享这些权限,而来自另一个站点的内容( https://nsa.ny.com )必须单独授予权限(通常,术语origin是使用域名,应用程序层协议和端口号定义的)。

不过,您至少有4种解决方案可以解决您的问题:

首先 – 通过中间控制器层与您的服务对话。 这样做意味着生成代理类(通过svcutil.exe ,通过使用Visual Studio添加服务引用所做的工作)。 与此客户端的通信如下所示:

 public class TtsController { public JsonResult RunTts(string text) { using(var client = new ServiceReference1.Service1Client()) { var response = client.RunTts(text); return Json(response); ... 

然后JavaScript端应该使用这样的URL: var serviceUrl = "/Tts/RunTts" (以及传递给AJAX请求的正确JSON数据,我将进一步讨论)。

第二 – 直接与服务对话。 如果要直接与服务通信,则必须在Web应用程序中托管此服务。 应遵循正确的WCF配置以支持RESTful服务:

                    

对于RESTful端点,您应该使用的绑定是WebHttpBinding以及适当的行为。 另外,对于许多RESTful服务WebServiceHostFactory都有免配置的体验。 您的.svc文件应如下所示( MSDN ):

 <%@ ServiceHost Language="C#" Debug="true" Service="Namespace.Service1" CodeBehind="Service1.svc.cs" Factory="System.ServiceModel.Activation.WebServiceHostFactory" %> 

WebServiceHostFactory创建WebServiceHostFactory的实例,并且由于WebServiceHost将使用WebHttpBinding和相关行为自动配置端点,因此根本不需要在web.config中对此端点进行任何配置(当然,如果需要)要自定义绑定,您必须使用配置)( MSDN )。

然后访问该服务使用适当的完整URL: http://localhost:[port]/Service1.svc/RunTts或相对的一个: /Service1.svc/RunTts

由于您正在使用ASP.NET MVC,基于您的路由定义,请求将被分派到某个控制器,此类操作不存在。 您必须告诉MVC忽略您的服务路由:

 routes.IgnoreRoute("{resource}.svc/{*pathInfo}"); 

(顺便说一句:如果您将.svc文件放在应用程序的不同目录下,请分别修改URL和路由以忽略。)

您的代码需要一些额外的修复:

  1. 如果要以JSON格式发送消息,请正确指定dataTypecontentType参数:

     $.ajax({ url: serviceUrl, type: "POST", dataType: "json", contentType: "application/json; charset=utf-8", ... 
  2. 不要手动构造JSON字符串,因为它可能导致进一步的解析错误 – 使用转换器,例如:

     var data = new Object(); data.text = $('#speak').val(); var jsonString = JSON.stringify(data); $.ajax({ ... data: jsonString, ... 
  3. 为您的服务提供其他声明性信息:

     [ServiceContract] public interface IService1 { [OperationContract] [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)] string RunTts(string text); ... 
  4. 从项目中删除服务引用。 你不需要它,因为这里没有使用中间控制器。

第三JSONP ( 在这里和这里看)可用于克服原始政策限制。 但是你不能使用JSONP进行POST,因为它不会那样工作 – 它会创建一个元素来获取数据,这必须通过GET请求来完成。 JSONP解决方案不使用XmlHttpRequest对象,因此它不是标准理解方式的AJAX请求,但内容仍然是动态访问的 - 对最终用户没有区别。

 $.ajax({ url: serviceUrl, dataType: "jsonp", contentType: "application/json; charset=utf-8", data: data, ... [OperationContract] [WebGet(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped, UriTemplate="RunTts?text={text}")] public string RunTts(string text); 

允许跨域请求的RESTful WCF配置:

                         

第四 - CORS 。 在现代浏览器中实现替代 JSON with Padding。