通过 HTTPS 对 WCF 自托管 REST 服务的 POST 请求示例

Posted

技术标签:

【中文标题】通过 HTTPS 对 WCF 自托管 REST 服务的 POST 请求示例【英文标题】:Example of a POST request to WCF selfhosted REST service over HTTPS 【发布时间】:2019-12-31 20:38:01 【问题描述】:

在 SO 上有很多关于同一主题的问题,但似乎没有一个给出完整的答案。我已经检查了大部分问题/答案并进行了测试、测试和测试。所以希望这个问题能帮助我和其他正在苦苦挣扎的人。

问题。

如何设置通过 https 工作的 WCF 自托管 REST 服务? 这就是我尝试设置服务和客户端的方式。它不起作用!但我觉得每一次改变我都非常接近,但我没有达到目标。

那么,有人可以帮助我提供一个使用 REST 端点、基于 HTTPS 的自托管 WCF 和 POST 请求的完整示例吗?我曾尝试将来自各处的点点滴滴拼凑在一起,但我无法让它发挥作用! 我应该放弃吗?选择其他技术?

所以,一些代码:

[服务主机]

        Uri uri = new Uri("https://localhost:443");

        WebHttpBinding binding = new WebHttpBinding(WebHttpSecurityMode.Transport);
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;

        using (ServiceHost sh = new ServiceHost(typeof(Service1), uri))
        
            ServiceEndpoint se = sh.AddServiceEndpoint(typeof(IService1), binding, "");
            //se.EndpointBehaviors.Add(new WebHttpBehavior());

            // Check to see if the service host already has a ServiceMetadataBehavior
            ServiceMetadataBehavior smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
            // If not, add one
            if (smb == null)
                smb = new ServiceMetadataBehavior();
            smb.HttpGetEnabled = false; //**http**
            smb.HttpsGetEnabled = true; //**https**
            smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
            sh.Description.Behaviors.Add(smb);
            // Add MEX endpoint
            sh.AddServiceEndpoint(
              ServiceMetadataBehavior.MexContractName,
              MetadataExchangeBindings.CreateMexHttpsBinding(), //**https**
              "mex"
            );

            var behaviour = sh.Description.Behaviors.Find<ServiceBehaviorAttribute>();
            behaviour.InstanceContextMode = InstanceContextMode.Single;

            Console.WriteLine("service is ready....");
            sh.Open();

            Console.ReadLine();
            sh.Close();
        

[智能服务]

 [ServiceContract]
public interface IService1

    [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Xml, RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest, UriTemplate = "Datarows_IN/")]
    [OperationContract]
    bool Save(BatchOfRows batchOfRows);

[服务]

[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
public class Service1 : IService1

    public bool Save(BatchOfRows batchOfRows)
    
        Console.WriteLine("Entered Save");
        return true;
    

[BatchOfRows] - 简化

[DataContract]
public class BatchOfRows

    [DataMember]
    public int ID  get; set;  = -1;
    [DataMember]
    public string Data  get; set;  = "Hej";

这是建立在 SO answer 和 Microsoft 教程之后的 SO answer 之上。 我什至不知道哪个示例从哪里开始而其他示例从哪里结束。 我从这个开始:https://***.com/a/57554374/619791 在我尝试启用 https 之前效果很好,然后一切都停止了。

这是我尝试过的一些客户端代码。

[网络客户端]

            string uri = "https://localhost:443/Datarows_IN";
            WebClient client = new WebClient();
            client.Headers["Content-type"] = "application/json";
            client.Encoding = Encoding.UTF8;
            var b = new BatchOfRows();
            var settings = new JsonSerializerSettings()  DateFormatHandling = DateFormatHandling.MicrosoftDateFormat ;

            string str2 = "\"batchOfRows\":" + JsonConvert.SerializeObject(b, settings) + "";
            string result = client.UploadString(uri, "POST", str2);

[HttpClient]

            string str2 = "\"batchOfRows\":" + JsonConvert.SerializeObject(b, settings) + "";
            var contentData = new StringContent(str2, System.Text.Encoding.UTF8, "application/json");
            //string result = client.UploadString(uri, "POST", str2);
            //HttpResponseMessage response = client.PostAsJsonAsync("https://localhost:443/Datarows_IN", b).GetAwaiter().GetResult();
            HttpResponseMessage response = client.PostAsync("https://localhost:443/Datarows_IN", contentData).GetAwaiter().GetResult();
            response.EnsureSuccessStatusCode();

[频道工厂]

            //var c = new ChannelFactory<IService1>(binding, new EndpointAddress("https://localhost:443/Datarows_IN"));
            var c = new ChannelFactory<IService1>(binding, new EndpointAddress("https://localhost:443/"));
            ((WebHttpBinding)c.Endpoint.Binding).Security.Mode = WebHttpSecurityMode.Transport;
            ((WebHttpBinding)c.Endpoint.Binding).Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
            c.Endpoint.Behaviors.Add(new WebHttpBehavior());
            var aw = c.CreateChannel();

            var b = new ModuleIntegration.Client.Objects.BatchOfRows();
            aw.Save(b);

没有一个客户端工作。如果我调试我的服务端点永远不会触发。 这是我得到的当前错误:

<Fault xmlns="http://schemas.microsoft.com/ws/2005/05/envelope/none">
    <Code>
        <Value>Sender</Value>
        <Subcode>
            <Value xmlns:a="http://schemas.microsoft.com/ws/2005/05/addressing/none">a:ActionNotSupported</Value>
        </Subcode>
    </Code>
    <Reason>
        <Text xml:lang="sv-SE">The message with Action '' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver.  Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).</Text>
    </Reason>
</Fault>

请帮忙!为什么这么难?!?

【问题讨论】:

如果您只使用 REST 端点,请改用 .net WebApi。只有在必须使用 SOAP 时才使用 WCF。 WCF 相当冗长,需要配置大量工作。 【参考方案1】:

您的服务缺少WebHttpBehavior

没有它,WebInvoke 属性什么也不做,路径"Datarows_IN" 不会被识别为一个动作。

这是完整的(适合我的)服务主机代码:

var binding = new WebHttpBinding()

    Security = 
        Mode = WebHttpSecurityMode.Transport
    
;
var baseUri = new Uri("https://localhost:443");

using (ServiceHost sh = new ServiceHost(typeof(Service1), baseUri))

    var metadata = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
    if (metadata == null) 
        metadata = new ServiceMetadataBehavior();
        sh.Description.Behaviors.Add(metadata);
    
    metadata.HttpsGetEnabled = true;

    var endpoint = sh.AddServiceEndpoint(typeof(IService1), binding, "/");
    endpoint.EndpointBehaviors.Add(new WebHttpBehavior());

    Console.WriteLine("Service is ready....");
    sh.Open();

    Console.WriteLine("Service started. Press <ENTER> to close.");
    Console.ReadLine();
    sh.Close();

【讨论】:

有了这个小小的改动,一切正常!!!你是我的英雄!我之前的代码中有 webHttpBehavior,但我不得不将其注释掉,现在可以记住原因了,但是当我从 http 切换到 https 时,我的代码崩溃了。我要多点赞!! =)【参考方案2】:

你的sn-ps代码看起来很谨慎,导致出现上述错误,即我们应该添加WebHttpBehavior。 但是,我们还需要注意一件事。 通常,当我们在 IIS 中通过 HTTPS 托管服务时,服务需要提供证书来加密和签署服务器端和客户端之间的通信。 因此,我们在使用自托管时理论上应该绑定证书,否则服务应该无法正常工作。 为什么这个服务端点地址工作得很好?唯一的解释是我们在某处的端口上绑定了证书,比如IIS,有一个网站用https绑定,使用默认端口。 如果自定义端口没有与证书关联,我们应该使用以下命令绑定证书。

Netsh http 添加 sslcert ipport=0.0.0.0:portnumber 证书哈希=0000000000003ed9cd0c315bbb6dc1c08da5e6 appid=00112233-4455-6677-8899-AABBCCDDEEFF

https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate https://docs.microsoft.com/en-us/windows/win32/http/add-sslcert 默认情况下,证书只能在存储在本地计算机而不是当前用户中时设置。我们可以使用以下命令管理证书。

Certlm.msc

祝你好运。 最后,WCF 并非旨在设计 Restful 风格的服务。我们应该考虑 Asp.net WebAPI。https://docs.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api 如果有什么可以帮助的,请随时告诉我。

【讨论】:

你是对的。我已使用 netsh 命令将证书绑定到 。我只是忘了提。对于代码的下一次迭代,我将看看我是否可以将其重构为 WebAPI,但目前这不在时间范围内。

以上是关于通过 HTTPS 对 WCF 自托管 REST 服务的 POST 请求示例的主要内容,如果未能解决你的问题,请参考以下文章

WCF Rest 自托管证书安全服务返回 401 未经授权

使用 IIS7、WCF Rest、自定义身份验证模块进行模拟

通过 WCF Rest 服务为 clientaccesspolicy.xml 提供服务,同时托管在 IIS 上

2003 年使用 POST / JSON 托管 WCF Rest 服务

WCF 自托管命令行返回 503 错误

RestSharp - 授权标头未涉及 WCF REST 服务