Как вернуть пользовательский код состояния http CoreWCF?
Я беру на себя устаревший проект SOAP, в котором мне нужно заменить службу SOAP решением .NET Core. Мы не можем изменить клиент SOAP, поэтому мы не можем смотреть на REST, GRPC и т. д. Я просмотрел SoapCore и CoreWCF, и оба работают с демонстрацией аутентификации имени пользователя и пароля заголовка SOAP, однако я собираюсь использовать CoreWCF для в настоящее время.
Существующая служба SOAP использует настраиваемые ответы кода состояния http, например, 202 возвращается после обработки службы и в некоторых ситуациях, когда возникает ошибка SOAP. Я понимаю, что это неправильно, однако нам нужно поддерживать существующую бизнес-логику.
Мои вопросы:
- Как я могу настроить свою службу для ответа с кодом состояния http 202 после завершения службы или при выполнении определенного условия? IsOneWay=True OperationContract не будет работать, так как это немедленно возвращается.
- Как мне настроить ошибку SOAP, чтобы она отвечала настраиваемым кодом состояния http?
Есть много старых сообщений SO, в которых упоминается WebOperationContext, однако я не могу получить к нему доступ в своей службе. OperationContext, похоже, не контролирует HttpStatusCode. Может быть, я что-то упускаю. то есть:
WebOperationContext ctx = WebOperationContext.Current;
ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
Вот мой пример разбивки проекта:
Программа.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>();
}
}
Startup.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;
});
}
}
}
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);
}
}
Demo.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}";
}
}
}
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);
}
}
}
Заранее большое спасибо за любую помощь. ваше здоровье.
2 ответа
CoreWCF имеет собственный WebOperationContext в пакете nuget CoreWCF.WebHttp, и вы можете установить там исходящий код состояния, чтобы вернуть собственный код состояния, который вы можете просто сделать
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.YourDesiredStatusCode;
OutgoingResponse StatusCode — это место, где вы устанавливаете код ответа, но это не целочисленное значение.
Если вы все еще хотите использовать его, попробуйте режим совместимости с ASP.NET. Развертывание должно быть в IIS.