WCF Restful CORS 无法执行 POST
Posted
技术标签:
【中文标题】WCF Restful CORS 无法执行 POST【英文标题】:WCF Restful CORS failed to execute POST 【发布时间】:2016-01-09 03:09:09 【问题描述】:我阅读了如何在 WCF 中创建 Restful 教程并从WCFTutorial 下载示例代码。 有 1 个主机(名称:MYFirstRestfulServiceHost)和 1 个客户端(名称:WebClient)。 WebClient 和 MYFirstRestfulServiceHost 位于不同的域中。 因此,当我发出 GET / POST 请求时,我遇到了一个问题:状态 405“方法不允许”。
经过 2 天的研究,我发现我必须在 Host app.config 中添加一些配置才能对跨域 wcf 服务执行 GET/POST 请求。
在 app.config 中添加配置后,我在 WebClient 中成功执行了 GET 请求,但在 WebClient 中按下按钮无法执行剩余的 POST、DELETE 和 PUT。
请告知我还应该配置什么才能使其成功。
下面是源码和配置:
IEmployeeService.cs
namespace MyFirstRESTfulService
[ServiceContract()]
public interface IEmployeeService
[WebGet(UriTemplate = "Employee", ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
List<Employee> GetAllEmployeeDetails();
[WebGet(UriTemplate = "Employee?id=id", ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
Employee GetEmployee(int Id);
[WebInvoke(Method = "POST", UriTemplate = "EmployeePOST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
[OperationContract]
void AddEmployee(Employee newEmp);
[WebInvoke(Method = "PUT", UriTemplate = "EmployeePUT", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
[OperationContract]
void UpdateEmployee(Employee newEmp);
[WebInvoke(Method = "DELETE", UriTemplate = "Employee/empId", ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
void DeleteEmployee(string empId);
EmployeeService.cs
namespace MyFirstRESTfulService
[ServiceContract()]
public interface IEmployeeService
[WebGet(UriTemplate = "Employee", ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
List<Employee> GetAllEmployeeDetails();
[WebGet(UriTemplate = "Employee?id=id", ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
Employee GetEmployee(int Id);
[WebInvoke(Method = "POST", UriTemplate = "EmployeePOST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
[OperationContract]
void AddEmployee(Employee newEmp);
[WebInvoke(Method = "PUT", UriTemplate = "EmployeePUT", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
[OperationContract]
void UpdateEmployee(Employee newEmp);
[WebInvoke(Method = "DELETE", UriTemplate = "Employee/empId", ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
void DeleteEmployee(string empId);
MyFirstRestfulServiceHost
app.config
<?xml version="1.0"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="POST,GET,OPTIONS" />
<add name="Access-Control-Max-Age" value="1728000" />
</customHeaders>
</httpProtocol>
</system.webServer>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint crossDomainScriptAccessEnabled="true"></standardEndpoint>
</webHttpEndpoint>
<webScriptEndpoint>
<standardEndpoint crossDomainScriptAccessEnabled="true"></standardEndpoint>
</webScriptEndpoint>
</standardEndpoints>
<bindings>
<webHttpBinding>
<binding name="webHttpBindingWithJsonP" crossDomainScriptAccessEnabled="true"/>
</webHttpBinding>
</bindings>
</system.serviceModel>
</configuration>
MyFirstRESTfulServiceHost
程序.cs
static void Main(string[] args)
try
Uri httpUrl = new Uri("http://localhost:8090/MyService/EmployeeService");
WebServiceHost host = new WebServiceHost(typeof(MyFirstRESTfulService.EmployeeService), httpUrl);
host.Open();
foreach (ServiceEndpoint se in host.Description.Endpoints)
Console.WriteLine("Service is host with endpoint " + se.Address);
Console.WriteLine("Host is running... Press <Enter> key to stop");
Console.ReadLine();
catch (Exception ex)
Console.WriteLine(ex.Message);
Console.ReadLine();
网络客户端
默认.aspx
<script type="text/javascript" >
function RefreshPage()
var serviceUrl = "http://localhost:8090/MyService/EmployeeService/Employee";
$.ajax(
type: "GET",
url: serviceUrl,
dataType: 'jsonp',
contentType: "application/json; charset=utf-8",
success: function (data)
var itemRow = "<table>";
$.each(data, function (index, item)
itemRow += "<tr><td>" + item.EmpId + "</td><td>" + item.Fname + "</td></tr>";
);
itemRow += "</table>";
$("#divItems").html(itemRow);
,
error: ServiceFailed
);
function POSTMethodCall()
var EmpUser = [ "EmpId": "13", "Fname": "WebClientUser", "Lname": "Raju", "JoinDate": Date(1224043200000), "Age": "23", "Salary": "12000", "Designation": "Software Engineer"];
var st = JSON.stringify(EmpUser);
$.ajax(
type: "POST",
url: "http://localhost:8090/MyService/EmployeeService/EmployeePOST",
data: JSON.stringify(EmpUser),
contentType: "application/json; charset=utf-8",
dataType: "jsonp",
success: function (data)
// Play with response returned in JSON format
,
error:ServiceFailed
);
function DELETEMethodCall()
$.ajax(
type: "DELETE",
url: "http://localhost:8090/MyService/EmployeeService/Employee/2",
data: "",
contentType: "application/json; charset=utf-8",
dataType: "jsonp",
success: function (data)
// Play with response returned in JSON format
,
error: function (msg)
alert(msg);
);
function PUTMethodCall()
var EmpUser = [ "EmpId": "3", "Fname": "WebClientUser", "Lname": "Raju", "JoinDate": Date(1224043200000), "Age": "23", "Salary": "12000", "Designation": "Software Engineer"];
$.ajax(
type: "PUT",
url: "http://localhost:8090/MyService/EmployeeService/EmployeePUT",
data: EmpUser,
contentType: "application/json; charset=utf-8",
dataType: "jsonp",
success: function (data)
alert('success');
// Play with response returned in JSON format
,
error: ServiceFailed
);
function ServiceFailed(xhr)
alert("response:" + xhr.responseText);
if (xhr.responseText)
var err = xhr.responseText;
if (err)
error(err);
else
error( Message: "Unknown server error." )
return;
</script>
<input type="button" onclick="PUTMethodCall();" name="btnUpdate" value ="Update" />
<input type="button" onclick="DELETEMethodCall();" name="btnDelete" value ="Delete" />
<input type="button" onclick="POSTMethodCall();" name="btnAdd" value ="Add" />
<input type="button" onclick="RefreshPage()" name="btnRefesh" value ="Refresh" />
<div id="divItems"></div>
按刷新按钮(GET)成功检索员工信息列表。但是,更新、删除和添加失败。 该图显示了在 chrome 中按下“添加”按钮后的状态 405 错误。
非常感谢您的建议和帮助!
【问题讨论】:
【参考方案1】:我不认为这里的问题是由 CORS 引起的,但事实上它实际上是一个 GET 请求,而不是所需的 POST。
$.ajax(
type: "POST",
url: "http://localhost:8090/MyService/EmployeeService/EmployeePOST",
data: JSON.stringify(EmpUser),
contentType: "application/json; charset=utf-8",
dataType: "jsonp",
^^^^^^^^^
success: function (data)
// Play with response returned in JSON format
,
error:ServiceFailed
);
JSONP 是一种避免跨域 ajax 请求的机制,JSONP 请求将始终使用 GET 发送。
您应该将数据类型设置为您期望从 API 获得的类型。
在进行跨域ajax请求时,浏览器会先进行OPTIONS请求。这称为预检,您可以在此处了解更多信息:MDN - CORS - Preflighted requests
要启用此功能,您需要在 IEmployeeService.cs 中为 OPTIONS 方法创建一个路由,并返回 200 的空响应。您的配置文件似乎设置了正确的标头。
【讨论】:
感谢您的回复@happyjack!所以我应该把它设为 dataType:"json" ? 我删除了 dataType: "jsonp" 这一行,chrome 控制台显示错误 No 'Access-Control-Allow-Origin' header is present on the requested resource。因此,Origin 'localhost:25249' 不允许访问。响应的 HTTP 状态代码为 405。 这是一个不同的问题,但我已经编辑了答案以包含可能的原因。 我应该如何为 IEmployeeService.cs 中的 OPTIONS 方法创建路由?您介意更正我的源代码吗? 我自己都不认识。我相信这是您原始问题的解决方案,因此请将其标记为正确答案,如果您需要其他帮助,请发布另一个问题。话虽如此,下面 G Brown 的回答似乎会对您有所帮助。【参考方案2】:如果您获得 405(2011 年的新 Web 标准;请参阅原始 RFC here),则需要启用 CORS。
使用 WCF 启用 CORS 并不是特别容易,因为您需要做的不仅仅是像在 web.config 中尝试的那样添加自定义标头。对于 ASP.NET Web API,只需编辑 web.config 就足够了,但如果这不是您正在处理的工作,则需要在 Web 服务中添加大量自定义代码以允许 OPTIONS 标头。幸运的是,人们过去曾这样做过。 (基本上,您需要创建一个消息检查器,然后创建一些使用消息检查器类添加所需标头的端点行为。)
添加必要的消息检查器的最简单方法是使用服务主机工厂,例如在this article 末尾链接的那个。将服务主机工厂引用添加到 .svc 文件以及实现消息检查器的两个必要文件后,您将成功启用 CORS。只要你得到405错误,就说明你没有成功启用CORS。
有关实现的更多参考,请参阅http://enable-cors.org/server_wcf.html 的示例。
【讨论】:
只是为了挑剔,但任何 ajax 请求(包括 GET)的响应中都需要 CORS 标头。确实,预检 OPTIONS 请求不会发生在标准 ajax GET 请求上,但也不会发生在标准 ajax POST 请求上。此外,您不能仅通过更改 access-control-allow-origin 来阻止任何人访问发布数据。 CORS 是为了保护最终用户,而不是服务器。 这不是挑剔,它是正确的。有机会会编辑。谢谢以上是关于WCF Restful CORS 无法执行 POST的主要内容,如果未能解决你的问题,请参考以下文章
Restful WCF 无法达到 operationcontract
将 transferMode 更改为“Streamed”后无法加载 wcf restful 帮助页面
C# Restful WCF 服务。无法在帖子正文中反序列化 XML
Fileless Restful WCF 在本地 IIS7 中托管时无法正常工作,但可以在本地运行
尽管有 web.config 条目,但带有 ASP.NET WCF 服务的 jQuery AJAX CORS 无法正常工作