WCF SOAP服务端解析

ASP.NET Core中间件(Middleware)进阶学习实现SOAP 解析。

本篇将介绍实现ASP.NET Core SOAP服务端解析,而不是ASP.NET Core整个WCF host。

因为WCF中不仅仅只是有SOAP, 它还包含很多如消息安全性,生成WSDL,双工信道,非HTTP传输等。

ASP.NET Core 官方推荐大家使用RESTful Web API的解决方案提供网络服务。

SOAP 即 Simple Object AccessProtocol 也就是简单对象访问协议。

SOAP 呢,其指导理念是“唯一一个没有发明任何新技术的技术”,

是一种用于访问 Web 服务的协议。

因为 SOAP 基于XML 和 HTTP ,其通过XML 来实现消息描述,然后再通过 HTTP 实现消息传输。

SOAP 是用于在应用程序之间进行通信的一种通信协议。

因为是基于 XML 和HTTP 的,所以其独立于语言,独立于平台,并且因为 XML 的扩展性很好,所以基于 XML 的 SOAP 自然扩展性也不差。

通过 SOAP 可以非常方便的解决互联网中消息互联互通的需求,其和其他的 Web 服务协议构建起 SOA 应用的技术基础。

下面来正式开始 ASP.NET Core 实现SOAP 服务端解析。

新建项目

首先新建一个ASP.NET Core Web Application -》 SOAPService 然后再模板里选择 Web API。

然后我们再添加一个Class Library -》 CustomMiddleware

实现

下面我们来实现功能,首先在类库项目中添加以下引用

Install-Package Microsoft.AspNetCore.Http.Abstractions

Install-Package System.ServiceModel.Primitives

Install-Package System.Reflection.TypeExtensions

Install-Package System.ComponentModel

首先新建一个 ServiceDescription、ContractDescription和OperationDescription 类,这里需要注意的是ServiceDescription,ContractDescription和OperationDescription这里使用的是不能使用 System.ServiceModel.Description命名空间中的类型。它们是示例中简单的新类型。

ServiceDescription.cs

 

ContractDescription.cs

 

OperationDescription.cs

 

添加完成后下面来新建一个中间件 SOAPMiddleware ,对于新建中间件可以参考我之前的文章:http://www.cnblogs.com/linezero/p/5529767.html

SOAPMiddleware.cs 代码如下:

    public class SOAPMiddleware
{
private readonly RequestDelegate _next;
private readonly Type _serviceType;
private readonly string _endpointPath;
private readonly MessageEncoder _messageEncoder;
private readonly ServiceDescription _service;
private IServiceProvider serviceProvider; public SOAPMiddleware(RequestDelegate next, Type serviceType, string path, MessageEncoder encoder,IServiceProvider _serviceProvider)
{
_next = next;
_serviceType = serviceType;
_endpointPath = path;
_messageEncoder = encoder;
_service = new ServiceDescription(serviceType);
serviceProvider = _serviceProvider;
} public async Task Invoke(HttpContext httpContext)
{
if (httpContext.Request.Path.Equals(_endpointPath, StringComparison.Ordinal))
{
Message responseMessage; //读取Request请求信息
var requestMessage = _messageEncoder.ReadMessage(httpContext.Request.Body, 0x10000, httpContext.Request.ContentType);
var soapAction = httpContext.Request.Headers["SOAPAction"].ToString().Trim('\"');
if (!string.IsNullOrEmpty(soapAction))
{
requestMessage.Headers.Action = soapAction;
}
//获取操作
var operation = _service.Operations.Where(o => o.SoapAction.Equals(requestMessage.Headers.Action, StringComparison.Ordinal)).FirstOrDefault();
if (operation == null)
{
throw new InvalidOperationException($"No operation found for specified action: {requestMessage.Headers.Action}");
}
//获取注入的服务
var serviceInstance = serviceProvider.GetService(_service.ServiceType); //获取操作的参数信息
var arguments = GetRequestArguments(requestMessage, operation); // 执行操作方法
var responseObject = operation.DispatchMethod.Invoke(serviceInstance, arguments.ToArray()); var resultName = operation.DispatchMethod.ReturnParameter.GetCustomAttribute<MessageParameterAttribute>()?.Name ?? operation.Name + "Result";
var bodyWriter = new ServiceBodyWriter(operation.Contract.Namespace, operation.Name + "Response", resultName, responseObject);
responseMessage = Message.CreateMessage(_messageEncoder.MessageVersion, operation.ReplyAction, bodyWriter); httpContext.Response.ContentType = httpContext.Request.ContentType;
httpContext.Response.Headers["SOAPAction"] = responseMessage.Headers.Action; _messageEncoder.WriteMessage(responseMessage, httpContext.Response.Body);
}
else
{
await _next(httpContext);
}
} private object[] GetRequestArguments(Message requestMessage, OperationDescription operation)
{
var parameters = operation.DispatchMethod.GetParameters();
var arguments = new List<object>(); // 反序列化请求包和对象
using (var xmlReader = requestMessage.GetReaderAtBodyContents())
{
// 查找的操作数据的元素
xmlReader.ReadStartElement(operation.Name, operation.Contract.Namespace); for (int i = 0; i < parameters.Length; i++)
{
var parameterName = parameters[i].GetCustomAttribute<MessageParameterAttribute>()?.Name ?? parameters[i].Name;
xmlReader.MoveToStartElement(parameterName, operation.Contract.Namespace);
if (xmlReader.IsStartElement(parameterName, operation.Contract.Namespace))
{
var serializer = new DataContractSerializer(parameters[i].ParameterType, parameterName, operation.Contract.Namespace);
arguments.Add(serializer.ReadObject(xmlReader, verifyObjectName: true));
}
}
} return arguments.ToArray();
}
} public static class SOAPMiddlewareExtensions
{
public static IApplicationBuilder UseSOAPMiddleware<T>(this IApplicationBuilder builder, string path, MessageEncoder encoder)
{
return builder.UseMiddleware<SOAPMiddleware>(typeof(T), path, encoder);
}
public static IApplicationBuilder UseSOAPMiddleware<T>(this IApplicationBuilder builder, string path, Binding binding)
{
var encoder = binding.CreateBindingElements().Find<MessageEncodingBindingElement>()?.CreateMessageEncoderFactory().Encoder;
return builder.UseMiddleware<SOAPMiddleware>(typeof(T), path, encoder);
}
}

这里对于输出的消息做了一个封装,以输出具有正确的元素名称的消息的主体。

添加一个 ServiceBodyWriter 类。

 

这里对于中间件整个就完成了。

服务端

在服务端使用,这里你也可以新建一个Web 项目。

因为刚刚我们已经新建好了一个Web API项目,我们就直接拿来使用。

首先添加 CustomMiddleware 引用

在 SOAPService 中添加一个 CalculatorService 类

    public class CalculatorService : ICalculatorService
{
public double Add(double x, double y) => x + y;
public double Divide(double x, double y) => x / y;
public double Multiply(double x, double y) => x * y;
public double Subtract(double x, double y) => x - y;
public string Get(string str) => $"{str} Hello World!";
} [ServiceContract]
public interface ICalculatorService
{
[OperationContract]
double Add(double x, double y);
[OperationContract]
double Subtract(double x, double y);
[OperationContract]
double Multiply(double x, double y);
[OperationContract]
double Divide(double x, double y);
[OperationContract]
string Get(string str);
}

这里我为了方便将接口契约也放在CalculatorService 中,你也可以新建一个接口。

然后在 Startup.cs  的 ConfigureServices 中注入 CalculatorService

        public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddScoped<CalculatorService>();
}

在Configure 方法中加入中间件

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
//加入一个/CalculatorService.svc 地址,绑定Http
app.UseSOAPMiddleware<CalculatorService>("/CalculatorService.svc", new BasicHttpBinding()); app.UseMvc();
}

这样就完成了服务端编写。

客户端

新建一个 Console Application -》SOAPClient

添加如下引用:

Install-Package System.ServiceModel.Primitives

Install-Package System.Private.ServiceModel

Install-Package System.ServiceModel.Http

Program代码如下:

    public class Program
{
public static void Main(string[] args)
{
Random numGen = new Random();
double x = numGen.NextDouble() * 20;
double y = numGen.NextDouble() * 20; var serviceAddress = "http://localhost:5000/CalculatorService.svc"; var client = new CalculatorServiceClient(new BasicHttpBinding(), new EndpointAddress(serviceAddress));
Console.WriteLine($"{x} + {y} == {client.Add(x, y)}");
Console.WriteLine($"{x} - {y} == {client.Subtract(x, y)}");
Console.WriteLine($"{x} * {y} == {client.Multiply(x, y)}");
Console.WriteLine($"{x} / {y} == {client.Divide(x, y)}");
client.Get("Client");
}
}
class CalculatorServiceClient : ClientBase<ICalculatorService>
{
public CalculatorServiceClient(Binding binding, EndpointAddress remoteAddress) : base(binding, remoteAddress) { }
public double Add(double x, double y) => Channel.Add(x, y);
public double Subtract(double x, double y) => Channel.Subtract(x, y);
public double Multiply(double x, double y) => Channel.Multiply(x, y);
public double Divide(double x, double y) => Channel.Divide(x, y); public void Get(string str)
{
Console.WriteLine(Channel.Get(str));
}
} [ServiceContract]
public interface ICalculatorService
{
[OperationContract]
double Add(double x, double y);
[OperationContract]
double Subtract(double x, double y);
[OperationContract]
double Multiply(double x, double y);
[OperationContract]
double Divide(double x, double y);
[OperationContract]
string Get(string str);
}

编写好以后,分别对应到目录使用dotnet run执行程序。

成功建立了连接,也有返回。也就实现SOAP 的解析。

示例代码GitHub:https://github.com/linezero/Blog/tree/master/SOAPService

参考文档:https://blogs.msdn.microsoft.com/dotnet/2016/09/19/custom-asp-net-core-middleware-example/

如果你觉得本文对你有帮助,请点击“推荐”,谢谢。

.NET Core 跨平台交流群: 550897034  
博客示例代码:GitHub

WCF SOAP的更多相关文章

  1. ASP.NET Core中间件(Middleware)实现WCF SOAP服务端解析

    ASP.NET Core中间件(Middleware)进阶学习实现SOAP 解析. 本篇将介绍实现ASP.NET Core SOAP服务端解析,而不是ASP.NET Core整个WCF host. 因 ...

  2. [C# | WinCE | Solution] 在 WinCE 上访问 SSL 加密后的 WCF SOAP 服务接口出现“未能与远程服务器建立信任关系”

    Scenario: 服务器的 SOAP 使用了 GeoTrust 签名的 EV 证书,WinCE调用时出现“未能与远程服务器建立信任关系”的错误.原因是该 WinCE 设备信任的证书包括 Global ...

  3. WCF SOAP用法

    基本思路 1.新建一个WCF服务库2.在客户端引用处右键,添加服务引用   点击发现,选择目标服务设置好命名空间   可以在高级一栏里面,设置详细信息   点击确认,添加服务引用 3.在客户端自动生成 ...

  4. WCF学习之旅—WCF概述(四)

    一.WCF概述 1) 什么是WCF? Windows Communication Foundation (WCF) 是用于构建面向服务的应用程序的框架.借助 WCF,可以将数据作为异步消息从一个服务终 ...

  5. WCF技术的不同应用场景及其实现分析

    这一篇文章,是总结一下WCF技术,以及基于这个技术发展出来的几个典型应用场景,并且我将尝试对这些不同的WCF实现的原理进行一些比较分析. 关于WCF这个技术的基本概念,如果你不是很清楚,可以参考一下有 ...

  6. WCF - REST服务

    WCF REST服务 一个基于REST的WEB服务操作请求只需要体现两点 一是资源的唯一标识 二是操作类型 资源的唯一标识通过URI来完成 而操作类型通过HTTP方法(GET/HEAD POST PU ...

  7. 响应消息的内容类型 text/html; charset=utf-8 与绑定(application/soap+xml; charset=utf-8)的内容类型不匹配。

    问题表述: 响应消息的内容类型 text/html; charset=utf-8 与绑定(application/soap+xml; charset=utf-8)的内容类型不匹配. 说明: 此类问题当 ...

  8. WCF学习目录

    WCF 基本 WCF概念 WCF配置文件详解 多个不同类对象传输思路 WCF 大文件传输配置 Uri ? & = 毫秒数据字符串转换为DateTime POST请求——HttpWebReque ...

  9. 使用ServiceStack构建Web服务

    提到构建WebService服务,大家肯定第一个想到的是使用WCF,因为简单快捷嘛.首先要说明的是,本人对WCF不太了解,但是想快速建立一个WebService,于是看到了MSDN上的这一篇文章 Bu ...

随机推荐

  1. CAS SSO:汇集配置过程中的错误解决方法

    本教程为gevin.me原创文章,转载请注明: CAS SSO:配置过程中的错误解决方法 | Gevin’s Blog 本文将收集在配置CAS SSO遇到的所有错误,希望对大家有帮助,也方便下次搭建的 ...

  2. 【转】Eclipse上安装GIT插件EGit及使用

    http://yufenfei.iteye.com/blog/1750124 一.Eclipse上安装GIT插件EGit Eclipse的版本eclipse-java-helios-SR2-win32 ...

  3. BA的广度和深度

    http://insights.thoughtworkers.org/ba-capability-and-development-path-in-thoughtworks/ 作者:ThoughtWor ...

  4. JAVA 上加密算法的实现用例---转载

    通常 , 使用的加密算法 比较简便高效 , 密钥简短,加解密速度快,破译极其困难.本文介绍了 MD5/SHA1,DSA,DESede/DES,Diffie-Hellman 的使用. 第 1 章基础知识 ...

  5. Android(java)学习笔记186:对ListView等列表组件中数据进行增、删、改操作

    1.ListView介绍 解决大量的相似的数据显示问题 采用了MVC模式: M: model (数据模型) V:  view  (显示的视图) C: controller 控制器 入门案例: acit ...

  6. my_pnd_start

    终于有个雏形了,再给自己一些时间做做看吧.

  7. SharePoint SiteCollection Administrator

    到网上去找怎么取到一个站点的sitecollection  Administrator, 如果设置了一个站点的 sitecollection  Administrator, 那么通过: SPSite ...

  8. Android 设计随便说说之简单实践(合理组合)

    上一篇(Android 设计随便说说之简单实践(模块划分))例举了应用商店设计来说明怎么做模块划分.模块划分主要依赖于第一是业务需求,具体是怎么样的业务.应用商店则包括两个业务,就是向用户展示appl ...

  9. Android - This Handler class should be static or leaks might occur.

    今天学习了使用 HTTP协议,从Android客户端往Tomcat服务器端以GET发送请求,途中无意中发现自己写的Handler类被Android提示:This Handler class shoul ...

  10. 浅谈.net中的params关键字

    先举个例子: 代码如下: class Program { static void Main(string[] args) { Console.WriteLine(Sum(1)); Console.Wr ...