如何在 Asp.net 核心项目中使用 SoapCore 在项目路由文件夹中公开 wsdl

How to use SoapCore in Asp.net Core project for exposing wsdl at project route folder

您的项目使用 Asp.net Core 编写,部署 Rest API。但是,您的客户想要与 soap 进行交流。如何改进

SoapCore已经为我们做了很多事情来支持这种情况。我们对 Asp.net 核心项目应用逐步更改。

Startup.cs第一名:

public void ConfigureServices(IServiceCollection services)
        {
            try
            {
                services.AddSoapServiceOperationTuner(new MyServiceOperationTuner());

                services.Configure<XApiSettings>(options =>
                {
                    options.baseurl = XApiOptions[nameof(XApi.baseurl)];
                    options.accesstokenroute = XApiOptions[nameof(XApi.accesstokenroute)];
                    options.secret = XApiOptions[nameof(XApi.secret)];
                    options.secretkey = XApiOptions[nameof(XApi.secretkey)];
                    options.grant_type = XApiOptions[nameof(XApi.grant_type)];
                    options.username = XApiOptions[nameof(XApi.username)];
                    options.password = XApiOptions[nameof(XApi.password)];
                });


                services.AddSoapCore();
                services.AddSingleton<IRESAdapterService>(new RESAdapterService(
                    Xcontroller: new XApiController(
                        services.BuildServiceProvider().GetRequiredService<IOptions<XApi>>(),
                        _corendonLogger
                        )));
                services.AddSoapExceptionTransformer((ex) => ex.Message);
            }
            catch (Exception ex)
            {
                Log.Logger.Error("ConfigureServices Message: " + ex.Message);
            }
        }

如果您希望您的应用程序可以从您部署地址的根目录访问,您可以直接键入路径'/'或将其命名为'/XX'

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, ApplicationContext dbContext)
    {
        try
        {
            app.UseSoapEndpoint<IRESAdapterService>(path: "/", binding: new BasicHttpBinding(), SoapSerializer.XmlSerializer);
        }
    }

对于在服务器端处理的请求,作为 xml 发送的数据通常为空。我们需要在 SoapCore 中做以下改进,以便服务器能够解析请求。

public class MyServiceOperationTuner : IServiceOperationTuner
    {
        public void Tune(HttpContext httpContext, object serviceInstance, SoapCore.OperationDescription operation)
        {
            RESAdapterService service = serviceInstance as RESAdapterService;
            service.SetHttpRequest = httpContext.Request;
        }
    }

此外,满足传入请求和服务重定向到我们控制器的接口应该写成如下

[ServiceContract]
    public interface IRESAdapterService
    {
        [OperationContract]
        [XmlSerializerFormat(SupportFaults = true)]
        Task<OTA_AirAvailRS> getAvailability([FromBody]HttpRequestMessage req);
        [OperationContract]
        Task<OTA_AirPriceRS> pricing([FromBody]HttpRequestMessage req);
    }

        public class RESAdapterService : IRESAdapterService
        {
            XApiController _controller;
            public HttpRequest SetHttpRequest { get; set; }
            public RESAdapterService(XApiController XApi)
            {
                _controller = XApi;
            }

            public Task<MyRequesterClass> Method1([FromBody]HttpRequestMessage req)
            {
                return _controller.Method1(SetHttpRequest);
            }

            public Task<MyDiffRequesterClass> Method2([FromBody]HttpRequestMessage req)
            {
                return _controller. Method2(SetHttpRequest);
            }
        }

控制器正在捕获来自 Request 对象的请求,但是;现在 Request 对象必须通过路由器服务才能在这种情况下为 null 的未来。因此我们可以实现读取XML的代码如下

 Stream reqBody = Request?.Body;
        if (Request == null)
            reqBody = (MyRequesterClass as HttpRequest).Body;

我们来客户端,写一个简单的框架控制台项目

通常我们提供wsdl visual studio 添加引用通过添加代理可以创建和遍历的部分。 (推荐的情况)但在我的情况下,我决定使用 webclient post xml 因为我使用用户证书并且我不知道要发送的对象类型。以下示例使用:

static string xml = " <ReqObj xmlns='http://tempuri.org/'>"
                + "</ ReqObj >";

        static string wrapbody = @"<?xml version=""1.0"" encoding=""utf-8""?>
                    <soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"" 
                        xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" 
                        xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
                        <soap:Body>
                           #
                        </soap:Body>
                    </soap:Envelope>";

        public static async Task<string> CreateSoapEnvelope()
        {
            HttpResponseMessage response = await PostXmlRequest("https://localhostorliveaddress.com");
            string content = await response.Content.ReadAsStringAsync();

            return content;
        }

        public static async Task<HttpResponseMessage> PostXmlRequest(string baseUrl)
        {
            X509Certificate2 clientCert = new X509Certificate2(@"D:\ccer\xxx.pfx", "password");
            //X509Certificate2 clientCert = GetClientCertificate();
            WebRequestHandler requestHandler = new WebRequestHandler();
            requestHandler.ClientCertificates.Add(clientCert);

            using (var httpClient = new HttpClient(requestHandler))
            {
                string wrpXmlContent = wrapbody.Replace("#", xml);
                var httpContent = new StringContent(wrpXmlContent, Encoding.UTF8, "text/xml");
                httpContent.Headers.Add("SOAPAction", "https://localhostorliveaddress.com/method1");
                httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml"));

                return await httpClient.PostAsync(baseUrl, httpContent);
            }
        }

从我的用户个人商店获取客户端证书

private static X509Certificate2 GetClientCertificate()
    {
        X509Store userCaStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
        try
        {
            userCaStore.Open(OpenFlags.ReadOnly);
            X509Certificate2Collection certificatesInStore = userCaStore.Certificates;
            X509Certificate2Collection findResult = certificatesInStore.
                Find(X509FindType.FindBySubjectName, "XRootCertificateOnMyUserPersonelStore", true);
            X509Certificate2 clientCertificate = null;

            if (findResult.Count == 1)
            {
                clientCertificate = findResult[0];
            }
            else
            {
                throw new Exception("Unable to locate the correct client certificate.");
            }
            return clientCertificate;
        }
        catch
        {
            throw;
        }
        finally
        {
            userCaStore.Close();
        }
    }