Last active
March 16, 2020 21:24
-
-
Save jahav/48453c80e36e30a78d90eafb94308b42 to your computer and use it in GitHub Desktop.
A demonstration of SoapCore filtering capabilities
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using FluentValidation; | |
using FluentValidation.Results; | |
using Microsoft.AspNetCore.Builder; | |
using Microsoft.AspNetCore.Hosting; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Mvc; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Hosting; | |
using SoapCore; | |
using SoapCore.Extensibility; | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Runtime.Serialization; | |
using System.ServiceModel; | |
namespace SoapValidationFilter | |
{ | |
public class User | |
{ | |
public string GivenName { get; set; } | |
public string Surname { get; set; } | |
} | |
[ServiceContract] | |
public interface IGreeterService | |
{ | |
[OperationContract] | |
[FaultContract(typeof(SoapValidationResult))] | |
[ServiceFilter(typeof(DontGreetDicatatorsActionFilter))] | |
string GreetMe(User user); | |
} | |
public class GreeterService : IGreeterService | |
{ | |
public string GreetMe(User user) | |
{ | |
return $"Hello {user.GivenName} {user.Surname}"; | |
} | |
} | |
class SoapModelBindingValidator<T> : IModelBindingFilter | |
{ | |
private readonly List<Type> _validatedType; | |
private readonly IValidator<T> _validator; | |
public SoapModelBindingValidator(IValidator<T> validator) | |
{ | |
_validator = validator; | |
_validatedType = new List<Type> { typeof(T) }; | |
} | |
public List<Type> ModelTypes | |
{ | |
get => _validatedType; | |
set => throw new InvalidOperationException(); | |
} | |
public void OnModelBound(object model, IServiceProvider serviceProvider, out object output) | |
{ | |
output = null; | |
var validationResult = _validator.Validate(model); | |
if (!validationResult.IsValid) | |
{ | |
throw new FaultException<SoapValidationResult>( | |
new SoapValidationResult(validationResult.Errors), | |
new FaultReason("Validation failed."), | |
code: null, | |
action: null); | |
} | |
} | |
} | |
public class UserValidator : AbstractValidator<User> | |
{ | |
public UserValidator() | |
{ | |
RuleFor(x => x.GivenName).NotEmpty(); | |
RuleFor(x => x.Surname).NotEmpty(); | |
} | |
} | |
/// Not really a public API, no interface, but it is there and even has unit tests. Called after model binding filters | |
public class DontGreetDicatatorsActionFilter | |
{ | |
public void OnSoapActionExecuting(string operationName, object[] allArgs, HttpContext httpContext, object result) | |
{ | |
if (operationName != nameof(IGreeterService.GreetMe) || allArgs.Length != 1) | |
return; | |
if (allArgs[0] is User user && user.GivenName == "Lucius" && user.Surname == "Sulla") | |
{ | |
throw new ArgumentException("Service is not obligated to greet everyone."); | |
} | |
} | |
} | |
// Keep name in the contract same as name of the class, otherwise SoapCore generates invalid WSDL (in one place, it uses name of the class, in other name of contract) | |
[DataContract(Name = nameof(SoapValidationResult), Namespace = _namespace)] | |
public class SoapValidationResult | |
{ | |
private const string _namespace = "https://tempuri.org/SoapCoreValidation"; | |
public SoapValidationResult(IEnumerable<ValidationFailure> failures) | |
{ | |
Failures = failures.Select(x => new SoapValidationFailure(x)).ToArray(); | |
} | |
[DataMember] | |
public SoapValidationFailure[] Failures { get; set; } | |
[DataContract(Name = nameof(SoapValidationFailure), Namespace = _namespace)] | |
public class SoapValidationFailure | |
{ | |
public SoapValidationFailure(ValidationFailure result) | |
{ | |
PropertyName = result.PropertyName; | |
ErrorMessage = result.ErrorMessage; | |
Severity = result.Severity.ToString(); | |
} | |
[DataMember] | |
public string PropertyName { get; set; } | |
[DataMember] | |
public string ErrorMessage { get; set; } | |
[DataMember] | |
public string Severity { get; set; } | |
} | |
} | |
public class Startup | |
{ | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
services.AddSingleton<IValidator<User>, UserValidator>(); | |
services.AddSingleton<IModelBindingFilter, SoapModelBindingValidator<User>>(); | |
services.AddSingleton<DontGreetDicatatorsActionFilter>(); | |
services.AddSingleton<GreeterService>(); | |
services.AddSoapCore(); | |
services.AddMvc(); | |
} | |
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | |
{ | |
app.UseRouting(); | |
app.UseEndpoints(endpoints => | |
{ | |
endpoints.UseSoapEndpoint<GreeterService>("/ServicePath.svc", new BasicHttpBinding()); | |
}); | |
} | |
} | |
public class Program | |
{ | |
public static void Main(string[] args) | |
{ | |
CreateHostBuilder(args).Build().Run(); | |
} | |
public static IHostBuilder CreateHostBuilder(string[] args) => | |
Host.CreateDefaultBuilder(args) | |
.ConfigureWebHostDefaults(webBuilder => | |
{ | |
webBuilder.UseStartup<Startup>(); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment