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. JMeter的基本介绍和入门

    1. 介绍 JMeter是Apache组织的开放源代码项目,能做功能测试和性能测试.它能够对HTTP和FTP服务器进行压力和性能测试,也可以对任何数据库进行同样的测试(通过JDBC),还能以多种形式展 ...

  2. 蜗牛—JSP学习之JavaBean初识

    初识: <%@ page language="java" import="java.util.*" pageEncoding="utf-8&qu ...

  3. 如何将angularJs项目与requireJs集成

    关于angularjs.requirejs的基础知识请自行学习 一.简单事例的项目目录如下: -index.html -scripts文件夹 --controller文件夹 --- mianContr ...

  4. XRDP与VNC的关系

    如果仅仅安装XRDP协议.是不能在windows上使用远程桌面连接到Ubuntu. 还须要安装VNCServer才行. 所以,XRDP启动之后.系统会自己主动启动一个VNC会话进程监听服务. 当我们通 ...

  5. web配置nagios工具

    Nagios是一款开源的免费网络监视工具,能有效监控Windows.Linux和Unix的主机状态,交换机路由器等网络设置,打印机等.在系统或服务状态异常时发出邮件或短信报警第一时间通知网站运维人员, ...

  6. SVN安装手册

    转:http://www.cnblogs.com/newsea/archive/2012/04/28/2474818.html 常用工具2012-04-04 21:24        by       ...

  7. android 61 logcat

    package com.itheima.logcat; import android.os.Bundle; import android.app.Activity; import android.ut ...

  8. request对象

    Servlet三大域对象的应用 request.session.application(ServletContext) 请求转发器: public void doGet(HttpServletRequ ...

  9. expect批量分发公钥

    sshkey.exp #!/usr/bin/expect# 由于是多台服务器,需要在shell脚本中循环调用该脚本 if { $argc != 2 } { send_user "usage: ...

  10. iOS网络层架构设计分享

    前言 前些天帮公司做了网络层的重构,当时就想做好了就分享给大家,后来接着做了新版本的需求,现在才有时间整理一下. 之前的网络层使用的是直接拖拽导入项目的方式导入了AF,然后还修改了大量的源码,时隔2年 ...