你的位置:首页 > ASP.net教程

[ASP.net教程]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 基于

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

因为是基于

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

 

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

新建项目

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

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

实现

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

Install-Package Microsoft.AspNetCore.Http.AbstractionsInstall-Package System.ServiceModel.PrimitivesInstall-Package System.Reflection.TypeExtensionsInstall-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  requestMessage.GetReaderAtBodyContents())      {        // 查找的操作数据的元素        for (int i = 0; i < parameters.Length; i++)        {          var parameterName = parameters[i].GetCustomAttribute<MessageParameterAttribute>()?.Name ?? parameters[i].Name;          if (var serializer = new DataContractSerializer(parameters[i].ParameterType, parameterName, operation.Contract.Namespace);            arguments.Add(serializer.ReadObject(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(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.PrimitivesInstall-Package System.Private.ServiceModelInstall-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/

 

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