程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> ASP.NET Core中間件(Middleware)實現WCF SOAP服務端解析,middlewarewcf

ASP.NET Core中間件(Middleware)實現WCF SOAP服務端解析,middlewarewcf

編輯:關於.NET

ASP.NET Core中間件(Middleware)實現WCF SOAP服務端解析,middlewarewcf


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; } } View Code

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; } } View Code

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; } } View Code

 

添加完成後下面來新建一個中間件 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(); } } View Code

這裡對於中間件整個就完成了。

服務端

在服務端使用,這裡你也可以新建一個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/

 

如果你覺得本文對你有幫助,請點擊“推薦”,謝謝。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved