|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
1. WSDL基础概念与重要性
WSDL(Web Services Description Language)是一种基于XML的描述语言,用于描述Web服务的功能和访问方式。作为Web服务体系结构中的核心组件,WSDL扮演着服务提供者和服务消费者之间的”合同”角色,确保不同平台和编程语言之间的互操作性。
WSDL的主要作用包括:
• 描述Web服务的接口和可用操作
• 定义服务请求和响应的消息格式
• 指定服务访问的通信协议和数据格式
• 提供服务访问的端点地址
对于开发者来说,掌握WSDL服务调用技巧是构建分布式应用程序和实现系统集成的重要能力。无论是企业级应用集成、第三方API调用,还是微服务架构中的服务通信,WSDL都扮演着关键角色。
2. WSDL文档结构详解
一个完整的WSDL文档通常包含以下主要元素:
2.1 WSDL基本元素
- <definitions>
- <types>...</types> <!-- 数据类型定义 -->
- <message>...</message> <!-- 消息定义 -->
- <portType>...</portType> <!-- 抽象接口定义 -->
- <binding>...</binding> <!-- 具体协议绑定 -->
- <service>...</service> <!-- 服务端点定义 -->
- </definitions>
复制代码
2.2 元素详解
• types:定义Web服务使用的数据类型,通常使用XML Schema定义。
• message:定义服务请求和响应的消息结构,包含多个part元素。
• portType:描述服务的抽象接口,包含一组操作(operation)。
• binding:定义具体的协议和数据格式规范,将portType绑定到具体协议。
• service:描述服务的端点地址和访问方式,包含一个或多个port元素。
2.3 示例WSDL文档
下面是一个简单的天气预报WSDL服务示例:
- <definitions name="WeatherService"
- targetNamespace="http://www.example.com/weather"
- xmlns="http://schemas.xmlsoap.org/wsdl/"
- xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
- xmlns:xsd="http://www.w3.org/2001/XMLSchema"
- xmlns:tns="http://www.example.com/weather">
- <!-- 类型定义 -->
- <types>
- <xsd:schema targetNamespace="http://www.example.com/weather">
- <xsd:element name="GetWeatherRequest">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="City" type="xsd:string"/>
- <xsd:element name="Date" type="xsd:date"/>
- </xsd:sequence>
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="GetWeatherResponse">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="Temperature" type="xsd:float"/>
- <xsd:element name="Conditions" type="xsd:string"/>
- </xsd:sequence>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- </types>
- <!-- 消息定义 -->
- <message name="GetWeatherRequest">
- <part name="parameters" element="tns:GetWeatherRequest"/>
- </message>
- <message name="GetWeatherResponse">
- <part name="parameters" element="tns:GetWeatherResponse"/>
- </message>
- <!-- 端口类型定义 -->
- <portType name="WeatherPortType">
- <operation name="GetWeather">
- <input message="tns:GetWeatherRequest"/>
- <output message="tns:GetWeatherResponse"/>
- </operation>
- </portType>
- <!-- SOAP绑定 -->
- <binding name="WeatherSoapBinding" type="tns:WeatherPortType">
- <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
- <operation name="GetWeather">
- <soap:operation soapAction="http://www.example.com/weather/GetWeather"/>
- <input>
- <soap:body use="literal"/>
- </input>
- <output>
- <soap:body use="literal"/>
- </output>
- </operation>
- </binding>
- <!-- 服务定义 -->
- <service name="WeatherService">
- <port name="WeatherPort" binding="tns:WeatherSoapBinding">
- <soap:address location="http://www.example.com/weather/weather.asmx"/>
- </port>
- </service>
- </definitions>
复制代码
3. 开发环境准备
在开始调用WSDL服务之前,需要准备适当的开发环境和工具。
3.1 IDE选择
• Java开发:Eclipse、IntelliJ IDEA
• .NET开发:Visual Studio
• Python开发:PyCharm、VS Code
• 通用开发:VS Code(支持多种语言的WSDL插件)
3.2 辅助工具
• SoapUI:用于测试SOAP/Web服务
• Postman:支持测试SOAP请求
• WSDL Viewer:在线WSDL文档查看工具
• XML编辑器:如XMLSpy、Notepad++等
3.3 必要的库和框架
• Java:JAX-WS、Apache CXF、Spring-WS
• Python:suds、zeep
• C#:.NET Framework内置支持
• PHP:SoapClient
4. 解析和理解WSDL文档
4.1 手动解析WSDL
解析WSDL文档时,应关注以下关键信息:
1. 服务端点:在<service>元素中的<soap:address>位置
2. 可用操作:在<portType>中定义的<operation>
3. 消息格式:在<message>和<types>中定义的数据结构
4. 绑定协议:在<binding>中定义的协议和数据格式
4.2 使用工具解析
大多数现代IDE和开发工具都提供了WSDL解析功能:
1. 在项目中右键,选择”Web Services” > “Generate Client from WSDL”
2. 输入WSDL URL或文件路径
3. 选择生成代码的包名和其他选项
4. 完成向导,IDE将自动生成客户端代码
1. 在项目中右键,选择”添加” > “服务引用”
2. 输入WSDL URL或文件路径
3. 设置命名空间
4. 点击”确定”,Visual Studio将自动生成代理类
4.3 WSDL到代码的映射
理解WSDL元素如何映射到编程语言的构造是关键:
• portType→ 接口或抽象类
• operation→ 接口方法
• message→ 方法参数和返回值
• types→ 数据类型或类
5. Java调用WSDL服务实例
5.1 使用JAX-WS调用WSDL服务
JAX-WS(Java API for XML Web Services)是Java标准版中用于处理Web服务的API。
首先,使用wsimport工具从WSDL生成客户端代码:
- wsimport -keep -p com.example.weather.client http://www.example.com/weather/weather.asmx?wsdl
复制代码
这将生成以下文件:
• 服务接口(如WeatherPortType.java)
• 服务类(如WeatherService.java)
• 数据模型类(如GetWeatherRequest.java、GetWeatherResponse.java)
- package com.example.weather.client;
- import javax.xml.ws.WebServiceRef;
- public class WeatherClient {
-
- @WebServiceRef(wsdlLocation = "http://www.example.com/weather/weather.asmx?wsdl")
- private static WeatherService service;
-
- public static void main(String[] args) {
- try {
- // 创建服务实例
- if (service == null) {
- service = new WeatherService();
- }
-
- // 获取服务端口
- WeatherPortType port = service.getWeatherPort();
-
- // 创建请求对象
- GetWeatherRequest request = new GetWeatherRequest();
- request.setCity("北京");
- request.setDate(java.sql.Date.valueOf("2023-06-15"));
-
- // 调用服务
- GetWeatherResponse response = port.getWeather(request);
-
- // 处理响应
- System.out.println("温度: " + response.getTemperature() + "°C");
- System.out.println("天气状况: " + response.getConditions());
-
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
复制代码
某些服务可能需要SOAP头信息,如认证信息:
- import javax.xml.ws.BindingProvider;
- import javax.xml.ws.handler.MessageContext;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- public class WeatherClientWithHeaders {
-
- public static void main(String[] args) {
- try {
- WeatherService service = new WeatherService();
- WeatherPortType port = service.getWeatherPort();
-
- // 获取BindingProvider以设置请求属性
- BindingProvider bp = (BindingProvider) port;
-
- // 添加SOAP头
- Map<String, Object> requestContext = bp.getRequestContext();
- Map<String, List<String>> requestHeaders = new HashMap<>();
- requestHeaders.put("Username", List.of("myuser"));
- requestHeaders.put("Password", List.of("mypassword"));
- requestContext.put(MessageContext.HTTP_REQUEST_HEADERS, requestHeaders);
-
- // 创建请求对象
- GetWeatherRequest request = new GetWeatherRequest();
- request.setCity("北京");
- request.setDate(java.sql.Date.valueOf("2023-06-15"));
-
- // 调用服务
- GetWeatherResponse response = port.getWeather(request);
-
- // 处理响应
- System.out.println("温度: " + response.getTemperature() + "°C");
- System.out.println("天气状况: " + response.getConditions());
-
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
复制代码
5.2 使用Apache CXF调用WSDL服务
Apache CXF是一个流行的开源服务框架,提供了对JAX-WS的完整支持。
- <dependencies>
- <dependency>
- <groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-frontend-jaxws</artifactId>
- <version>3.4.5</version>
- </dependency>
- <dependency>
- <groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-transports-http</artifactId>
- <version>3.4.5</version>
- </dependency>
- </dependencies>
复制代码- package com.example.weather.client;
- import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
- public class WeatherCXFClient {
-
- public static void main(String[] args) {
- String wsdlUrl = "http://www.example.com/weather/weather.asmx?wsdl";
-
- // 创建JaxWsProxyFactoryBean
- JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
- factory.setServiceClass(WeatherPortType.class);
- factory.setAddress(wsdlUrl);
-
- // 创建客户端代理
- WeatherPortType client = (WeatherPortType) factory.create();
-
- // 创建请求对象
- GetWeatherRequest request = new GetWeatherRequest();
- request.setCity("北京");
- request.setDate(java.sql.Date.valueOf("2023-06-15"));
-
- // 调用服务
- try {
- GetWeatherResponse response = client.getWeather(request);
-
- // 处理响应
- System.out.println("温度: " + response.getTemperature() + "°C");
- System.out.println("天气状况: " + response.getConditions());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
复制代码
如果服务需要WS-Security,可以使用CXF的拦截器:
- package com.example.weather.client;
- import org.apache.cxf.endpoint.Client;
- import org.apache.cxf.frontend.ClientProxy;
- import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
- import org.apache.ws.security.WSConstants;
- import org.apache.ws.security.handler.WSHandlerConstants;
- import java.util.HashMap;
- import java.util.Map;
- public class WeatherSecureClient {
-
- public static void main(String[] args) {
- String wsdlUrl = "http://www.example.com/weather/weather.asmx?wsdl";
-
- // 创建JaxWsProxyFactoryBean
- JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
- factory.setServiceClass(WeatherPortType.class);
- factory.setAddress(wsdlUrl);
-
- // 创建客户端代理
- WeatherPortType client = (WeatherPortType) factory.create();
-
- // 配置WS-Security
- Map<String, Object> outProps = new HashMap<>();
- outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
- outProps.put(WSHandlerConstants.USER, "myuser");
- outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
- outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, "com.example.weather.client.ClientPasswordCallback");
-
- // 添加WSS4J拦截器
- Client cxfClient = ClientProxy.getClient(client);
- cxfClient.getOutInterceptors().add(new WSS4JOutInterceptor(outProps));
-
- // 创建请求对象
- GetWeatherRequest request = new GetWeatherRequest();
- request.setCity("北京");
- request.setDate(java.sql.Date.valueOf("2023-06-15"));
-
- // 调用服务
- try {
- GetWeatherResponse response = client.getWeather(request);
-
- // 处理响应
- System.out.println("温度: " + response.getTemperature() + "°C");
- System.out.println("天气状况: " + response.getConditions());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
复制代码
5.3 使用Spring Web Services调用WSDL服务
Spring Web Services是Spring框架的一个模块,专注于创建文档驱动的Web服务。
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web-services</artifactId>
- <version>2.7.0</version>
- </dependency>
- <dependency>
- <groupId>wsdl4j</groupId>
- <artifactId>wsdl4j</artifactId>
- <version>1.6.3</version>
- </dependency>
- </dependencies>
复制代码- package com.example.weather.config;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.oxm.jaxb.Jaxb2Marshaller;
- import org.springframework.ws.client.core.WebServiceTemplate;
- import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
- @Configuration
- public class WeatherServiceConfig {
-
- @Bean
- public Jaxb2Marshaller marshaller() {
- Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
- marshaller.setContextPath("com.example.weather.client");
- return marshaller;
- }
-
- @Bean
- public WebServiceTemplate webServiceTemplate(Jaxb2Marshaller marshaller) {
- WebServiceTemplate template = new WebServiceTemplate();
- template.setDefaultUri("http://www.example.com/weather/weather.asmx");
- template.setMarshaller(marshaller);
- template.setUnmarshaller(marshaller);
- return template;
- }
- }
复制代码- package com.example.weather.client;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
- import org.springframework.ws.client.core.WebServiceTemplate;
- @Component
- public class WeatherSpringClient {
-
- @Autowired
- private WebServiceTemplate webServiceTemplate;
-
- public void getWeather(String city, java.util.Date date) {
- GetWeatherRequest request = new GetWeatherRequest();
- request.setCity(city);
- request.setDate(new java.sql.Date(date.getTime()));
-
- try {
- GetWeatherResponse response = (GetWeatherResponse) webServiceTemplate.marshalSendAndReceive(request);
-
- // 处理响应
- System.out.println("温度: " + response.getTemperature() + "°C");
- System.out.println("天气状况: " + response.getConditions());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
复制代码- package com.example.weather;
- import com.example.weather.client.WeatherSpringClient;
- import org.springframework.boot.CommandLineRunner;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.context.annotation.Bean;
- import java.util.Date;
- @SpringBootApplication
- public class WeatherApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(WeatherApplication.class, args);
- }
-
- @Bean
- public CommandLineRunner run(WeatherSpringClient client) {
- return args -> {
- client.getWeather("北京", new Date());
- };
- }
- }
复制代码
6. Python调用WSDL服务实例
6.1 使用Zeep库调用WSDL服务
Zeep是一个现代的SOAP客户端库,提供了简洁的API来调用WSDL服务。
- from zeep import Client
- from datetime import date
- # 创建客户端
- wsdl_url = 'http://www.example.com/weather/weather.asmx?wsdl'
- client = Client(wsdl_url)
- # 创建请求
- request_data = {
- 'City': '北京',
- 'Date': date(2023, 6, 15)
- }
- # 调用服务
- try:
- response = client.service.GetWeather(**request_data)
-
- # 处理响应
- print(f"温度: {response.Temperature}°C")
- print(f"天气状况: {response.Conditions}")
-
- except Exception as e:
- print(f"调用服务时出错: {str(e)}")
复制代码- from zeep import Client
- from zeep.wsse.username import UsernameToken
- from datetime import date
- # 创建客户端并添加安全令牌
- wsdl_url = 'http://www.example.com/weather/weather.asmx?wsdl'
- client = Client(
- wsdl_url,
- wsse=UsernameToken('myuser', 'mypassword')
- )
- # 创建请求
- request_data = {
- 'City': '北京',
- 'Date': date(2023, 6, 15)
- }
- # 调用服务
- try:
- response = client.service.GetWeather(**request_data)
-
- # 处理响应
- print(f"温度: {response.Temperature}°C")
- print(f"天气状况: {response.Conditions}")
-
- except Exception as e:
- print(f"调用服务时出错: {str(e)}")
复制代码- from zeep import Client
- from zeep import xsd
- from datetime import date
- # 创建客户端
- wsdl_url = 'http://www.example.com/weather/weather.asmx?wsdl'
- client = Client(wsdl_url)
- # 创建自定义头
- header = xsd.Element(
- 'AuthHeader',
- xsd.ComplexType([
- xsd.Element('Username', xsd.String()),
- xsd.Element('Password', xsd.String())
- ])
- )
- header_value = header(Username='myuser', Password='mypassword')
- # 设置头
- client.set_default_soapheaders([header_value])
- # 创建请求
- request_data = {
- 'City': '北京',
- 'Date': date(2023, 6, 15)
- }
- # 调用服务
- try:
- response = client.service.GetWeather(**request_data)
-
- # 处理响应
- print(f"温度: {response.Temperature}°C")
- print(f"天气状况: {response.Conditions}")
-
- except Exception as e:
- print(f"调用服务时出错: {str(e)}")
复制代码
6.2 使用Suds库调用WSDL服务
Suds是一个较老的SOAP客户端库,但仍在许多项目中使用。
- from suds.client import Client
- from datetime import date
- # 创建客户端
- wsdl_url = 'http://www.example.com/weather/weather.asmx?wsdl'
- client = Client(wsdl_url)
- # 创建请求
- request = client.factory.create('GetWeatherRequest')
- request.City = '北京'
- request.Date = date(2023, 6, 15)
- # 调用服务
- try:
- response = client.service.GetWeather(request)
-
- # 处理响应
- print(f"温度: {response.Temperature}°C")
- print(f"天气状况: {response.Conditions}")
-
- except Exception as e:
- print(f"调用服务时出错: {str(e)}")
复制代码- from suds.client import Client
- from suds.sax.element import Element
- from datetime import date
- # 创建客户端
- wsdl_url = 'http://www.example.com/weather/weather.asmx?wsdl'
- client = Client(wsdl_url)
- # 创建SOAP头
- auth_header = Element('AuthHeader')
- username = Element('Username').setText('myuser')
- password = Element('Password').setText('mypassword')
- auth_header.append(username)
- auth_header.append(password)
- client.set_options(soapheaders=auth_header)
- # 创建请求
- request = client.factory.create('GetWeatherRequest')
- request.City = '北京'
- request.Date = date(2023, 6, 15)
- # 调用服务
- try:
- response = client.service.GetWeather(request)
-
- # 处理响应
- print(f"温度: {response.Temperature}°C")
- print(f"天气状况: {response.Conditions}")
-
- except Exception as e:
- print(f"调用服务时出错: {str(e)}")
复制代码
7. C#调用WSDL服务实例
7.1 使用Visual Studio添加服务引用
1. 在Visual Studio中创建一个新的控制台应用程序项目
2. 在解决方案资源管理器中,右键单击项目,选择”添加” > “服务引用”
3. 在地址栏中输入WSDL URL:http://www.example.com/weather/weather.asmx?wsdl
4. 点击”转到”按钮,Visual Studio将分析WSDL并显示可用的服务
5. 设置命名空间(例如:WeatherServiceReference)
6. 点击”确定”按钮,Visual Studio将生成代理类
- using System;
- using System.ServiceModel;
- using WeatherClient.WeatherServiceReference;
- namespace WeatherClient
- {
- class Program
- {
- static void Main(string[] args)
- {
- try
- {
- // 创建客户端
- var client = new WeatherServiceClient();
-
- // 创建请求
- var request = new GetWeatherRequest
- {
- City = "北京",
- Date = DateTime.Parse("2023-06-15")
- };
-
- // 调用服务
- var response = client.GetWeather(request);
-
- // 处理响应
- Console.WriteLine($"温度: {response.Temperature}°C");
- Console.WriteLine($"天气状况: {response.Conditions}");
-
- // 关闭客户端
- client.Close();
- }
- catch (FaultException ex)
- {
- Console.WriteLine($"服务错误: {ex.Message}");
- }
- catch (CommunicationException ex)
- {
- Console.WriteLine($"通信错误: {ex.Message}");
- }
- catch (Exception ex)
- {
- Console.WriteLine($"错误: {ex.Message}");
- }
- }
- }
- }
复制代码- using System;
- using System.ServiceModel;
- using System.ServiceModel.Channels;
- using WeatherClient.WeatherServiceReference;
- namespace WeatherClient
- {
- class Program
- {
- static void Main(string[] args)
- {
- try
- {
- // 创建客户端
- var client = new WeatherServiceClient();
-
- // 添加自定义SOAP头
- using (var scope = new OperationContextScope(client.InnerChannel))
- {
- // 创建SOAP头
- var authHeader = MessageHeader.CreateHeader(
- "AuthHeader",
- "http://www.example.com/weather",
- new { Username = "myuser", Password = "mypassword" }
- );
-
- // 添加头到消息
- OperationContext.Current.OutgoingMessageHeaders.Add(authHeader);
-
- // 创建请求
- var request = new GetWeatherRequest
- {
- City = "北京",
- Date = DateTime.Parse("2023-06-15")
- };
-
- // 调用服务
- var response = client.GetWeather(request);
-
- // 处理响应
- Console.WriteLine($"温度: {response.Temperature}°C");
- Console.WriteLine($"天气状况: {response.Conditions}");
- }
-
- // 关闭客户端
- client.Close();
- }
- catch (FaultException ex)
- {
- Console.WriteLine($"服务错误: {ex.Message}");
- }
- catch (CommunicationException ex)
- {
- Console.WriteLine($"通信错误: {ex.Message}");
- }
- catch (Exception ex)
- {
- Console.WriteLine($"错误: {ex.Message}");
- }
- }
- }
- }
复制代码
7.2 使用ChannelFactory调用WSDL服务
ChannelFactory提供了一种更灵活的方式来创建客户端代理,特别适用于需要更多控制的情况。
首先,需要手动创建服务接口,而不是使用Visual Studio生成的代理类:
- using System;
- using System.ServiceModel;
- using System.Runtime.Serialization;
- namespace WeatherClient.ServiceContracts
- {
- [ServiceContract(Namespace = "http://www.example.com/weather")]
- public interface IWeatherService
- {
- [OperationContract(Action = "http://www.example.com/weather/GetWeather")]
- GetWeatherResponse GetWeather(GetWeatherRequest request);
- }
- [DataContract(Namespace = "http://www.example.com/weather")]
- public class GetWeatherRequest
- {
- [DataMember]
- public string City { get; set; }
-
- [DataMember]
- public DateTime Date { get; set; }
- }
- [DataContract(Namespace = "http://www.example.com/weather")]
- public class GetWeatherResponse
- {
- [DataMember]
- public float Temperature { get; set; }
-
- [DataMember]
- public string Conditions { get; set; }
- }
- }
复制代码- using System;
- using System.ServiceModel;
- using System.ServiceModel.Channels;
- using WeatherClient.ServiceContracts;
- namespace WeatherClient
- {
- class Program
- {
- static void Main(string[] args)
- {
- try
- {
- // 创建绑定
- var binding = new BasicHttpBinding();
-
- // 创建端点地址
- var endpointAddress = new EndpointAddress("http://www.example.com/weather/weather.asmx");
-
- // 创建通道工厂
- var factory = new ChannelFactory<IWeatherService>(binding, endpointAddress);
-
- // 创建通道
- var channel = factory.CreateChannel();
-
- // 创建请求
- var request = new GetWeatherRequest
- {
- City = "北京",
- Date = DateTime.Parse("2023-06-15")
- };
-
- // 调用服务
- var response = channel.GetWeather(request);
-
- // 处理响应
- Console.WriteLine($"温度: {response.Temperature}°C");
- Console.WriteLine($"天气状况: {response.Conditions}");
-
- // 关闭工厂
- factory.Close();
- }
- catch (FaultException ex)
- {
- Console.WriteLine($"服务错误: {ex.Message}");
- }
- catch (CommunicationException ex)
- {
- Console.WriteLine($"通信错误: {ex.Message}");
- }
- catch (Exception ex)
- {
- Console.WriteLine($"错误: {ex.Message}");
- }
- }
- }
- }
复制代码- using System;
- using System.ServiceModel;
- using System.ServiceModel.Channels;
- using System.ServiceModel.Description;
- using System.ServiceModel.Dispatcher;
- using WeatherClient.ServiceContracts;
- namespace WeatherClient
- {
- // 自定义消息检查器
- public class AuthHeaderMessageInspector : IClientMessageInspector
- {
- public void AfterReceiveReply(ref Message reply, object correlationState)
- {
- // 不需要实现
- }
- public object BeforeSendRequest(ref Message request, IClientChannel channel)
- {
- // 创建SOAP头
- var authHeader = MessageHeader.CreateHeader(
- "AuthHeader",
- "http://www.example.com/weather",
- new { Username = "myuser", Password = "mypassword" }
- );
-
- // 添加头到消息
- request.Headers.Add(authHeader);
-
- return null;
- }
- }
- // 自定义端点行为
- public class AuthHeaderEndpointBehavior : IEndpointBehavior
- {
- public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
- {
- // 不需要实现
- }
- public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
- {
- clientRuntime.MessageInspectors.Add(new AuthHeaderMessageInspector());
- }
- public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
- {
- // 不需要实现
- }
- public void Validate(ServiceEndpoint endpoint)
- {
- // 不需要实现
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- try
- {
- // 创建绑定
- var binding = new BasicHttpBinding();
-
- // 创建端点地址
- var endpointAddress = new EndpointAddress("http://www.example.com/weather/weather.asmx");
-
- // 创建通道工厂
- var factory = new ChannelFactory<IWeatherService>(binding, endpointAddress);
-
- // 添加自定义行为
- factory.Endpoint.EndpointBehaviors.Add(new AuthHeaderEndpointBehavior());
-
- // 创建通道
- var channel = factory.CreateChannel();
-
- // 创建请求
- var request = new GetWeatherRequest
- {
- City = "北京",
- Date = DateTime.Parse("2023-06-15")
- };
-
- // 调用服务
- var response = channel.GetWeather(request);
-
- // 处理响应
- Console.WriteLine($"温度: {response.Temperature}°C");
- Console.WriteLine($"天气状况: {response.Conditions}");
-
- // 关闭工厂
- factory.Close();
- }
- catch (FaultException ex)
- {
- Console.WriteLine($"服务错误: {ex.Message}");
- }
- catch (CommunicationException ex)
- {
- Console.WriteLine($"通信错误: {ex.Message}");
- }
- catch (Exception ex)
- {
- Console.WriteLine($"错误: {ex.Message}");
- }
- }
- }
- }
复制代码
8. 常见问题与解决方案
8.1 连接问题
问题:调用WSDL服务时,经常遇到连接超时错误。
解决方案:
- // Java (JAX-WS)
- import javax.xml.ws.BindingProvider;
- import java.util.Map;
- import java.util.concurrent.TimeUnit;
- // 获取BindingProvider
- BindingProvider bp = (BindingProvider) port;
- Map<String, Object> requestContext = bp.getRequestContext();
- // 设置连接超时(单位:毫秒)
- requestContext.put("javax.xml.ws.client.connectionTimeout", TimeUnit.SECONDS.toMillis(30));
- // 设置请求超时(单位:毫秒)
- requestContext.put("javax.xml.ws.client.receiveTimeout", TimeUnit.SECONDS.toMillis(60));
复制代码- # Python (Zeep)
- from zeep import Client
- from zeep.transports import Transport
- from requests import Session
- # 创建会话并设置超时
- session = Session()
- session.timeout = 30 # 30秒超时
- # 创建客户端
- transport = Transport(session=session)
- client = Client(wsdl_url, transport=transport)
复制代码- // C#
- using System.ServiceModel;
- // 创建绑定并设置超时
- var binding = new BasicHttpBinding();
- binding.SendTimeout = TimeSpan.FromSeconds(60);
- binding.ReceiveTimeout = TimeSpan.FromSeconds(60);
- binding.OpenTimeout = TimeSpan.FromSeconds(30);
- // 创建客户端
- var client = new WeatherServiceClient(binding, endpointAddress);
复制代码
问题:在企业环境中,需要通过代理服务器访问外部WSDL服务。
解决方案:
- // Java
- import java.net.InetSocketAddress;
- import java.net.Proxy;
- // 设置代理
- System.setProperty("http.proxyHost", "proxy.example.com");
- System.setProperty("http.proxyPort", "8080");
- System.setProperty("https.proxyHost", "proxy.example.com");
- System.setProperty("https.proxyPort", "8080");
- // 如果需要认证
- System.setProperty("http.proxyUser", "username");
- System.setProperty("http.proxyPassword", "password");
复制代码- # Python (Zeep)
- from zeep import Client
- from zeep.transports import Transport
- from requests import Session
- # 创建会话并设置代理
- session = Session()
- session.proxies = {
- 'http': 'http://username:password@proxy.example.com:8080',
- 'https': 'http://username:password@proxy.example.com:8080'
- }
- # 创建客户端
- transport = Transport(session=session)
- client = Client(wsdl_url, transport=transport)
复制代码- // C#
- using System.ServiceModel;
- using System.ServiceModel.Channels;
- // 创建绑定
- var binding = new BasicHttpBinding();
- // 创建代理
- var proxyUri = new Uri("http://proxy.example.com:8080");
- binding.ProxyAddress = proxyUri;
- binding.UseDefaultWebProxy = false;
- binding.BypassProxyOnLocal = false;
- // 如果需要认证
- binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic;
- // 创建客户端
- var client = new WeatherServiceClient(binding, endpointAddress);
复制代码
8.2 认证和安全问题
问题:服务需要HTTP基本认证。
解决方案:
- // Java
- import javax.xml.ws.BindingProvider;
- import java.util.Base64;
- import java.util.Map;
- // 获取BindingProvider
- BindingProvider bp = (BindingProvider) port;
- Map<String, Object> requestContext = bp.getRequestContext();
- // 创建认证头
- String auth = "username:password";
- String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
- requestContext.put(MessageContext.HTTP_REQUEST_HEADERS,
- Map.of("Authorization", List.of("Basic " + encodedAuth)));
复制代码- # Python (Zeep)
- from zeep import Client
- from zeep.transports import Transport
- from requests.auth import HTTPBasicAuth
- # 创建会话并设置认证
- session = Session()
- session.auth = HTTPBasicAuth('username', 'password')
- # 创建客户端
- transport = Transport(session=session)
- client = Client(wsdl_url, transport=transport)
复制代码- // C#
- using System.ServiceModel;
- using System.ServiceModel.Description;
- // 创建绑定
- var binding = new BasicHttpBinding();
- binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
- binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
- // 创建客户端
- var client = new WeatherServiceClient(binding, endpointAddress);
- // 设置凭据
- client.ClientCredentials.UserName.UserName = "username";
- client.ClientCredentials.UserName.Password = "password";
复制代码
问题:服务需要WS-Security安全头。
解决方案:
- // Java (Apache CXF)
- import org.apache.cxf.endpoint.Client;
- import org.apache.cxf.frontend.ClientProxy;
- import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
- import org.apache.ws.security.WSConstants;
- import org.apache.ws.security.handler.WSHandlerConstants;
- import java.util.HashMap;
- import java.util.Map;
- // 获取CXF客户端
- Client cxfClient = ClientProxy.getClient(port);
- // 配置WS-Security
- Map<String, Object> outProps = new HashMap<>();
- outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
- outProps.put(WSHandlerConstants.USER, "username");
- outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
- outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, "com.example.ClientPasswordCallback");
- // 添加拦截器
- cxfClient.getOutInterceptors().add(new WSS4JOutInterceptor(outProps));
复制代码- # Python (Zeep)
- from zeep import Client
- from zeep.wsse.username import UsernameToken
- # 创建客户端并添加安全令牌
- client = Client(
- wsdl_url,
- wsse=UsernameToken('username', 'password')
- )
复制代码- // C#
- using System.ServiceModel;
- using System.ServiceModel.Description;
- using System.ServiceModel.Security;
- // 创建绑定
- var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
- binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
- // 创建客户端
- var client = new WeatherServiceClient(binding, endpointAddress);
- // 设置凭据
- client.ClientCredentials.UserName.UserName = "username";
- client.ClientCredentials.UserName.Password = "password";
复制代码
8.3 数据类型转换问题
问题:服务期望特定的日期时间格式,但默认格式不匹配。
解决方案:
- // Java
- import javax.xml.datatype.DatatypeFactory;
- import javax.xml.datatype.XMLGregorianCalendar;
- import java.util.GregorianCalendar;
- // 创建XMLGregorianCalendar
- GregorianCalendar gregorianCalendar = new GregorianCalendar();
- gregorianCalendar.setTime(yourDate);
- XMLGregorianCalendar xmlGregorianCalendar =
- DatatypeFactory.newInstance().newXMLGregorianCalendar(gregorianCalendar);
- // 在请求中使用
- request.setDate(xmlGregorianCalendar);
复制代码- # Python (Zeep)
- from datetime import datetime
- from zeep import xsd
- # 创建日期时间对象
- date_value = datetime(2023, 6, 15)
- # 转换为XML日期格式
- xsd_date = xsd.Date(date_value)
- # 在请求中使用
- request_data = {
- 'Date': xsd_date
- }
复制代码- // C#
- using System;
- using System.Xml.Serialization;
- // 确保日期格式正确
- var date = DateTime.Parse("2023-06-15");
- var request = new GetWeatherRequest
- {
- Date = date
- };
- // 或者指定格式
- var date = DateTime.ParseExact("2023-06-15", "yyyy-MM-dd", null);
复制代码
问题:服务使用枚举类型,但客户端不知道如何正确映射。
解决方案:
- // Java
- // 生成的客户端代码通常包含枚举类型
- public enum WeatherConditions {
- SUNNY, CLOUDY, RAINY, SNOWY
- }
- // 使用枚举
- request.setConditions(WeatherConditions.SUNNY);
复制代码- # Python (Zeep)
- # Zeep会自动创建枚举类型
- try:
- # 获取枚举类型
- conditions_type = client.get_type('ns0:WeatherConditions')
-
- # 使用枚举
- request_data = {
- 'Conditions': conditions_type.SUNNY
- }
-
- response = client.service.GetWeather(**request_data)
- except Exception as e:
- print(f"错误: {str(e)}")
复制代码- // C#
- // 生成的客户端代码通常包含枚举类型
- public enum WeatherConditions
- {
- SUNNY, CLOUDY, RAINY, SNOWY
- }
- // 使用枚举
- var request = new GetWeatherRequest
- {
- Conditions = WeatherConditions.SUNNY
- };
复制代码
8.4 错误处理和异常管理
问题:服务返回SOAP错误,需要正确处理。
解决方案:
- // Java
- import javax.xml.ws.soap.SOAPFaultException;
- try {
- // 调用服务
- GetWeatherResponse response = port.getWeather(request);
- } catch (SOAPFaultException e) {
- // 获取SOAP错误详情
- System.out.println("SOAP错误代码: " + e.getFault().getFaultCode());
- System.out.println("SOAP错误信息: " + e.getFault().getFaultString());
-
- // 获取详细错误信息
- if (e.getFault().getDetail() != null) {
- System.out.println("详细错误: " + e.getFault().getDetail().getTextContent());
- }
- } catch (Exception e) {
- // 处理其他异常
- e.printStackTrace();
- }
复制代码- # Python (Zeep)
- from zeep.exceptions import Fault
- try:
- # 调用服务
- response = client.service.GetWeather(**request_data)
- except Fault as e:
- # 获取SOAP错误详情
- print(f"SOAP错误: {e.message}")
- print(f"错误代码: {e.code}")
- print(f"错误actor: {e.actor}")
- except Exception as e:
- # 处理其他异常
- print(f"错误: {str(e)}")
复制代码- // C#
- using System.ServiceModel;
- try
- {
- // 调用服务
- var response = client.GetWeather(request);
- }
- catch (FaultException ex)
- {
- // 获取SOAP错误详情
- Console.WriteLine($"SOAP错误代码: {ex.Code}");
- Console.WriteLine($"SOAP错误信息: {ex.Message}");
-
- // 如果有详细错误信息
- if (ex.Detail != null)
- {
- Console.WriteLine($"详细错误: {ex.Detail}");
- }
- }
- catch (CommunicationException ex)
- {
- // 处理通信异常
- Console.WriteLine($"通信错误: {ex.Message}");
- }
- catch (Exception ex)
- {
- // 处理其他异常
- Console.WriteLine($"错误: {ex.Message}");
- }
复制代码
问题:需要记录SOAP请求和响应以便调试。
解决方案:
- // Java (Apache CXF)
- import org.apache.cxf.interceptor.LoggingInInterceptor;
- import org.apache.cxf.interceptor.LoggingOutInterceptor;
- import org.apache.cxf.endpoint.Client;
- import org.apache.cxf.frontend.ClientProxy;
- // 获取CXF客户端
- Client cxfClient = ClientProxy.getClient(port);
- // 添加日志拦截器
- cxfClient.getInInterceptors().add(new LoggingInInterceptor());
- cxfClient.getOutInterceptors().add(new LoggingOutInterceptor());
复制代码- # Python (Zeep)
- import logging
- from zeep import Client
- from zeep.transports import Transport
- from requests import Session
- # 配置日志
- logging.basicConfig(level=logging.DEBUG)
- logging.getLogger('zeep.transports').setLevel(logging.DEBUG)
- # 创建客户端
- session = Session()
- transport = Transport(session=session)
- client = Client(wsdl_url, transport=transport)
复制代码- // C#
- using System.ServiceModel;
- using System.ServiceModel.Description;
- using System.Diagnostics;
- // 创建客户端
- var client = new WeatherServiceClient();
- // 启用消息日志
- client.Endpoint.EndpointBehaviors.Add(new MessageLoggingBehavior());
- // 自定义消息日志行为
- public class MessageLoggingBehavior : IEndpointBehavior
- {
- public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
- {
- }
- public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
- {
- clientRuntime.MessageInspectors.Add(new MessageLogger());
- }
- public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
- {
- }
- public void Validate(ServiceEndpoint endpoint)
- {
- }
- }
- public class MessageLogger : IClientMessageInspector
- {
- public object BeforeSendRequest(ref Message request, IClientChannel channel)
- {
- Debug.WriteLine("请求消息:");
- Debug.WriteLine(request.ToString());
- return null;
- }
- public void AfterReceiveReply(ref Message reply, object correlationState)
- {
- Debug.WriteLine("响应消息:");
- Debug.WriteLine(reply.ToString());
- }
- }
复制代码
9. 最佳实践与性能优化
9.1 代码组织和架构设计
- // Java
- public class WeatherServiceClientFactory {
-
- private static final String WSDL_URL = "http://www.example.com/weather/weather.asmx?wsdl";
-
- public static WeatherPortType createClient() {
- WeatherService service = new WeatherService();
- return service.getWeatherPort();
- }
-
- public static WeatherPortType createClientWithTimeout(long connectionTimeout, long receiveTimeout) {
- WeatherPortType port = createClient();
-
- BindingProvider bp = (BindingProvider) port;
- Map<String, Object> requestContext = bp.getRequestContext();
- requestContext.put("javax.xml.ws.client.connectionTimeout", connectionTimeout);
- requestContext.put("javax.xml.ws.client.receiveTimeout", receiveTimeout);
-
- return port;
- }
-
- public static WeatherPortType createSecureClient(String username, String password) {
- WeatherPortType port = createClient();
-
- // 添加认证逻辑
- BindingProvider bp = (BindingProvider) port;
- Map<String, Object> requestContext = bp.getRequestContext();
-
- String auth = username + ":" + password;
- String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
- requestContext.put(MessageContext.HTTP_REQUEST_HEADERS,
- Map.of("Authorization", List.of("Basic " + encodedAuth)));
-
- return port;
- }
- }
复制代码- # Python
- class WeatherServiceClientFactory:
-
- WSDL_URL = 'http://www.example.com/weather/weather.asmx?wsdl'
-
- @staticmethod
- def create_client():
- return Client(WeatherServiceClientFactory.WSDL_URL)
-
- @staticmethod
- def create_client_with_timeout(timeout=30):
- session = Session()
- session.timeout = timeout
- transport = Transport(session=session)
- return Client(WeatherServiceClientFactory.WSDL_URL, transport=transport)
-
- @staticmethod
- def create_secure_client(username, password):
- return Client(
- WeatherServiceClientFactory.WSDL_URL,
- wsse=UsernameToken(username, password)
- )
复制代码- // C#
- public static class WeatherServiceClientFactory
- {
- private const string WsdlUrl = "http://www.example.com/weather/weather.asmx?wsdl";
-
- public static WeatherServiceClient CreateClient()
- {
- return new WeatherServiceClient();
- }
-
- public static WeatherServiceClient CreateClientWithTimeout(TimeSpan timeout)
- {
- var binding = new BasicHttpBinding();
- binding.SendTimeout = timeout;
- binding.ReceiveTimeout = timeout;
- binding.OpenTimeout = timeout;
-
- var endpointAddress = new EndpointAddress(WsdlUrl);
- return new WeatherServiceClient(binding, endpointAddress);
- }
-
- public static WeatherServiceClient CreateSecureClient(string username, string password)
- {
- var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
- binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
-
- var endpointAddress = new EndpointAddress(WsdlUrl);
- var client = new WeatherServiceClient(binding, endpointAddress);
-
- client.ClientCredentials.UserName.UserName = username;
- client.ClientCredentials.UserName.Password = password;
-
- return client;
- }
- }
复制代码- // Java
- public class WeatherServiceWrapper {
-
- private final WeatherPortType port;
-
- public WeatherServiceWrapper(WeatherPortType port) {
- this.port = port;
- }
-
- public WeatherInfo getWeatherInfo(String city, Date date) {
- try {
- // 创建请求
- GetWeatherRequest request = new GetWeatherRequest();
- request.setCity(city);
- request.setDate(date);
-
- // 调用服务
- GetWeatherResponse response = port.getWeather(request);
-
- // 转换为业务对象
- WeatherInfo info = new WeatherInfo();
- info.setCity(city);
- info.setDate(date);
- info.setTemperature(response.getTemperature());
- info.setConditions(response.getConditions());
-
- return info;
- } catch (Exception e) {
- throw new WeatherServiceException("获取天气信息失败", e);
- }
- }
- }
- // 业务对象
- public class WeatherInfo {
- private String city;
- private Date date;
- private float temperature;
- private String conditions;
-
- // getters and setters
- }
- // 自定义异常
- public class WeatherServiceException extends RuntimeException {
- public WeatherServiceException(String message, Throwable cause) {
- super(message, cause);
- }
- }
复制代码- # Python
- class WeatherServiceWrapper:
-
- def __init__(self, client):
- self.client = client
-
- def get_weather_info(self, city, date):
- try:
- # 创建请求
- request_data = {
- 'City': city,
- 'Date': date
- }
-
- # 调用服务
- response = self.client.service.GetWeather(**request_data)
-
- # 转换为业务对象
- info = WeatherInfo(
- city=city,
- date=date,
- temperature=response.Temperature,
- conditions=response.Conditions
- )
-
- return info
- except Exception as e:
- raise WeatherServiceException("获取天气信息失败", e)
- # 业务对象
- class WeatherInfo:
- def __init__(self, city, date, temperature, conditions):
- self.city = city
- self.date = date
- self.temperature = temperature
- self.conditions = conditions
- # 自定义异常
- class WeatherServiceException(Exception):
- pass
复制代码- // C#
- public class WeatherServiceWrapper : IDisposable
- {
- private readonly WeatherServiceClient _client;
-
- public WeatherServiceWrapper(WeatherServiceClient client)
- {
- _client = client;
- }
-
- public WeatherInfo GetWeatherInfo(string city, DateTime date)
- {
- try
- {
- // 创建请求
- var request = new GetWeatherRequest
- {
- City = city,
- Date = date
- };
-
- // 调用服务
- var response = _client.GetWeather(request);
-
- // 转换为业务对象
- var info = new WeatherInfo
- {
- City = city,
- Date = date,
- Temperature = response.Temperature,
- Conditions = response.Conditions
- };
-
- return info;
- }
- catch (Exception ex)
- {
- throw new WeatherServiceException("获取天气信息失败", ex);
- }
- }
-
- public void Dispose()
- {
- _client?.Dispose();
- }
- }
- // 业务对象
- public class WeatherInfo
- {
- public string City { get; set; }
- public DateTime Date { get; set; }
- public float Temperature { get; set; }
- public string Conditions { get; set; }
- }
- // 自定义异常
- public class WeatherServiceException : Exception
- {
- public WeatherServiceException(string message, Exception innerException)
- : base(message, innerException)
- {
- }
- }
复制代码
9.2 缓存策略
- // Java
- import java.util.HashMap;
- import java.util.Map;
- import java.util.concurrent.TimeUnit;
- public class WeatherServiceCache {
-
- private final WeatherServiceWrapper service;
- private final Map<String, CacheEntry> cache = new HashMap<>();
- private final long cacheDurationMillis;
-
- public WeatherServiceCache(WeatherServiceWrapper service, long cacheDuration, TimeUnit timeUnit) {
- this.service = service;
- this.cacheDurationMillis = timeUnit.toMillis(cacheDuration);
- }
-
- public WeatherInfo getWeatherInfo(String city, Date date) {
- String key = city + "_" + date.toString();
-
- // 检查缓存
- CacheEntry entry = cache.get(key);
- if (entry != null && !entry.isExpired()) {
- return entry.getData();
- }
-
- // 调用服务
- WeatherInfo info = service.getWeatherInfo(city, date);
-
- // 更新缓存
- cache.put(key, new CacheEntry(info, System.currentTimeMillis()));
-
- return info;
- }
-
- private static class CacheEntry {
- private final WeatherInfo data;
- private final long timestamp;
-
- public CacheEntry(WeatherInfo data, long timestamp) {
- this.data = data;
- this.timestamp = timestamp;
- }
-
- public WeatherInfo getData() {
- return data;
- }
-
- public boolean isExpired() {
- return System.currentTimeMillis() - timestamp > cacheDurationMillis;
- }
- }
- }
复制代码- # Python
- import time
- from threading import Lock
- class WeatherServiceCache:
-
- def __init__(self, service, cache_duration_seconds):
- self.service = service
- self.cache_duration = cache_duration_seconds
- self.cache = {}
- self.lock = Lock()
-
- def get_weather_info(self, city, date):
- key = f"{city}_{date}"
-
- with self.lock:
- # 检查缓存
- entry = self.cache.get(key)
- if entry and not entry.is_expired():
- return entry.get_data()
-
- # 调用服务
- info = self.service.get_weather_info(city, date)
-
- with self.lock:
- # 更新缓存
- self.cache[key] = CacheEntry(info, time.time())
-
- return info
- class CacheEntry:
-
- def __init__(self, data, timestamp):
- self.data = data
- self.timestamp = timestamp
-
- def get_data(self):
- return self.data
-
- def is_expired(self, cache_duration):
- return time.time() - self.timestamp > cache_duration
复制代码- // C#
- using System;
- using System.Collections.Concurrent;
- public class WeatherServiceCache
- {
- private readonly WeatherServiceWrapper _service;
- private readonly ConcurrentDictionary<string, CacheEntry> _cache;
- private readonly TimeSpan _cacheDuration;
-
- public WeatherServiceCache(WeatherServiceWrapper service, TimeSpan cacheDuration)
- {
- _service = service;
- _cache = new ConcurrentDictionary<string, CacheEntry>();
- _cacheDuration = cacheDuration;
- }
-
- public WeatherInfo GetWeatherInfo(string city, DateTime date)
- {
- string key = $"{city}_{date:yyyy-MM-dd}";
-
- // 检查缓存
- if (_cache.TryGetValue(key, out CacheEntry entry) && !entry.IsExpired(_cacheDuration))
- {
- return entry.Data;
- }
-
- // 调用服务
- WeatherInfo info = _service.GetWeatherInfo(city, date);
-
- // 更新缓存
- _cache.AddOrUpdate(key, new CacheEntry(info, DateTime.Now), (k, v) => new CacheEntry(info, DateTime.Now));
-
- return info;
- }
-
- private class CacheEntry
- {
- public WeatherInfo Data { get; }
- public DateTime Timestamp { get; }
-
- public CacheEntry(WeatherInfo data, DateTime timestamp)
- {
- Data = data;
- Timestamp = timestamp;
- }
-
- public bool IsExpired(TimeSpan cacheDuration)
- {
- return DateTime.Now - Timestamp > cacheDuration;
- }
- }
- }
复制代码
9.3 连接池和资源管理
- // Java
- import java.util.concurrent.ArrayBlockingQueue;
- import java.util.concurrent.BlockingQueue;
- import java.util.concurrent.TimeUnit;
- public class WeatherServiceClientPool {
-
- private final BlockingQueue<WeatherPortType> pool;
- private final int maxSize;
- private final WeatherServiceClientFactory factory;
-
- public WeatherServiceClientPool(int maxSize, WeatherServiceClientFactory factory) {
- this.maxSize = maxSize;
- this.factory = factory;
- this.pool = new ArrayBlockingQueue<>(maxSize);
- }
-
- public WeatherPortType borrowClient() throws InterruptedException {
- // 尝试从池中获取客户端
- WeatherPortType client = pool.poll();
-
- // 如果池为空,创建新客户端
- if (client == null) {
- return factory.createClient();
- }
-
- return client;
- }
-
- public void returnClient(WeatherPortType client) {
- if (client != null) {
- // 尝试将客户端返回池中
- pool.offer(client);
- }
- }
-
- public void close() {
- // 关闭池中的所有客户端
- for (WeatherPortType client : pool) {
- try {
- // 这里假设客户端有close方法
- // 实际情况可能需要根据具体实现调整
- } catch (Exception e) {
- // 记录错误
- }
- }
- pool.clear();
- }
- }
复制代码- # Python
- import queue
- import threading
- class WeatherServiceClientPool:
-
- def __init__(self, max_size, factory):
- self.max_size = max_size
- self.factory = factory
- self.pool = queue.Queue(max_size)
- self.lock = threading.Lock()
-
- def borrow_client(self):
- try:
- # 尝试从池中获取客户端
- return self.pool.get_nowait()
- except queue.Empty:
- # 如果池为空,创建新客户端
- return self.factory.create_client()
-
- def return_client(self, client):
- if client is not None:
- try:
- # 尝试将客户端返回池中
- self.pool.put_nowait(client)
- except queue.Full:
- # 如果池已满,不执行任何操作
- pass
-
- def close(self):
- # 关闭池中的所有客户端
- while not self.pool.empty():
- try:
- client = self.pool.get_nowait()
- # 这里假设客户端有close方法
- # 实际情况可能需要根据具体实现调整
- except queue.Empty:
- break
复制代码- // C#
- using System;
- using System.Collections.Concurrent;
- public class WeatherServiceClientPool : IDisposable
- {
- private readonly ConcurrentBag<WeatherServiceClient> _pool;
- private readonly int _maxSize;
- private readonly Func<WeatherServiceClient> _clientFactory;
-
- public WeatherServiceClientPool(int maxSize, Func<WeatherServiceClient> clientFactory)
- {
- _maxSize = maxSize;
- _clientFactory = clientFactory;
- _pool = new ConcurrentBag<WeatherServiceClient>();
- }
-
- public WeatherServiceClient BorrowClient()
- {
- // 尝试从池中获取客户端
- if (_pool.TryTake(out WeatherServiceClient client))
- {
- return client;
- }
-
- // 如果池为空,创建新客户端
- return _clientFactory();
- }
-
- public void ReturnClient(WeatherServiceClient client)
- {
- if (client != null && _pool.Count < _maxSize)
- {
- _pool.Add(client);
- }
- else
- {
- client?.Dispose();
- }
- }
-
- public void Dispose()
- {
- // 关闭池中的所有客户端
- while (_pool.TryTake(out WeatherServiceClient client))
- {
- client?.Dispose();
- }
- }
- }
复制代码
9.4 性能优化建议
- // Java
- import java.util.ArrayList;
- import java.util.List;
- import java.util.concurrent.CompletableFuture;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.stream.Collectors;
- public class WeatherServiceBatchProcessor {
-
- private final WeatherServiceWrapper service;
- private final ExecutorService executor;
-
- public WeatherServiceBatchProcessor(WeatherServiceWrapper service, int threadPoolSize) {
- this.service = service;
- this.executor = Executors.newFixedThreadPool(threadPoolSize);
- }
-
- public List<WeatherInfo> getWeatherInfoBatch(List<WeatherRequest> requests) {
- List<CompletableFuture<WeatherInfo>> futures = requests.stream()
- .map(request -> CompletableFuture.supplyAsync(
- () -> service.getWeatherInfo(request.getCity(), request.getDate()),
- executor))
- .collect(Collectors.toList());
-
- return futures.stream()
- .map(CompletableFuture::join)
- .collect(Collectors.toList());
- }
-
- public void shutdown() {
- executor.shutdown();
- }
-
- public static class WeatherRequest {
- private final String city;
- private final Date date;
-
- public WeatherRequest(String city, Date date) {
- this.city = city;
- this.date = date;
- }
-
- public String getCity() {
- return city;
- }
-
- public Date getDate() {
- return date;
- }
- }
- }
复制代码- # Python
- import concurrent.futures
- from typing import List, Tuple
- class WeatherServiceBatchProcessor:
-
- def __init__(self, service, max_workers):
- self.service = service
- self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=max_workers)
-
- def get_weather_info_batch(self, requests):
- futures = []
- for city, date in requests:
- future = self.executor.submit(self.service.get_weather_info, city, date)
- futures.append(future)
-
- results = []
- for future in concurrent.futures.as_completed(futures):
- try:
- results.append(future.result())
- except Exception as e:
- print(f"请求处理出错: {str(e)}")
-
- return results
-
- def shutdown(self):
- self.executor.shutdown()
复制代码- // C#
- using System;
- using System.Collections.Generic;
- using System.Threading.Tasks;
- public class WeatherServiceBatchProcessor : IDisposable
- {
- private readonly WeatherServiceWrapper _service;
- private readonly int _maxDegreeOfParallelism;
-
- public WeatherServiceBatchProcessor(WeatherServiceWrapper service, int maxDegreeOfParallelism)
- {
- _service = service;
- _maxDegreeOfParallelism = maxDegreeOfParallelism;
- }
-
- public async Task<List<WeatherInfo>> GetWeatherInfoBatchAsync(List<WeatherRequest> requests)
- {
- var options = new ParallelOptions
- {
- MaxDegreeOfParallelism = _maxDegreeOfParallelism
- };
-
- var results = new List<WeatherInfo>();
- var lockObj = new object();
-
- await Parallel.ForEachAsync(requests, options, async (request, cancellationToken) =>
- {
- try
- {
- var info = await Task.Run(() => _service.GetWeatherInfo(request.City, request.Date), cancellationToken);
- lock (lockObj)
- {
- results.Add(info);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine($"请求处理出错: {ex.Message}");
- }
- });
-
- return results;
- }
-
- public void Dispose()
- {
- // 清理资源
- }
-
- public record WeatherRequest(string City, DateTime Date);
- }
复制代码- // Java
- import javax.xml.ws.AsyncHandler;
- import javax.xml.ws.Response;
- import java.util.concurrent.CompletableFuture;
- import java.util.concurrent.ExecutionException;
- public class WeatherServiceAsync {
-
- private final WeatherPortType port;
-
- public WeatherServiceAsync(WeatherPortType port) {
- this.port = port;
- }
-
- public CompletableFuture<WeatherInfo> getWeatherInfoAsync(String city, Date date) {
- CompletableFuture<WeatherInfo> future = new CompletableFuture<>();
-
- // 创建请求
- GetWeatherRequest request = new GetWeatherRequest();
- request.setCity(city);
- request.setDate(date);
-
- // 异步调用
- AsyncHandler<GetWeatherResponse> handler = new AsyncHandler<GetWeatherResponse>() {
- @Override
- public void handleResponse(Response<GetWeatherResponse> response) {
- try {
- GetWeatherResponse responseObj = response.get();
-
- // 转换为业务对象
- WeatherInfo info = new WeatherInfo();
- info.setCity(city);
- info.setDate(date);
- info.setTemperature(responseObj.getTemperature());
- info.setConditions(responseObj.getConditions());
-
- future.complete(info);
- } catch (InterruptedException | ExecutionException e) {
- future.completeExceptionally(e);
- }
- }
- };
-
- port.getWeatherAsync(request, handler);
-
- return future;
- }
- }
复制代码- # Python
- import asyncio
- from concurrent.futures import ThreadPoolExecutor
- class WeatherServiceAsync:
-
- def __init__(self, service):
- self.service = service
- self.executor = ThreadPoolExecutor(max_workers=10)
-
- async def get_weather_info_async(self, city, date):
- loop = asyncio.get_event_loop()
- return await loop.run_in_executor(
- self.executor,
- lambda: self.service.get_weather_info(city, date)
- )
-
- def shutdown(self):
- self.executor.shutdown()
复制代码- // C#
- using System;
- using System.Threading.Tasks;
- public class WeatherServiceAsync
- {
- private readonly WeatherServiceClient _client;
-
- public WeatherServiceAsync(WeatherServiceClient client)
- {
- _client = client;
- }
-
- public Task<WeatherInfo> GetWeatherInfoAsync(string city, DateTime date)
- {
- // 创建请求
- var request = new GetWeatherRequest
- {
- City = city,
- Date = date
- };
-
- // 异步调用服务
- return _client.GetWeatherAsync(request)
- .ContinueWith(task =>
- {
- if (task.IsFaulted)
- {
- throw task.Exception;
- }
-
- var response = task.Result;
-
- // 转换为业务对象
- return new WeatherInfo
- {
- City = city,
- Date = date,
- Temperature = response.Temperature,
- Conditions = response.Conditions
- };
- });
- }
- }
复制代码
10. 总结
本文详细介绍了从零开始学习WSDL服务调用的全过程,包括WSDL基础概念、文档结构解析、多种编程语言的调用实例以及常见问题的解决方案。通过本文的学习,开发者可以快速掌握网络服务接口调用的关键技巧。
10.1 关键要点回顾
1. WSDL基础:理解WSDL文档结构和各元素的作用是调用Web服务的基础。
2. 开发环境准备:选择合适的IDE和工具可以大大提高开发效率。
3. 多语言支持:本文提供了Java、Python和C#三种主流语言的调用实例,覆盖了大多数开发场景。
4. 问题解决:连接问题、认证问题、数据类型转换和错误处理是WSDL服务调用中最常见的问题,本文提供了实用的解决方案。
5. 最佳实践:通过使用工厂模式、包装器、缓存策略、连接池和异步调用等技术,可以显著提高代码质量和性能。
10.2 进阶学习建议
1. 深入学习SOAP协议:了解SOAP消息结构、协议规范和扩展机制。
2. 学习RESTful服务:与SOAP服务相比,RESTful服务更轻量级,也是现代Web服务的主流。
3. 探索微服务架构:了解如何在微服务架构中使用Web服务进行服务间通信。
4. 学习API管理:了解API网关、服务发现和负载均衡等高级主题。
通过不断实践和学习,开发者可以掌握WSDL服务调用的精髓,并在实际项目中灵活应用这些技术,构建高效、可靠的企业级应用程序。 |
|