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. onkeyup 事件会在键盘按键被松开时发生

    定义和用法 onkeyup 事件会在键盘按键被松开时发生. 语法 onkeyup="SomeJavaScriptCode" 参数 描述 SomeJavaScriptCode 必需. ...

  2. (转) html块级元素和内联元素区别详解

    http://blog.csdn.net/chen_zw/article/details/8713205 块级元素(block)特性: 总是独占一行,表现为另起一行开始,而且其后的元素也必须另起一行显 ...

  3. SQL语法集锦一:显示每个类别最新更新的数据

    本文转载http://www.cnblogs.com/lxblog/archive/2012/09/28/2707504.html (1)显示每个类别最新更新的数据 在项目中经常遇到求每个类别最新显示 ...

  4. mybatis0203 一对一查询 resultMap实现

    resultType实现的时候先要确定po类(数据库类)满不满足要求,如果不满足就要自定义一个pojo类(工具类). resultMap提供一对一关联查询的映射和一对多关联查询映射,一对一映射思路:将 ...

  5. Btrace

    http://www.iteye.com/topic/1005918 背景 周五下班回家,在公司班车上觉得无聊,看了下btrace的源码(自己反编译). 一些关于btrace的基本内容,可以看下我早起 ...

  6. PERFORMANCE_SCHEMA 详解

    http://keithlan.github.io/2015/07/17/22_performance_schema/ http://www.markleith.co.uk/ http://www.c ...

  7. Flume NG中的Kafka Channel

    kafka(官网地址:http://kafka.apache.org)是一款分布式消息发布和订阅的系统 在Flume中的KafkaChannel支持Flume与Kafka整合,可以将Kafka当做ch ...

  8. thinkphp中session跨域问题

    问题描述 <thinkphp实现短信验证注册>中,小编不止记录了短信验证码的实现方法,同时还记录了图片验证码的实现方法. 本地使用,一切正常:后端项目和前端项目都部署到服务器,一切正常:后 ...

  9. tomcat中jsp编译

    tomcat运行的工程中,jsp替换文件后可能不起作用.原因是jsp也是需要编译的.编译后的文件存放在tomcat/work文件夹下.如果替换不起作用,可以将work文件夹下的内容删除掉,重新启tom ...

  10. Java-struts2 之值栈问题

    这里是根据一个小项目,将数据库的值查出来,然后在页面前台进行遍历的方法 放入值的几种方式: Struts2的三种存值取值的方式 值栈: 栈上下文: ActionContext: package com ...