我阅读如何在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
Program.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();
}
}
WebClient
Default.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错误。
我非常感谢您的建议和帮助!
答案 0 :(得分:2)
我不相信这里的问题是由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的空响应。您的配置文件似乎正在设置正确的标题。
答案 1 :(得分:1)
如果您获得405(2011年的新Web标准,请参阅原始RFC here),则需要启用CORS 。
使用WCF启用CORS并不是特别容易,因为您必须做的不仅仅是在web.config中尝试添加自定义标头。只需编辑web.config就足以支持ASP.NET Web API,但如果这不是您正在处理的内容,那么您需要添加大量自定义代码以允许OPTIONS标头,在您的Web服务中。幸运的是,人们过去曾这样做过。 (基本上,您需要创建一个消息检查器,然后创建一些使用消息检查器类添加所需标头的端点行为。)
添加必要的消息检查程序的最简单方法是使用服务主机工厂,例如this article末尾链接的服务主机工厂。将服务主机工厂引用添加到.svc文件以及实现消息检查器的两个必要文件之后,您将成功启用CORS。只要您收到405错误,您就无法成功启用CORS。
有关实施的更多参考,请参阅http://enable-cors.org/server_wcf.html上的示例。