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

时间:2022-01-27 21:08:12

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。

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

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

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

实现

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

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

    public class ServiceDescription
{
public Type ServiceType { get; private set; }
public IEnumerable<ContractDescription> Contracts { get; private set; }
public IEnumerable<OperationDescription> Operations => Contracts.SelectMany(c => c.Operations); public ServiceDescription(Type serviceType)
{
ServiceType = serviceType; var contracts = new List<ContractDescription>(); foreach (var contractType in ServiceType.GetInterfaces())
{
foreach (var serviceContract in contractType.GetTypeInfo().GetCustomAttributes<ServiceContractAttribute>())
{
contracts.Add(new ContractDescription(this, contractType, serviceContract));
}
} Contracts = contracts;
}
}

ContractDescription.cs

    public class ContractDescription
{
public ServiceDescription Service { get; private set; }
public string Name { get; private set; }
public string Namespace { get; private set; }
public Type ContractType { get; private set; }
public IEnumerable<OperationDescription> Operations { get; private set; } public ContractDescription(ServiceDescription service, Type contractType, ServiceContractAttribute attribute)
{
Service = service;
ContractType = contractType;
Namespace = attribute.Namespace ?? "http://tempuri.org/"; // Namespace defaults to http://tempuri.org/
Name = attribute.Name ?? ContractType.Name; // Name defaults to the type name var operations = new List<OperationDescription>();
foreach (var operationMethodInfo in ContractType.GetTypeInfo().DeclaredMethods)
{
foreach (var operationContract in operationMethodInfo.GetCustomAttributes<OperationContractAttribute>())
{
operations.Add(new OperationDescription(this, operationMethodInfo, operationContract));
}
}
Operations = operations;
}
}

OperationDescription.cs

    public class OperationDescription
{
public ContractDescription Contract { get; private set; }
public string SoapAction { get; private set; }
public string ReplyAction { get; private set; }
public string Name { get; private set; }
public MethodInfo DispatchMethod { get; private set; }
public bool IsOneWay { get; private set; } public OperationDescription(ContractDescription contract, MethodInfo operationMethod, OperationContractAttribute contractAttribute)
{
Contract = contract;
Name = contractAttribute.Name ?? operationMethod.Name;
SoapAction = contractAttribute.Action ?? $"{contract.Namespace.TrimEnd('/')}/{contract.Name}/{Name}";
IsOneWay = contractAttribute.IsOneWay;
ReplyAction = contractAttribute.ReplyAction;
DispatchMethod = operationMethod;
}
}

添加完成后下面来新建一个中间件 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 = ; 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 类。

    public class ServiceBodyWriter : BodyWriter
{
string ServiceNamespace;
string EnvelopeName;
string ResultName;
object Result; public ServiceBodyWriter(string serviceNamespace, string envelopeName, string resultName, object result) : base(isBuffered: true)
{
ServiceNamespace = serviceNamespace;
EnvelopeName = envelopeName;
ResultName = resultName;
Result = result;
} protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartElement(EnvelopeName, ServiceNamespace);
var serializer = new DataContractSerializer(Result.GetType(), ResultName, ServiceNamespace);
serializer.WriteObject(writer, Result);
writer.WriteEndElement();
}
}

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

服务端

在服务端使用,这里你也可以新建一个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

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

添加如下引用:

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() * ;
double y = numGen.NextDouble() * ; 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执行程序。

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

成功建立了连接,也有返回。也就实现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/

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

ASP.NET Core中间件(Middleware)实现WCF SOAP服务端解析的更多相关文章

  1. ASP&period;NET Core 入门教程 9、ASP&period;NET Core 中间件&lpar;Middleware&rpar;入门

    一.前言 1.本教程主要内容 ASP.NET Core 中间件介绍 通过自定义 ASP.NET Core 中间件实现请求验签 2.本教程环境信息 软件/环境 说明 操作系统 Windows 10 SD ...

  2. ASP&period;NET Core 入门笔记10,ASP&period;NET Core 中间件&lpar;Middleware&rpar;入门

    一.前言 1.本教程主要内容 ASP.NET Core 中间件介绍 通过自定义 ASP.NET Core 中间件实现请求验签 2.本教程环境信息 软件/环境 说明 操作系统 Windows 10 SD ...

  3. ASP&period;NET Core -中间件&lpar;Middleware&rpar;使用

    ASP.NET Core开发,开发并使用中间件(Middleware). 中间件是被组装成一个应用程序管道来处理请求和响应的软件组件. 每个组件选择是否传递给管道中的下一个组件的请求,并能之前和下一组 ...

  4. ASP&period;NET Core 中间件Diagnostics使用

    ASP.NET Core 中间件(Middleware)Diagnostics使用.对于中间件的介绍可以查看之前的文章ASP.NET Core 开发-中间件(Middleware). Diagnost ...

  5. ASP&period;NET Core 中间件Diagnostics使用 异常和错误信息

    ASP.NET Core 中间件(Middleware)Diagnostics使用.对于中间件的介绍可以查看之前的文章ASP.NET Core 开发-中间件(Middleware). Diagnost ...

  6. ASP&period;NET Core 中间件自定义全局异常处理

    目录 背景 ASP.NET Core过滤器(Filter) ASP.NET Core 中间件(Middleware) 自定义全局异常处理 .Net Core中使用ExceptionFilter .Ne ...

  7. &lbrack;转帖&rsqb;ASP&period;NET Core 中间件(Middleware)详解

    ASP.NET Core 中间件(Middleware)详解   本文为官方文档译文,官方文档现已非机器翻译 https://docs.microsoft.com/zh-cn/aspnet/core/ ...

  8. 在ASP&period;NET Core使用Middleware模拟Custom Error Page功能

    一.使用场景 在传统的ASP.NET MVC中,我们可以使用HandleErrorAttribute特性来具体指定如何处理Action抛出的异常.只要某个Action设置了HandleErrorAtt ...

  9. &lbrack;转&rsqb;ASP&period;NET Core 中间件详解及项目实战

    本文转自:http://www.cnblogs.com/savorboard/p/5586229.html 前言 在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际 ...

随机推荐

  1. 设计模式C&num;合集--抽象工厂模式

    抽象工厂,名字就告诉你是抽象的了.上代码. public interface BMW { public void Drive(); } public class BMW730 : BMW { publ ...

  2. c&plus;&plus; string的实现。

    第三次做了.只是做个复习.偶然发现之前的版本有内存泄露.基本功还是不过关.这次应该没有内存泄漏了.虽然是个简单版本. 1)了解堆,栈,值copy. 2)几个常用的c的字符函数和c中的char 如何表示 ...

  3. Metropolis Hasting算法

    Metropolis Hasting Algorithm: MH算法也是一种基于模拟的MCMC技术,一个非常重要的应用是从给定的概率分布中抽样.主要原理是构造了一个精妙的Markov链,使得该链的稳态 ...

  4. HTML系列(一):创建HTML文档

    从本学期开始我打算把我以前学的知识点系统地总结一下,先从HTML开始.(本系列内容总结自博文视点出版社•代码逆袭系列书籍,包括代码片段.) 一.HTML文档类型 HTML版本众多,浏览器如何得知使用的 ...

  5. IntelliJ IDEA 出现Unable to execute DX 问题

    最近从eclipse转移到IDEA上写代码的时候,无意间发现一个让我头疼不已的问题,message报错为 Error:Android Dex: [**] Unable to execute DX Er ...

  6. linux 删除开头是减号的文件名

    若有一个文件的文件名为 : -testfile 删除方法为 : 输入“rm — -testfile”或”rm ./-testfile”命令便可顺利删除名为“-testfile”的文件. 如果是其他特殊 ...

  7. 实验验证sys和system用户全库导出的区别

    我们在做逻辑数据泵全库导出的时候,有两种流行的写法,一种是sys用户导出,一种是使用system用户导出. 现在想知道二者之间有什么区别?实验验证之前不妨先思考一下: sys和system用户的权限区 ...

  8. ssh config配置

    使用ssh config文件可以简化ssh连接输入参数,直接从config读取 (1)建立config文件 config文件位置在~/.ssh/config 如果不存在,可以创建一个 (2)confi ...

  9. 【转】TensorFlow四种Cross Entropy算法实现和应用

    http://www.jianshu.com/p/75f7e60dae95 作者:陈迪豪 来源:CSDNhttp://dataunion.org/26447.html 交叉熵介绍 交叉熵(Cross ...

  10. IIS7虚拟目录出现HTTP错误500&period;19(由于权限不足而无法读取配置文件)的解决方案

    今天在window7上配置asp.net网站,但是访问总是提示 错误摘要HTTP 错误 500.19 - Internal Server Error无法访问请求的页面,因为该页的相关配置数据无效.详细 ...