如屏幕截图所示,该服务未托管在您的 Web 应用程序中。您无法直接从客户端访问此类服务(托管在您的 Web 应用程序外部),因为您违反了同源政策限制。它是信任的基本概念之一,网络安全基于此(例如,保护反对者)XSS) - 您无法发送跨域 AJAX 请求。这实质上表明,如果内容来自一个站点(例如https://bank.ny.com
) 被授予访问系统资源的权限,那么来自该站点的任何内容都将共享这些权限,而来自另一个站点 (https://nsa.ny.com
)必须单独授予权限(一般来说,术语origin使用域名、应用层协议和端口号定义)。
尽管如此,您至少有 4 个解决方案来解决您的问题:
First- 通过中间控制器层与您的服务对话。采用这种方式意味着生成代理类(通过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 数据,我将进一步介绍这一点)。
Second- 直接与服务人员交谈。如果您想直接与该服务通信,则必须在 Web 应用程序中托管该服务。应遵循正确的 WCF 配置以支持 RESTful 服务:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="webby">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="Namespace.Service1">
<endpoint address=""
behaviorConfiguration="webby"
binding="webHttpBinding"
contract="Namespace.IService1" />
</service>
</services>
</system.serviceModel>
对于 RESTful 端点,您应该使用的绑定是WebHttpBinding
以及适当的行为。或者,许多 RESTful 服务都具有免配置体验 -WebServiceHostFactory
。您的 .svc 文件应如下所示(MSDN):
<%@ ServiceHost Language="C#" Debug="true" Service="Namespace.Service1"
CodeBehind="Service1.svc.cs"
Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>
WebServiceHostFactory
创建一个实例WebServiceHost
,并且自从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 和路由以忽略。)
您的代码需要一些额外的修复:
-
如果要以 JSON 格式发送消息,请指定dataType
and contentType
参数正确:
$.ajax({
url: serviceUrl,
type: "POST",
dataType: "json",
contentType: "application/json; charset=utf-8",
...
-
不要手动构建 JSON 字符串,因为它可能会导致进一步的解析错误 - 使用转换器,例如:
var data = new Object();
data.text = $('#speak').val();
var jsonString = JSON.stringify(data);
$.ajax({
...
data: jsonString,
...
-
为您的服务提供额外的声明性信息:
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
string RunTts(string text);
...
从项目中删除服务引用。您不需要它,因为这里没有使用中间控制器。
Third - JSONP (look here and here)可用于克服原产地政策限制。但是你不能使用 JSONP 进行 POST,因为它只是不是这样的- 它创建了一个<script>
元素来获取数据,这必须通过 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 配置:
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="jsonp" crossDomainScriptAccessEnabled="true" />
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="webby">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="Namespace.Service1">
<endpoint address=""
behaviorConfiguration="webby"
binding="webHttpBinding"
bindingConfiguration="jsonp"
contract="Namespace.IService1" />
</service>
</services>
</system.serviceModel>
Fourth - CORS。在现代浏览器中实现选择带填充的 JSON。