wcf 调用wcf_使用WCF打破所有规则

wcf 调用wcf

wcf 调用wcf

Sometimes, in my job, I go onsite at partners and work with them, sometimes architecturally, sometimes doing proofs of concepts to make sure they're comfortable with things working together.

有时,在工作中,我会在合作伙伴现场与他们合作,有时在架构上,有时会进行概念验证,以确保他们对共同工作感到满意。

This week I’m onsite at a large enterprise and one of the things they wanted to see, amongst many, was .NET interoperating with an existing Web Service. It's not important what platform their Web Service is running on, but it's not Windows and .NET. What was important was that they had WSDL and XSDs for the service, which put them above 99% of the Web Services I come upon in the enterprise.

本周,我在一家大型企业上班,他们希望看到的一件事是,.NET与现有Web服务的互操作。 他们的Web服务在什么平台上运行并不重要,但Windows和.NET都不重要。 重要的是它们具有用于服务的WSDL和XSD,这使它们超过了我在企业中遇到的Web服务的99%。

The team here said that this particular web service used WS-Security and was a compliant web service. I figured, and told them, no problem. That's something .NET is good at. Moving angle-brackets around is something both I, and .NET do pretty well. I figured we had a number of options.

这里的团队说,这个特定的Web服务使用WS-Security,并且是兼容的Web服务。 我想通了,并告诉他们,没问题。 .NET擅长这一点。 移动尖括号是我和.NET都很好的事情。 我认为我们有很多选择。

In this scenario was I going to be the Client, I could use:

在这种情况下,我将成为客户,我可以使用:

  • WCF - svcutil.exe - good

    WCF-svcutil.exe-很好
  • System.Web.Services - wsdl.exe - pretty good

    System.Web.Services-wsdl.exe-很好
  • WebClient/XDocument/XmlDocument - not so good, but workable.

    WebClient / XDocument / XmlDocument-不太好,但可行。

You get the idea. There were a few things wrong, though.

你明白了。 不过,有几处错误。

不良WSDL (Bad-ish WSDL)

They gave me the WSDL and when I ran svcutil.exe on it, I got this error (the elements have been changed to protect the innocent.)

他们给了我WSDL,当我在上面运行svcutil.exe时,出现了这个错误(为了保护无辜,这些元素已经更改了。)

C:\Users\Scott\Desktop\foo>svcutil foo.Wsdl foo.xsd /config:app.config
Microsoft (R) Service Model Metadata Tool
[Microsoft (R) Windows (R) Communication Foundation, Version 3.0.4506.2152]
Copyright (c) Microsoft Corporation. All rights reserved.

Error: Cannot import wsdl:binding
Detail: The WSDL binding named FooBinding is not valid because no match for
operation GetFooDetails was found in the corresponding portType definition.
XPath to Error Source: //wsdl:definitions[@targetNamespace='urn:foo:v1']/wsdl:
binding[@name='FooBinding']

Error: Cannot import wsdl:port
Detail: There was an error importing a wsdl:binding that the wsdl:port is dependent on.
XPath to wsdl:binding: //wsdl:definitions[@targetNamespace='urn:foo:v1']
/wsdl:binding[@name='FooBinding']
XPath to Error Source: //wsdl:definitions[@targetNamespace='urn:foo:v1']
/wsdl:service[@name='FooService']/wsdl:port[@name='FooPort']

I googled binged around for this to no avail. After staring at the file long enough, I realized that while this is a lousy error message (to be clear) it was telling me (obscurely) what was up all the while.

我用 搜索 了一下,没有结果。 盯着文件看了足够长的时间后,我意识到虽然这是一个糟糕的错误消息(需要清除),但它却一直在(模糊地)告诉我发生了什么。

Here's a snippet of what I was looking at:

这是我正在查看的代码片段:

    <Type name="FooType">
<operation name="FooSearch">
<input message="tns:FooSearchRequest"></input>
<output message="tns:FooSearchResponse"></output>
<fault name="FooFault" message="tns:FooFault"></fault>
</operation>
</Type>

<binding name="FooBinding" type="tns:FooType">

<soap:binding style="document" trans="http://schemas.xmlsoap.org/soap/http"></soap:binding>

<operation name="FooSearch">
<soap:operation soapAction=""></soap:operation>
<input name="FooSearchRequest">
<soap:body use="literal"></soap:body>
</input>
<output name="FooSearchResponse">
<soap:body use="literal"></soap:body>
</output>
<fault name="FooFault">
<soap:fault name="FooFault" use="literal"></soap:fault>
</fault>
</operation>
...

The key was that their WSDL didn't have the name="" attribute on the input and output elements of the operation. The name needs to line up to the operation name in the binding.

关键是他们的WSDL在操作的输入和输出元素上没有name =“”属性。 该名称需要与绑定中的操作名称对齐。

<Type name="FooType">
<operation name="FooSearch">
<input name="FooSearchRequest" message="tns:FooSearchRequest"></input>
<output name="FooSearchResponse" message="tns:FooSearchResponse"></output>
<fault name="FooFault" message="tns:FooFault"></fault>
</operation>
</Type>

Once these new name="" attributes were added, I was able to generate my client-side stubs. I had to edit their WSDL, which sucks. However, you might argue svcutil.exe could chill out. Either way, a speed bump.

添加了这些新的name =“”属性后,便能够生成客户端存根。 我不得不编辑他们的WSDL,这很糟糕。 但是,您可能认为svcutil.exe可能会变冷。 无论哪种方式,都是减速带。

声称合规 (Claiming Compliance)

I was told the Web Service would use WS-Security and a usernameToken. However, the actual message seemed like it was missing something.

有人告诉我Web服务将使用WS-Security和usernameToken。 但是,实际的消息似乎丢失了一些东西。

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext" xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility" xmlns:tns="urn:foo:v1" xsi:schemaLocation="http://www.w3.org/2003/05/soap-envelope http://www.w3.org/2003/05/soap-envelope/soap-envelope.xsd urn:foo:v1 com.foo.messages.v1.xsd">
<soapenv:Header>
<wsse:Security>
<wsse:UsernameToken>
<wsse:Username>secret</wsse:Username>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<tns:FooRequest>
...

It's been a while (about 18 months) since I did any WCF and WS-Security, but UsernameToken really needs to have a Password element also. Additionally, when you're using WS-Security, you typically get WS-Addressing, etc along for the ride. There's other headers I'd expect to see.

自从我进行了WCF和WS-Security以来已经有一段时间了(大约18个月),但是UsernameToken确实还需要具有一个Password元素。 另外,当您使用WS-Security时,通常会得到WS-Addressing等信息。 我希望看到其他标题。

I trudged on, built up the message and tried to send it off. First problem was that the endpoint URI I had was http, not https. It's not possible to send a UsernameToken in plain-text - the system explicitly forbids it. However, their system was setup to default to basic HTTP. Some gnashing of teeth and I found an SSL endpoint I could use. However, it's a hassle to debug SSL traffic. I usually use ProxyTrace or TCPTrace but with SSL, not so much.

我迷路了,积累了信息并试图发送出去。 第一个问题是我拥有的端点URI是http,而不是https。 无法以纯文本格式发送UsernameToken-系统明确禁止使用它。 但是,他们的系统默认设置为基本HTTP。 咬牙切齿,我发现我可以使用一个SSL端点。 但是,调试SSL流量很麻烦。 我通常使用ProxyTraceTCPTrace,但使用SSL的机会不多。

使用代理嗅探SSL流量 (Sniffing SSL Traffic with a Proxy)

I ended up using Charles, an HTTP Proxy that can act as a man-in-the middle, issue an SSL cert, then decrypt the traffic, and forward it along to the real endpoint. However, the SSL Cert Charles issues isn't from a certificate authority, so I had to make a Policy to blindly (temporarily) accept all certificates:

我最终使用了Charles ,它是一个HTTP代理,可以充当中间人,发出SSL证书,然后解密流量,并将其转发到真实的端点。 但是,SSL Cert Charles问题不是来自证书颁发机构,因此我必须制定一个策略以盲目(临时)接受所有证书:

internal class AcceptAllCertificatePolicy : ICertificatePolicy
{
public AcceptAllCertificatePolicy(){}

public bool CheckValidationResult(ServicePoint sPoint,
X509Certificate cert, WebRequest wRequest, int certProb)
{
return true; //Always accept
}
}

Then I apply it in this (obsolete, but easy) way:

然后,我以这种(过时但简单)的方式应用它:

ServicePointManager.CertificatePolicy = new AcceptAllCertificatePolicy();

Now I can run all my traffic through my local man-in-the-middle. I can set the proxy in my config file:

现在,我可以通过本地中间人来管理所有流量。 我可以在配置文件中设置代理:

<basicHttpBinding>
<binding name="FooBinding"
...
proxyAddress="http://BigAssLaptop:8888"
useDefaultWebProxy="false">

or in my own binding:

或在我自己的绑定中:

WSHttpBinding oldBinding = new WSHttpBinding();
oldBinding.ProxyAddress = new Uri("http://BIGASSLAPTOP:8888");

FooPortTypeClient svc = new FooPortTypeClient(oldBinding, new EndpointAddress("https://example.com/foo/v1"));

This let me see the outgoing request. I noticed immediately that my WCF client was sending a LOT more stuff that I needed.

这让我看到了传出的请求。 我立即注意到,我的WCF客户端正在发送更多我需要的东西。

打破规则 (Breaking the Rules)

It was hard for the client to hear, but here's the deal. They were using the usernameToken element, alone, in the WS-Security namespace in the style of an apiKey. You often see these kinds of APIs in the Web 2.0 world, when intense security isn't needed. You get a key that's unique to you, basically a GUID, and it also acts as a tracker for the provider.

客户很难听到,但这是交易。 他们单独使用apiKey样式的WS-Security命名空间的usernameToken元素 在不需要高度安全性的Web 2.0世界中,您经常会看到这类API。 您将获得一个唯一的密钥,基本上就是一个GUID,它还充当提供者的跟踪器。

However, this isn't how WS-Security usernameTokens work, or are supposed to work. Perhaps a better way would have been for them to use a custom soap:header, rather than trying to tunnel "apikey" semantics into an existing token.

但是,这不是WS-Security usernameToken的工作原理,也不应该是这样。 也许对他们来说,更好的方法是使用自定义的soap:header,而不是尝试将“ apikey”语义传递到现有令牌中。

At this point, regardless of relative-wrongness, I still need to get the WCF client to talk to this unusual endpoint. I could use one of the other XML mechanism available, or, gasp, a StringBuilder, but since I wasn't having trouble with the body of the message, just the envelope.

在这一点上,无论相对错误,我仍然需要让WCF客户端与这个不寻常的端点进行对话。 我可以使用其他可用的XML机制之一,也可以使用StringBuilder喘气,但是由于我对邮件正文没有任何麻烦,因此只需使用信封即可。

This essentially means that I wanted WCF to do something incorrect, on purpose. After a call to Steve Maine and team, along with some general freaking out, I was able to get WCF to spit out JUST a usernameToken, like this.

这实质上意味着我希望WCF故意做一些不正确的事情。 打电话给史蒂夫·缅因(Steve Maine)和团队之后,加上一些大惊小怪的事情,我得以让WCF吐出一个用户名令牌,就像这样。

WSHttpBinding oldBinding = new WSHttpBinding();
oldBinding.Security.Mode = SecurityMode.TransportWithMessageCredential;
//Just the username
oldBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
//And basically nothing else
oldBinding.Security.Message.NegotiateServiceCredential = false;
oldBinding.Security.Message.EstablishSecurityContext = false;

//oldBinding.ProxyAddress = new Uri("http://BIGASSLAPTOP:8888");
//oldBinding.UseDefaultWebProxy = false;

//remove the timestamp
BindingElementCollection elements = oldBinding.CreateBindingElements();
elements.Find<SecurityBindingElement>().IncludeTimestamp = false;

//sets the content type to application/soap+xml
elements.Find<TextMessageEncodingBindingElement>().MessageVersion = MessageVersion.Soap12;
CustomBinding newBinding = new CustomBinding(elements);
FooPortTypeClient svc = new FooPortTypeClient(newBinding, new EndpointAddress("https://example.com/foo/v1"));
FooRequest req = new FooRequest();
//...etc...now it's just request and response.

Unfortunate, but I'll put this configuration of a custom binding, and hopefully when they fix it, it'll be a configuration change. This at least got us to a point where I can reliably call their web services.

不幸的是,但是我将把这种自定义绑定的配置放到一起,希望当他们修复它时,它将是一个配置更改。 至少这使我们可以可靠地调用他们的Web服务。

Long day, but interesting stuff.

漫长的一天,但有趣的东西。

翻译自: https://www.hanselman.com/blog/breaking-all-the-rules-with-wcf

wcf 调用wcf

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值