ASP.NET Core中间件(Middleware)实现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
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.cspublic 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 = 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 類。
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 類
這里我為了方便將接口契約也放在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代碼如下:
編寫好以后,分別對應到目錄使用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/
原文地址:http://www.cnblogs.com/linezero/p/aspnetcoresoap.html
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的ASP.NET Core中间件(Middleware)实现WCF SOAP服务端解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自包含 .NET Core应用程序
- 下一篇: ASP.NET Core MVC 配置全