使用Framework 4.0编写了一个用C#.NET编写的Visual Studio 2010 WCF Web服务。 我的Web服务调用使用HTTPS,WS-Security和Mtom的Java Web服务,我需要处理结果。
表示Java Web服务不需要<u:Timestamp><u:Created><u:Expires></u:Timestamp>
标题中的部分。
当前问题: 当我测试对listStuff的调用时,对Web服务调用的响应显示了我正在寻找的数据(使用Fiddler)但是我在WCF测试客户端中收到以下错误:
&#34;文本/ XML&#34;响应消息的内容与绑定的内容类型不匹配(text / xml; charset = utf-8)。如果使用自定义编码器,请确保正确实现IsContentTypeSupported方法。
我如何处理我正在调用的Web服务未返回&#34; charset = utf-8&#34;他们的回应?
tried <globalization requestEncoding="utf-8" responseEncoding="utf-8"/>
tried <globalization fileEncoding="utf-8" requestEncoding="utf-8" responseEncoding="utf-8" culture="en-GB" uiCulture="en-GB"/>
tried <textMessageEncoding messageVersion="Soap12"/>
tried <mtomMessageEncoding/>
看起来WS-Security和Mtom正在运作。
用于创建我的网络服务的步骤:
打开Visual Studio 2010。
档案 - &gt;新 - &gt;项目
WCF - &gt;服务申请。 将名称更改为MyWebService。 将位置更改为C:\ MyWebService。 将解决方案名称更改为MyWebService。 单击“确定”按钮。
显示Service1.svc.cs文件。
右键单击公共类名称Service1。 重构 - &gt;改名。 更改名称:EstuffService。 单击确定按钮,然后单击应用按钮。
打开Service1.svc.cs。 添加以下方法:
public String listStuff()
{
return "Called listStuff()";
}
打开IService1.cs并将以下内容添加到公共接口IService1。
[OperationContract]
String listStuff();
现在的测试方法。
选择Service1.svc.cs文件。 调试 - &gt;开始调试。 打开WCF测试客户端窗口。 双击listStuff。 打开listStuff选项卡。 单击“调用”按钮。 显示安全警告。 单击确定按钮。 响应包含“Called listStuff()&#34;在价值领域。
务!现在加入JAVA WEB服务逻辑。
添加服务参考。
解决方案资源管理器 - &gt;右键单击服务参考 - &gt;添加服务参考。
输入地址: https://the.stuff.com/createFiling?wsdl
点击“转到”按钮。
展开createStuffService。
单击CreateMTOMInterface。
输入命名空间:createStuffService。
单击“确定”按钮。
项目 - &gt;创建发布设置。 选择Application选项卡。 目标框架:.NET Framework 4。 显示目标框架更改消息。 单击是按钮。
对默认代码进行了以下更改:
//////////////////////////////////////////////////////////////////////////
// Service1.svc.cs
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using MyWebService.createStuffService;
using System.IdentityModel;
using System.ServiceModel.Description;
using System.ServiceModel.Security;
using System.Security.Cryptography;
using System.IdentityModel.Tokens;
namespace MyWebService
{
public class EstuffService : IService1
{
public String listStuff()
{
CreateMTOMInterfaceClient estuffService = getEstuffService();
try
{
stuff[] stuffs = estuffService.listStuff();
return "listStuff returned " + stuffs.Length.ToString() + " rows.";
}
catch (Exception ex)
{
return ex.Message;
}
// was - return "Called listStuff()";
}
// ADDED the following to use the CustomeCredentials
public CreateMTOMInterfaceClient getEstuffService()
{
CreateMTOMInterfaceClient service = new CreateMTOMInterfaceClient();
service.ChannelFactory.Endpoint.Behaviors.Remove<ClientCredentials>();
service.ChannelFactory.Endpoint.Behaviors.Add(new CustomCredentials());
service.ClientCredentials.UserName.UserName = "[UserName]";
service.ClientCredentials.UserName.Password = "[Password]";
return service;
}
// END ADDED
}
// ADDED
// The following logic comes from
// https://weblog.west-wind.com/posts/2012/nov/24/wcf-wssecurity-and-wse-nonce-authentication
// with a couple of changes as noted. Thanks Rick Strahl.
public class CustomCredentials : ClientCredentials
{
public CustomCredentials()
{ }
protected CustomCredentials(CustomCredentials cc)
: base(cc)
{ }
public override System.IdentityModel.Selectors.SecurityTokenManager CreateSecurityTokenManager()
{
return new CustomSecurityTokenManager(this);
}
protected override ClientCredentials CloneCore()
{
return new CustomCredentials(this);
}
}
public class CustomSecurityTokenManager : ClientCredentialsSecurityTokenManager
{
public CustomSecurityTokenManager(CustomCredentials cred)
: base(cred)
{ }
public override System.IdentityModel.Selectors.SecurityTokenSerializer CreateSecurityTokenSerializer(System.IdentityModel.Selectors.SecurityTokenVersion version)
{
return new CustomTokenSerializer(System.ServiceModel.Security.SecurityVersion.WSSecurity11);
}
}
public class CustomTokenSerializer : WSSecurityTokenSerializer
{
public CustomTokenSerializer(SecurityVersion sv)
: base(sv)
{ }
protected override void WriteTokenCore(System.Xml.XmlWriter writer,
System.IdentityModel.Tokens.SecurityToken token)
{
UserNameSecurityToken userToken = token as UserNameSecurityToken;
string tokennamespace = "o";
DateTime created = DateTime.UtcNow; // was .Now;
string createdStr = created.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); // was "yyyy-MM-ddThh:mm:ss.fffZ"
// unique Nonce value - encode with SHA-1 for 'randomness'
// in theory the nonce could just be the GUID by itself
string phrase = Guid.NewGuid().ToString();
var nonce = GetSHA1String(phrase);
// in this case password is plain text
// for digest mode password needs to be encoded as:
// PasswordAsDigest = Base64(SHA-1(Nonce + Created + Password))
// and profile needs to change to
//string password = GetSHA1String(nonce + createdStr + userToken.Password);
string password = userToken.Password;
writer.WriteRaw(string.Format(
"<{0}:UsernameToken u:Id=\"" + token.Id +
"\" xmlns:u=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" +
"<{0}:Username>" + userToken.UserName + "</{0}:Username>" +
"<{0}:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">" +
password + "</{0}:Password>" +
"<{0}:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">" +
nonce + "</{0}:Nonce>" +
"<u:Created>" + createdStr + "</u:Created></{0}:UsernameToken>", tokennamespace));
}
protected string GetSHA1String(string phrase)
{
SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
byte[] hashedDataBytes = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(phrase));
return Convert.ToBase64String(hashedDataBytes);
}
}
// END ADDED
}
//
// END Service1.svc.cs
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// IService1.cs
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace MyWebService
{
[ServiceContract]
public interface IService1
{
// ADDED
[OperationContract]
String listStuff();
// END ADDED
}
}
//
// END IService1.cs
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Web.config
//
<?xml version="1.0"?>
<configuration>
<appSettings/>
<connectionStrings/>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
<!--
The <authentication> section enables configuration
of the security authentication mode used by
ASP.NET to identify an incoming user.
-->
<authentication mode="Windows"/>
<!--
The <customErrors> section enables configuration
of what to do if/when an unhandled error occurs
during the execution of a request. Specifically,
it enables developers to configure html error pages
to be displayed in place of a error stack trace.
<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>
-->
<pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/>
<!-- ADDED -->
<!--tried <globalization requestEncoding="utf-8" responseEncoding="utf-8"/>-->
<!--tried <globalization fileEncoding="utf-8" requestEncoding="utf-8" responseEncoding="utf-8" culture="en-GB" uiCulture="en-GB"/> -->
<!-- END ADDED -->
</system.web>
<!--
The system.webServer section is required for running ASP.NET AJAX under Internet
Information Services 7.0. It is not necessary for previous version of IIS.
-->
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="createStuffServiceSoapBinding">
<security mode="Transport"/>
</binding>
<binding name="createStuffServiceSoapBinding1"/>
</basicHttpBinding>
<!-- Added -->
<!--tried <textMessageEncoding messageVersion="Soap12"/>-->
<!--tried <mtomMessageEncoding/>-->
<customBinding>
<binding name="CustomSoapBinding">
<security includeTimestamp="false" authenticationMode="UserNameOverTransport" defaultAlgorithmSuite="Basic256" requireDerivedKeys="false" messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"/>
<textMessageEncoding messageVersion="Soap11"/>
<httpsTransport maxReceivedMessageSize="2000000000"/>
</binding>
</customBinding>
<!-- End Added -->
</bindings>
<client>
<!-- Was binding="basicHttpBinding" bindingConfiguration="createStuffServiceSoapBinding"-->
<endpoint address="https://the.stuff.com/createFiling" binding="customBinding" bindingConfiguration="CustomSoapBinding" contract="createStuffService.CreateMTOMInterface" name="createStuffServicePort"/>
</client>
<services>
<service behaviorConfiguration="MyWebService.Service1Behavior" name="MyWebService.EstuffService">
<endpoint address="" binding="wsHttpBinding" contract="MyWebService.IService1">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<!--tried <serviceMetadata httpGetEnabled="true" policyVersion="Policy15"/>-->
<behaviors>
<serviceBehaviors>
<behavior name="MyWebService.Service1Behavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
//
// END Web.config
//////////////////////////////////////////////////////////////////////////
这是我的Fiddler调用结果。
*** REQUEST Captured using Telerik Fiddler Web Debugger ***
POST https://the.stuff.com/services/createFiling HTTP/1.1
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
Host: the.stuff.com
Content-Length: 1279
Expect: 100-continue
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
<s:Envelope
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<VsDebuggerCausalityData
xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">
uIDPo8CYUJmfemFDgV85fb9Z8dwBAAAAHYQCoVVch0SXHY5oNUdGeXF1Nv5vBlNMnDbXQEH7yAoACQAA
</VsDebuggerCausalityData>
<o:Security
s:mustUnderstand="1"
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:UsernameToken
u:Id="uuid-920edd97-b092-4f62-a2f2-0463dd064628-10"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<o:Username>
[UserName]
</o:Username>
<o:Password
Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
[Password]
</o:Password>
<o:Nonce
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
SsN9bPcfZEsAulkbEtN2FksKXWg=
</o:Nonce>
<u:Created>2016-10-12T16:44:30.147Z</u:Created>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<listStuff xmlns="http://efile.the.stuff.com/"/>
</s:Body>
</s:Envelope>
*** END REQUEST Captured using Telerik Fiddler Web Debugger ***
*** RESPONSE Captured using Telerik Fiddler Web Debugger ***
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
XM: ttc1a
Content-Type: multipart/related; type="application/xop+xml";
boundary="uuid:89bea26c-8f7d-4dcf-8809-c5cb9697a1f0"; start="<root.message@cxf.apache.org>"; start-info="text/xml"
Transfer-Encoding: chunked
Date: Wed, 12 Oct 2016 16:44:32 GMT
Set-Cookie: F5-SESSION-PERSIST=889397770.63520.0000; path=/
X-FRAME-OPTIONS: SAMEORIGIN
Content-Length: 53173
--uuid:89bea26c-8f7d-4dcf-8809-c5cb9697a1f0
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml";
Content-Transfer-Encoding: binary
Content-ID: <root.message@cxf.apache.org>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body><ns2:listStuffResponse xmlns:ns2="http://efile.the.stuff.com/">
<stuff><id>1</id><name>Stuff1</name><abbreviation>A001</abbreviation></stuff>
... left out stuff 2 to 536 ...
<stuff><id>537</id><name>Stuff537</name><abbreviation>A537</abbreviation></stuff>
</ns2:listStuffResponse>
</soap:Body>
</soap:Envelope>
--uuid:89bea26c-8f7d-4dcf-8809-c5cb9697a1f0--
*** END RESPONSE Captured using Telerik Fiddler Web Debugger ***
这是我从WCF测试客户端获得的XML结果。
*** REQUEST Captured from WCF Test Client ***
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/IService1/listStuff</a:Action>
<a:MessageID>urn:uuid:8a03d3ad-dd79-4745-b9bb-a55ce293d314</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
</s:Header>
<s:Body>
<listStuff xmlns="http://tempuri.org/" />
</s:Body>
</s:Envelope>
*** END REQUEST Captured from WCF Test Client ***
*** RESPONSE Captured from WCF Test Client ***
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<a:Action s:mustUnderstand="1"
u:Id="_2">http://tempuri.org/IService1/listStuffResponse</a:Action>
<a:RelatesTo u:Id="_3">urn:uuid:26883546-39ea-44e0-83b2-d3cee32ba8b0</a:RelatesTo>
<o:Security s:mustUnderstand="1"
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="uuid-303895ea-d557-4df1-a665-2f94391dd40d-15">
<u:Created>2016-10-12T16:44:32.350Z</u:Created>
<u:Expires>2016-10-12T16:49:32.350Z</u:Expires>
</u:Timestamp>
<c:DerivedKeyToken u:Id="uuid-303895ea-d557-4df1-a665-2f94391dd40d-13"
xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<o:SecurityTokenReference>
<o:Reference URI="urn:uuid:6e0ba079-fc3c-4ae9-9186-e0e3f928a113"
ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct" />
</o:SecurityTokenReference>
<c:Offset>0</c:Offset>
<c:Length>24</c:Length>
<c:Nonce>UvseoObRVJeMp2Dv3t/5tg==</c:Nonce>
</c:DerivedKeyToken>
<c:DerivedKeyToken
u:Id="uuid-303895ea-d557-4df1-a665-2f94391dd40d-14"
xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<o:SecurityTokenReference>
<o:Reference
URI="urn:uuid:6e0ba079-fc3c-4ae9-9186-e0e3f928a113"
ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct" />
</o:SecurityTokenReference>
<c:Nonce>gFfWI8QjzYJH8kh/9dzDgA==</c:Nonce>
</c:DerivedKeyToken>
<e:ReferenceList xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:DataReference URI="#_1" />
<e:DataReference URI="#_4" />
</e:ReferenceList>
<e:EncryptedData Id="_4"
Type="http://www.w3.org/2001/04/xmlenc#Element"
xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<o:SecurityTokenReference>
<o:Reference
ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/dk"
URI="#uuid-303895ea-d557-4df1-a665-2f94391dd40d-14" />
</o:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
<e:CipherValue>... left out the CipherValue ...</e:CipherValue>
</e:CipherData>
</e:EncryptedData>
</o:Security>
</s:Header>
<s:Body u:Id="_0">
<listStuffResponse xmlns="http://tempuri.org/">
<listStuffResult>
The content type multipart/related; type="application/xop+xml";
boundary="uuid:89bea26c-8f7d-4dcf-8809-c5cb9697a1f0"; start="<root.message@cxf.apache.org>";
start-info="text/xml" of the response message does not match the content type of the binding (text/xml; charset=utf-8).
If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 1024 bytes
of the response were: '--uuid:89bea26c-8f7d-4dcf-8809-c5cb9697a1f0
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml";
Content-Transfer-Encoding: binary
Content-ID: <root.message@cxf.apache.org>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:listStuffResponse
xmlns:ns2="http://efile.the.stuff.com/">
<stuff><id>1</id><name>Stuff001</name><abbreviation>X001</abbreviation></stuff>
... left out stuff 2 to 6 ...
<stuff><id>7</id><name>Stuff007</name><abbreviation>X007</abbreviation><'.
</listStuffResult>
</listStuffResponse>
</s:Body>
</s:Envelope>
*** END RESPONSE Captured from WCF Test Client ***