Skip to content

Instantly share code, notes, and snippets.

@jahav
Last active March 16, 2020 21:24
Show Gist options
  • Save jahav/48453c80e36e30a78d90eafb94308b42 to your computer and use it in GitHub Desktop.
Save jahav/48453c80e36e30a78d90eafb94308b42 to your computer and use it in GitHub Desktop.
A demonstration of SoapCore filtering capabilities
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