mono-service raspbian 上的 WCF 选项
mono-service WCF OPTIONS on rasbian
我在 raspberry jessie 上成功创建了一个 self-hosted WCF mono-service,并且它(几乎)可以工作。
版本
- Mono JIT 编译器版本 4.2.1
- Raspbian GNU/Linux 8\n\l
问题是 Chrome 网络应用无法使用它,因为它不响应任何 OPTIONS 请求。请求保持 'pending' 状态,直到我终止服务。
我确实尝试了很多与CORS相关的解决方案,但我认为问题可能更深一些。我是这么认为的,因为 OPTIONS 请求没有到达 CorsDispatchMessageInspector(下面的第三个代码片段)。
我目前的设置。
app.config
<startup>
<supportedRuntime version="v4.5" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<services>
<service name="AspaDeviceControlCenter.Fp550Module.Fp550Module">
<endpoint address="http://localhost:8111/json/fp550/" binding="webHttpBinding" behaviorConfiguration="jsonEndpointBehaviour" contract="AspaDeviceControlCenter.Fp550Module.IFp550Module"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="jsonEndpointBehaviour">
<webHttp/>
<corsEndpointBehaviorExtension/>
</behavior>
<behavior name="webscriptBehavior">
<enableWebScript/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<directoryBrowse enabled="true"/>
</system.webServer>
<extensions>
<behaviorExtensions>
<add name="corsEndpointBehaviorExtension" type="AspaDeviceControlCenter.Service.Hosting.CorsEndpointBehaviorExtension, AspaDeviceControlCenter.Service"/>
</behaviorExtensions>
</extensions>
我使用了 enable-cors.org 中的 EndpoinBehaviorExtension 解决方案来将必需的 'alow' headers 添加到任何 OPTIONS 请求,但是那里的断点不会为 OPTIONS 请求触发,只为 GET 和 P OST
扩展实际部分
public class CorsEndpointBehaviorExtension : BehaviorExtensionElement, IEndpointBehavior
{
/*some empty methods*/
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
var requiredHeaders = new Dictionary<string, string>();
//requiredHeaders.Add("Access-Control-Allow-Origin", "*");
requiredHeaders.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS");
requiredHeaders.Add("Access-Control-Allow-Headers", "Accept,Origin,Authorization,X-Requested-With,Content-Type,X-Tenant");
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CorsDispatchMessageInspector(requiredHeaders));
}
现在应该有帮助的部分以及我认为问题更深层次的部分,可能是 OS 配置问题,但我不是 Linux 专家。
public class CorsDispatchMessageInspector: IDispatchMessageInspector
{
Dictionary<string, string> requiredHeaders;
public CorsDispatchMessageInspector(Dictionary<string, string> headers)
{
requiredHeaders = headers ?? new Dictionary<string, string>();
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
return null;
}
public void BeforeSendReply(ref Message reply,object correlationState)
{
var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (var item in requiredHeaders)
{
httpHeader.Headers.Add(item.Key, item.Value);
}
}
}
AfterReceiveRequest 和 BeforeSendReply 都有断点,当我发送 POST / GET 时断点,但当请求方法是 OPTIONS
时不会断点
服务契约接口。我定义了许多不同的操作合同来测试到目前为止我在互联网上找到的内容。 None 的人响应了 OPTIONS 请求。
[ServiceContract]
public interface IFp550Module : IControlCenterModule
{
[OperationContract]
bool Init();
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
string Open(/*int port, string settings*/);
[OperationContract]
[WebInvoke(Method = "*", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
string Version();
[OperationContract]
[WebInvoke(Method = "OPTIONS", UriTemplate="*", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
bool GetOptions();
}
运行时没有错误回调中断。
var host = new ServiceHost(module);
_serviceHosts.Add(host);
host.Faulted += OnFaulted;
host.UnknownMessageReceived += OnUnknownMessageReceived;
host.Opening += module.ServiceStarting;
host.Closing += module.ServiceClosing;
host.Open();
我还尝试了我自己的 CustomTextMessageEncoder : MessageEncoder,并且 public override Message ReadMessage method breakpoint was hit only for GET/POST。我必须深入到那个 OPTIONS 请求断点吗?或者也许它只是 mono-service.
中某处的一些标志
此外,当我停止服务时,所有未决选项请求 returns 网络错误。
当我启动请求然后发送任何请求(包括 OPTIONS)时,应用程序输出中有一条 "Thread started: #16" 消息,因此 OPTIONS 请求似乎达到 'the application level'。
更深入的下一步是我自己的 ServiceHost class 和通道侦听器,但这是大量工作,而且问题甚至可能不在应用程序级别。
如果我遗漏了什么,请告诉我。我把这个问题作为最后的手段。
好的,我已经厌倦了搜索 'mono custom request methods',我发现这是由于 mono-bug-tracker 中记录的错误所致。如果找到解决方法,我会更新此答案。
我正在寻找一种方法来 re-implement 有问题的部分。
编辑: 我解决了我的问题。我未能重新编译整个单声道及其依赖项,所以我只重新编译了 System.ServiceModel.dll 并在单声道 GAC 目录 System.ServiceModel 文件夹中用新的替换了原来的。加载了新的 dll 并接受了更改。
更改发生在 HttpReplyChannel.cs
if (ctxi.Request.HttpMethod == "POST" || ctxi.Request.HttpMethod == "PUT" )
msg = CreatePostMessage (ctxi);
else if (ctxi.Request.HttpMethod == "GET" || ctxi.Request.HttpMethod == "DELETE" || ctxi.Request.HttpMethod == "OPTIONS")
msg = Message.CreateMessage (MessageVersion.None, null);
我添加了额外的方法,在更改之前,只有 GET 和 POST。
现在我的服务能够接收选项请求并使用 MessageInspector 添加所需的 headers。
我在 raspberry jessie 上成功创建了一个 self-hosted WCF mono-service,并且它(几乎)可以工作。
版本
- Mono JIT 编译器版本 4.2.1
- Raspbian GNU/Linux 8\n\l
问题是 Chrome 网络应用无法使用它,因为它不响应任何 OPTIONS 请求。请求保持 'pending' 状态,直到我终止服务。
我确实尝试了很多与CORS相关的解决方案,但我认为问题可能更深一些。我是这么认为的,因为 OPTIONS 请求没有到达 CorsDispatchMessageInspector(下面的第三个代码片段)。
我目前的设置。
app.config
<startup>
<supportedRuntime version="v4.5" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<services>
<service name="AspaDeviceControlCenter.Fp550Module.Fp550Module">
<endpoint address="http://localhost:8111/json/fp550/" binding="webHttpBinding" behaviorConfiguration="jsonEndpointBehaviour" contract="AspaDeviceControlCenter.Fp550Module.IFp550Module"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="jsonEndpointBehaviour">
<webHttp/>
<corsEndpointBehaviorExtension/>
</behavior>
<behavior name="webscriptBehavior">
<enableWebScript/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<directoryBrowse enabled="true"/>
</system.webServer>
<extensions>
<behaviorExtensions>
<add name="corsEndpointBehaviorExtension" type="AspaDeviceControlCenter.Service.Hosting.CorsEndpointBehaviorExtension, AspaDeviceControlCenter.Service"/>
</behaviorExtensions>
</extensions>
我使用了 enable-cors.org 中的 EndpoinBehaviorExtension 解决方案来将必需的 'alow' headers 添加到任何 OPTIONS 请求,但是那里的断点不会为 OPTIONS 请求触发,只为 GET 和 P OST
扩展实际部分
public class CorsEndpointBehaviorExtension : BehaviorExtensionElement, IEndpointBehavior
{
/*some empty methods*/
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
var requiredHeaders = new Dictionary<string, string>();
//requiredHeaders.Add("Access-Control-Allow-Origin", "*");
requiredHeaders.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS");
requiredHeaders.Add("Access-Control-Allow-Headers", "Accept,Origin,Authorization,X-Requested-With,Content-Type,X-Tenant");
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CorsDispatchMessageInspector(requiredHeaders));
}
现在应该有帮助的部分以及我认为问题更深层次的部分,可能是 OS 配置问题,但我不是 Linux 专家。
public class CorsDispatchMessageInspector: IDispatchMessageInspector
{
Dictionary<string, string> requiredHeaders;
public CorsDispatchMessageInspector(Dictionary<string, string> headers)
{
requiredHeaders = headers ?? new Dictionary<string, string>();
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
return null;
}
public void BeforeSendReply(ref Message reply,object correlationState)
{
var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (var item in requiredHeaders)
{
httpHeader.Headers.Add(item.Key, item.Value);
}
}
}
AfterReceiveRequest 和 BeforeSendReply 都有断点,当我发送 POST / GET 时断点,但当请求方法是 OPTIONS
时不会断点服务契约接口。我定义了许多不同的操作合同来测试到目前为止我在互联网上找到的内容。 None 的人响应了 OPTIONS 请求。
[ServiceContract]
public interface IFp550Module : IControlCenterModule
{
[OperationContract]
bool Init();
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
string Open(/*int port, string settings*/);
[OperationContract]
[WebInvoke(Method = "*", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
string Version();
[OperationContract]
[WebInvoke(Method = "OPTIONS", UriTemplate="*", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
bool GetOptions();
}
运行时没有错误回调中断。
var host = new ServiceHost(module);
_serviceHosts.Add(host);
host.Faulted += OnFaulted;
host.UnknownMessageReceived += OnUnknownMessageReceived;
host.Opening += module.ServiceStarting;
host.Closing += module.ServiceClosing;
host.Open();
我还尝试了我自己的 CustomTextMessageEncoder : MessageEncoder,并且 public override Message ReadMessage method breakpoint was hit only for GET/POST。我必须深入到那个 OPTIONS 请求断点吗?或者也许它只是 mono-service.
中某处的一些标志此外,当我停止服务时,所有未决选项请求 returns 网络错误。
当我启动请求然后发送任何请求(包括 OPTIONS)时,应用程序输出中有一条 "Thread started: #16" 消息,因此 OPTIONS 请求似乎达到 'the application level'。
更深入的下一步是我自己的 ServiceHost class 和通道侦听器,但这是大量工作,而且问题甚至可能不在应用程序级别。
如果我遗漏了什么,请告诉我。我把这个问题作为最后的手段。
好的,我已经厌倦了搜索 'mono custom request methods',我发现这是由于 mono-bug-tracker 中记录的错误所致。如果找到解决方法,我会更新此答案。
我正在寻找一种方法来 re-implement 有问题的部分。
编辑: 我解决了我的问题。我未能重新编译整个单声道及其依赖项,所以我只重新编译了 System.ServiceModel.dll 并在单声道 GAC 目录 System.ServiceModel 文件夹中用新的替换了原来的。加载了新的 dll 并接受了更改。
更改发生在 HttpReplyChannel.cs
if (ctxi.Request.HttpMethod == "POST" || ctxi.Request.HttpMethod == "PUT" )
msg = CreatePostMessage (ctxi);
else if (ctxi.Request.HttpMethod == "GET" || ctxi.Request.HttpMethod == "DELETE" || ctxi.Request.HttpMethod == "OPTIONS")
msg = Message.CreateMessage (MessageVersion.None, null);
我添加了额外的方法,在更改之前,只有 GET 和 POST。
现在我的服务能够接收选项请求并使用 MessageInspector 添加所需的 headers。