如何返回自定义http状态代码CoreWCF?

Dol*_*lph 3 .net c# wcf soap corewcf

我正在接管一个遗留的 SOAP 项目,我需要用 .NET Core 解决方案替换 SOAP 服务。我们不能更改 SOAP 客户端,因此我们不能查看 REST、GRPC 等。我查看了 SoapCore 和 CoreWCF,并且都使用 SOAP 标头用户名/密码身份验证演示,但是,我将使用 CoreWCF暂时。

现有的SOAP服务使用自定义的http状态码响应,例如在服务处理完成后以及在某些情况下出现SOAP错误时返回202。我意识到这是不正确的,但是,我们需要维护现有的业务逻辑。

我的问题是:

  1. 如何将我的服务配置为在服务完成后或满足特定条件时响应 http 状态代码 202?IsOneWay=True OperationContract 将不起作用,因为它会立即返回。
  2. 如何配置 SOAP 错误以使用自定义 http 状态代码进行响应?

有许多旧的 SO 帖子提到 WebOperationContext,但是,我似乎无法在我的服务中访问它。OperationContext 似乎无法控制 HttpStatusCode。也许我错过了一些东西。IE:

WebOperationContext ctx = WebOperationContext.Current;
ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
Run Code Online (Sandbox Code Playgroud)

这是我的示例项目细分:

程序.cs

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using System.Diagnostics;

namespace CoreWcf.Samples.Http
{
    public class Program
    {
        public const int HTTP_PORT = 8088;
        public const int HTTPS_PORT = 8443;

        static void Main(string[] args)
        {
            IWebHost host = CreateWebHostBuilder(args).Build();
            host.Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
           WebHost.CreateDefaultBuilder(args)
            .UseKestrel(options =>
            {
                options.ListenLocalhost(HTTP_PORT);
                options.ListenLocalhost(HTTPS_PORT, listenOptions =>
                {
                    listenOptions.UseHttps();
                    if (Debugger.IsAttached)
                    {
                        listenOptions.UseConnectionLogging();
                    }
                });
            })
            .UseStartup<BasicHttpBindingStartup>();
    }
}
Run Code Online (Sandbox Code Playgroud)

启动.cs

using CoreWCF;
using CoreWCF.Configuration;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using System;

namespace CoreWcf.Samples.Http
{
    public class BasicHttpBindingStartup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            //Enable CoreWCF Services, with metadata (WSDL) support
            services.AddServiceModelServices()
                .AddServiceModelMetadata();
        }

        public void Configure(IApplicationBuilder app)
        {
            var wsHttpBindingWithCredential = new BasicHttpBinding(CoreWCF.Channels.BasicHttpSecurityMode.TransportWithMessageCredential);
            wsHttpBindingWithCredential.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

            app.UseServiceModel(builder =>
            {
                // Add the Demo Service
                builder.AddService<DemoService>(serviceOptions =>
                {
                    // Set a base address for all bindings to the service, and WSDL discovery
                    serviceOptions.BaseAddresses.Add(new Uri($"http://localhost:{Program.HTTP_PORT}/DemoService"));
                    serviceOptions.BaseAddresses.Add(new Uri($"https://localhost:{Program.HTTPS_PORT}/DemoService"));
                })
                // Add BasicHttpBinding endpoint
                .AddServiceEndpoint<DemoService, IDemo>(wsHttpBindingWithCredential, "/wsHttpUserPassword", ep => { ep.Name = "AuthenticatedDemoEP"; });

                builder.ConfigureServiceHostBase<DemoService>(CustomUserNamePasswordValidator.AddToHost);

                // Configure WSDL to be available over http & https
                var serviceMetadataBehavior = app.ApplicationServices.GetRequiredService<CoreWCF.Description.ServiceMetadataBehavior>();
                serviceMetadataBehavior.HttpGetEnabled = serviceMetadataBehavior.HttpsGetEnabled = true;
            });
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

IDemo.cs(服务接口):

using CoreWCF;

namespace CoreWcf.Samples.Http
{
    // Define a service contract.
    [ServiceContract]
    public interface IDemo
    {
        //[OperationContract(IsOneWay = true)]
        [OperationContract]
        string DemoRequest(string tagid, string readerid, string datetime);
    }
}
Run Code Online (Sandbox Code Playgroud)

演示.cs(服务):

using CoreWCF.Channels;
using Microsoft.AspNetCore.Http;
using System.Net;


namespace CoreWcf.Samples.Http
{
    public class DemoService : IDemo
    {
        public string DemoRequest(string tagid, string readerid, string datetime)
        {
            
            return $"Received tagid: {tagid}; readerid: {readerid};  datetime: {datetime}";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

CustomUserNamePasswordValidator.cs:

using CoreWCF;
using System.Threading.Tasks;

namespace CoreWcf.Samples.Http
{
    internal class CustomUserNamePasswordValidator : CoreWCF.IdentityModel.Selectors.UserNamePasswordValidator
    {
        public override ValueTask ValidateAsync(string userName, string password)
        {
            bool valid = userName.ToLowerInvariant().EndsWith("valid")
                && password.ToLowerInvariant().EndsWith("valid");
            if (!valid)
            {
                throw new FaultException("Unknown Username or Incorrect Password");
            }
            return new ValueTask();
        }

        public static void AddToHost(ServiceHostBase host)
        {
            var srvCredentials = new CoreWCF.Description.ServiceCredentials();
            srvCredentials.UserNameAuthentication.UserNamePasswordValidationMode = CoreWCF.Security.UserNamePasswordValidationMode.Custom;
            srvCredentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNamePasswordValidator();
            host.Description.Behaviors.Add(srvCredentials);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

非常感谢您的帮助。干杯。

小智 5

CoreWCF 在 nuget 包 CoreWCF.WebHttp 中有它自己的WebOperationContext,您可以在那里设置传出 StatusCode,以便返回自定义状态代码,您可以这样做

WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.YourDesiredStatusCode;