diff --git a/src/SoapCore.Tests/OperationDescription/OperationDescriptionTests.cs b/src/SoapCore.Tests/OperationDescription/OperationDescriptionTests.cs index 6375c327..ca6effdd 100644 --- a/src/SoapCore.Tests/OperationDescription/OperationDescriptionTests.cs +++ b/src/SoapCore.Tests/OperationDescription/OperationDescriptionTests.cs @@ -11,14 +11,14 @@ public class OperationDescriptionTests [Fact] public void TestProperUnrappingOfGenericResponses() { - ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContract)); - ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContract), new ServiceContractAttribute()); + ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContract), false); + ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContract), new ServiceContractAttribute(), false); System.Reflection.MethodInfo method = typeof(IServiceWithMessageContract).GetMethod(nameof(IServiceWithMessageContract.GetMyClass)); OperationContractAttribute contractAttribute = new OperationContractAttribute(); - ServiceModel.OperationDescription operationDescription = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute); + ServiceModel.OperationDescription operationDescription = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute, false); Assert.True(operationDescription.IsMessageContractResponse); } @@ -26,14 +26,14 @@ public void TestProperUnrappingOfGenericResponses() [Fact] public void TestSupportXmlRootForParameterName() { - ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContract)); - ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContract), new ServiceContractAttribute()); + ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContract), false); + ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContract), new ServiceContractAttribute(), false); System.Reflection.MethodInfo method = typeof(IServiceWithMessageContract).GetMethod(nameof(IServiceWithMessageContract.GetClassWithXmlRoot)); OperationContractAttribute contractAttribute = new OperationContractAttribute(); - ServiceModel.OperationDescription operationDescription = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute); + ServiceModel.OperationDescription operationDescription = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute, false); Assert.Equal("test", operationDescription.AllParameters.FirstOrDefault()?.Name); } @@ -41,14 +41,14 @@ public void TestSupportXmlRootForParameterName() [Fact] public void TestSupportXmlRootForParameterNameWithEmptyStringAsRootElementNameUsesParameterInfoToExtractAName() { - ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContractAndEmptyXmlRoot)); - ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContractAndEmptyXmlRoot), new ServiceContractAttribute()); + ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContractAndEmptyXmlRoot), false); + ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContractAndEmptyXmlRoot), new ServiceContractAttribute(), false); System.Reflection.MethodInfo method = typeof(IServiceWithMessageContractAndEmptyXmlRoot).GetMethod(nameof(IServiceWithMessageContractAndEmptyXmlRoot.GetClassWithEmptyXmlRoot)); OperationContractAttribute contractAttribute = new OperationContractAttribute(); - ServiceModel.OperationDescription operationDescription = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute); + ServiceModel.OperationDescription operationDescription = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute, false); Assert.Equal("classWithXmlRoot", operationDescription.AllParameters.FirstOrDefault()?.Name); } @@ -56,14 +56,14 @@ public void TestSupportXmlRootForParameterNameWithEmptyStringAsRootElementNameUs [Fact] public void TestProperUnrappingOfNonGenericResponses() { - ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContract)); - ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContract), new ServiceContractAttribute()); + ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContract), false); + ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContract), new ServiceContractAttribute(), false); System.Reflection.MethodInfo method = typeof(IServiceWithMessageContract).GetMethod(nameof(IServiceWithMessageContract.GetMyOtherClass)); OperationContractAttribute contractAttribute = new OperationContractAttribute(); - ServiceModel.OperationDescription operationDescription = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute); + ServiceModel.OperationDescription operationDescription = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute, false); Assert.True(operationDescription.IsMessageContractResponse); } @@ -71,14 +71,14 @@ public void TestProperUnrappingOfNonGenericResponses() [Fact] public void TestProperUnrappingOfNonMessageContractResponses() { - ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContract)); - ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContract), new ServiceContractAttribute()); + ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContract), false); + ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContract), new ServiceContractAttribute(), false); System.Reflection.MethodInfo method = typeof(IServiceWithMessageContract).GetMethod(nameof(IServiceWithMessageContract.GetMyStringClass)); OperationContractAttribute contractAttribute = new OperationContractAttribute(); - ServiceModel.OperationDescription operationDescription = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute); + ServiceModel.OperationDescription operationDescription = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute, false); Assert.False(operationDescription.IsMessageContractResponse); } @@ -86,14 +86,14 @@ public void TestProperUnrappingOfNonMessageContractResponses() [Fact] public void TestProperUnwrappingOfSoapFaults() { - ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContract)); - ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContract), new ServiceContractAttribute()); + ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContract), false); + ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContract), new ServiceContractAttribute(), false); System.Reflection.MethodInfo method = typeof(IServiceWithMessageContract).GetMethod(nameof(IServiceWithMessageContract.ThrowTypedFault)); OperationContractAttribute contractAttribute = new OperationContractAttribute(); - ServiceModel.OperationDescription operationDescription = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute); + ServiceModel.OperationDescription operationDescription = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute, false); var faultInfo = Assert.Single(operationDescription.Faults); Assert.Equal("TypedSoapFault", faultInfo.Name); @@ -106,17 +106,36 @@ public void TestProperUnwrappingOfSoapFaults() [Fact] public void TestProperNamingOfAsyncMethods() { - ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContract)); - ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContract), new ServiceContractAttribute()); + ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContract), false); + ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContract), new ServiceContractAttribute(), false); System.Reflection.MethodInfo method = typeof(IServiceWithMessageContract).GetMethod(nameof(IServiceWithMessageContract.GetMyAsyncClassAsync)); OperationContractAttribute contractAttribute = new OperationContractAttribute(); - ServiceModel.OperationDescription operationDescription = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute); + ServiceModel.OperationDescription operationDescription = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute, false); Assert.True(operationDescription.IsMessageContractResponse); Assert.Equal("GetMyAsyncClass", operationDescription.Name); } + + [Fact] + public void TestGeneratedSoapActionName() + { + ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithMessageContract), true); + ContractDescription contractDescription = new ContractDescription(serviceDescription, typeof(IServiceWithMessageContract), new ServiceContractAttribute(), true); + + System.Reflection.MethodInfo method = typeof(IServiceWithMessageContract).GetMethod(nameof(IServiceWithMessageContract.GetMyOtherClass)); + + OperationContractAttribute contractAttribute = new OperationContractAttribute(); + + ServiceModel.OperationDescription operationDescription1 = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute, true); + + Assert.Equal("http://tempuri.org/GetMyOtherClass", operationDescription1.SoapAction); + + ServiceModel.OperationDescription operationDescription2 = new ServiceModel.OperationDescription(contractDescription, method, contractAttribute, false); + + Assert.Equal("http://tempuri.org/IServiceWithMessageContract/GetMyOtherClass", operationDescription2.SoapAction); + } } } diff --git a/src/SoapCore.Tests/ServiceDescription/ServiceContractTests.cs b/src/SoapCore.Tests/ServiceDescription/ServiceContractTests.cs index 47897229..70568c45 100644 --- a/src/SoapCore.Tests/ServiceDescription/ServiceContractTests.cs +++ b/src/SoapCore.Tests/ServiceDescription/ServiceContractTests.cs @@ -11,7 +11,7 @@ public class ServiceContractTests [Fact] public void TestFallbackOfServiceNameToTypeName() { - ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithoutName)); + ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithoutName), false); Assert.Equal("IServiceWithoutName", serviceDescription.ServiceName); } @@ -19,7 +19,7 @@ public void TestFallbackOfServiceNameToTypeName() [Fact] public void TestExplicitlySetServiceName() { - ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithName)); + ServiceDescription serviceDescription = new ServiceDescription(typeof(IServiceWithName), false); Assert.Equal("MyServiceWithName", serviceDescription.ServiceName); } diff --git a/src/SoapCore.Tests/Wsdl/WsdlTests.cs b/src/SoapCore.Tests/Wsdl/WsdlTests.cs index 537c7b3a..9972abc3 100644 --- a/src/SoapCore.Tests/Wsdl/WsdlTests.cs +++ b/src/SoapCore.Tests/Wsdl/WsdlTests.cs @@ -1156,7 +1156,7 @@ private string GetWsdlFromAsmx() private async Task GetWsdlFromMetaBodyWriter(SoapSerializer serializer, string bindingName = null, string portName = null, bool useMicrosoftGuid = false) { - var service = new ServiceDescription(typeof(T)); + var service = new ServiceDescription(typeof(T), false); var baseUrl = "http://tempuri.org/"; var xmlNamespaceManager = Namespaces.CreateDefaultXmlNamespaceManager(useMicrosoftGuid); var defaultBindingName = !string.IsNullOrWhiteSpace(bindingName) ? bindingName : "BasicHttpBinding"; diff --git a/src/SoapCore/Extensibility/AuthorizeOperationMessageProcessor.cs b/src/SoapCore/Extensibility/AuthorizeOperationMessageProcessor.cs index 4cf2cf28..626d1e19 100644 --- a/src/SoapCore/Extensibility/AuthorizeOperationMessageProcessor.cs +++ b/src/SoapCore/Extensibility/AuthorizeOperationMessageProcessor.cs @@ -24,13 +24,20 @@ public class AuthorizeOperationMessageProcessor : ISoapMessageProcessor /// private readonly Dictionary _pathTypes; + /// + /// Whether to generate the soapAction without the contract name. This is also specified in the SoapOptions. + /// + private readonly bool _generateSoapActionWithoutContractName; + /// /// Initializes a new instance of the class. /// /// A dictionary that has the path of the endpoint as Key and the corresponding type as Value. Similar to using the UseSoapEndpoint extension function. - public AuthorizeOperationMessageProcessor(Dictionary pathAndTypes) + /// Whether to generate the soapAction without the contract name. Default is false. + public AuthorizeOperationMessageProcessor(Dictionary pathAndTypes, bool generateSoapActionWithoutContractName = false) { _pathTypes = pathAndTypes; + _generateSoapActionWithoutContractName = generateSoapActionWithoutContractName; } public async Task ProcessMessage(Message requestMessage, HttpContext httpContext, Func> next) @@ -43,7 +50,7 @@ public async Task ProcessMessage(Message requestMessage, HttpContext ht throw new ArgumentException("Unable to handle request without a valid action parameter. Please supply a valid soap action."); } - if (!_pathTypes.TryGetValue(httpContext.Request.Path.Value.ToLowerInvariant(), out Type serviceType) || !TryGetOperation(soapAction, serviceType, out var operation)) + if (!_pathTypes.TryGetValue(httpContext.Request.Path.Value.ToLowerInvariant(), out Type serviceType) || !TryGetOperation(soapAction, serviceType, _generateSoapActionWithoutContractName, out var operation)) { throw new InvalidOperationException($"No operation found for specified action: {soapAction}"); } @@ -141,9 +148,9 @@ public async Task ProcessMessage(Message requestMessage, HttpContext ht } } - private static bool TryGetOperation(string methodName, Type serviceType, out OperationDescription operation) + private static bool TryGetOperation(string methodName, Type serviceType, bool generateSoapActionWithoutContractName, out OperationDescription operation) { - var service = new ServiceDescription(serviceType); + var service = new ServiceDescription(serviceType, generateSoapActionWithoutContractName); operation = service.Operations.FirstOrDefault(o => o.SoapAction.Equals(methodName, StringComparison.Ordinal) || o.Name.Equals(HeadersHelper.GetTrimmedSoapAction(methodName), StringComparison.Ordinal) || methodName.Equals(HeadersHelper.GetTrimmedSoapAction(o.Name), StringComparison.Ordinal)); diff --git a/src/SoapCore/ServiceModel/ContractDescription.cs b/src/SoapCore/ServiceModel/ContractDescription.cs index 4e1a70ca..546cc254 100644 --- a/src/SoapCore/ServiceModel/ContractDescription.cs +++ b/src/SoapCore/ServiceModel/ContractDescription.cs @@ -7,7 +7,7 @@ namespace SoapCore.ServiceModel { public class ContractDescription { - public ContractDescription(ServiceDescription service, Type contractType, ServiceContractAttribute attribute) + public ContractDescription(ServiceDescription service, Type contractType, ServiceContractAttribute attribute, bool generateSoapActionWithoutContractName) { Service = service; ContractType = contractType; @@ -20,7 +20,7 @@ public ContractDescription(ServiceDescription service, Type contractType, Servic { foreach (var operationContract in operationMethodInfo.GetCustomAttributes()) { - operations.Add(new OperationDescription(this, operationMethodInfo, operationContract)); + operations.Add(new OperationDescription(this, operationMethodInfo, operationContract, generateSoapActionWithoutContractName)); } } diff --git a/src/SoapCore/ServiceModel/OperationDescription.cs b/src/SoapCore/ServiceModel/OperationDescription.cs index 6e19adf4..5af21dd8 100644 --- a/src/SoapCore/ServiceModel/OperationDescription.cs +++ b/src/SoapCore/ServiceModel/OperationDescription.cs @@ -12,11 +12,24 @@ namespace SoapCore.ServiceModel { public class OperationDescription { - public OperationDescription(ContractDescription contract, MethodInfo operationMethod, OperationContractAttribute contractAttribute) + public OperationDescription(ContractDescription contract, MethodInfo operationMethod, OperationContractAttribute contractAttribute, bool generateSoapActionWithoutContractName) { Contract = contract; Name = contractAttribute.Name ?? GetNameByAction(contractAttribute.Action) ?? GetNameByMethod(operationMethod); - SoapAction = contractAttribute.Action ?? $"{contract.Namespace.TrimEnd('/')}/{contract.Name}/{Name}"; + + if (contractAttribute.Action != null) + { + SoapAction = contractAttribute.Action; + } + else if (generateSoapActionWithoutContractName) + { + SoapAction = $"{contract.Namespace.TrimEnd('/')}/{Name}"; + } + else + { + SoapAction = $"{contract.Namespace.TrimEnd('/')}/{contract.Name}/{Name}"; + } + IsOneWay = contractAttribute.IsOneWay; DispatchMethod = operationMethod; diff --git a/src/SoapCore/ServiceModel/ServiceDescription.cs b/src/SoapCore/ServiceModel/ServiceDescription.cs index 4822f0d1..16d0c4ec 100644 --- a/src/SoapCore/ServiceModel/ServiceDescription.cs +++ b/src/SoapCore/ServiceModel/ServiceDescription.cs @@ -8,7 +8,7 @@ namespace SoapCore.ServiceModel { public class ServiceDescription { - public ServiceDescription(Type serviceType) + public ServiceDescription(Type serviceType, bool generateSoapActionWithoutContractName) { ServiceType = serviceType; ServiceKnownTypes = serviceType.GetCustomAttributes(inherit: false); @@ -21,7 +21,7 @@ public ServiceDescription(Type serviceType) { foreach (var serviceContract in contractType.GetTypeInfo().GetCustomAttributes()) { - var contractDescription = new ContractDescription(this, contractType, serviceContract); + var contractDescription = new ContractDescription(this, contractType, serviceContract, generateSoapActionWithoutContractName); contracts.Add(contractDescription); diff --git a/src/SoapCore/SoapCoreOptions.cs b/src/SoapCore/SoapCoreOptions.cs index 74e98875..ed1654e8 100644 --- a/src/SoapCore/SoapCoreOptions.cs +++ b/src/SoapCore/SoapCoreOptions.cs @@ -136,5 +136,12 @@ public class SoapCoreOptions /// Sets additional namespace declaration attributes in envelope /// public Dictionary AdditionalEnvelopeXmlnsAttributes { get; set; } + + /// + /// By default, the soapAction that is generated if not explicitely specified is + /// {namespace}/{contractName}/{methodName}. If set to true, the service name will + /// be omitted, so that the soapAction will be {namespace}/{methodName}. + /// + public bool GenerateSoapActionWithoutContractName { get; set; } = false; } } diff --git a/src/SoapCore/SoapEndpointMiddleware.cs b/src/SoapCore/SoapEndpointMiddleware.cs index afa0e7d6..942e279c 100644 --- a/src/SoapCore/SoapEndpointMiddleware.cs +++ b/src/SoapCore/SoapEndpointMiddleware.cs @@ -65,7 +65,7 @@ public SoapEndpointMiddleware(ILogger> logger, _serializerHelper = new SerializerHelper(options.SoapSerializer); _pathComparisonStrategy = options.CaseInsensitivePath ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - _service = new ServiceDescription(options.ServiceType); + _service = new ServiceDescription(options.ServiceType, options.GenerateSoapActionWithoutContractName); if (options.EncoderOptions is null) { diff --git a/src/SoapCore/SoapOptions.cs b/src/SoapCore/SoapOptions.cs index 660e2eb8..e55b67cd 100644 --- a/src/SoapCore/SoapOptions.cs +++ b/src/SoapCore/SoapOptions.cs @@ -70,6 +70,8 @@ public class SoapOptions public WsdlFileOptions WsdlFileOptions { get; set; } public Dictionary AdditionalEnvelopeXmlnsAttributes { get; set; } + public bool GenerateSoapActionWithoutContractName { get; set; } = false; + [Obsolete] public static SoapOptions FromSoapCoreOptions(SoapCoreOptions opt) { @@ -99,7 +101,8 @@ public static SoapOptions FromSoapCoreOptions(SoapCoreOptions opt, Type serviceT WsdlFileOptions = opt.WsdlFileOptions, AdditionalEnvelopeXmlnsAttributes = opt.AdditionalEnvelopeXmlnsAttributes, CheckXmlCharacters = opt.CheckXmlCharacters, - UseMicrosoftGuid = opt.UseMicrosoftGuid + UseMicrosoftGuid = opt.UseMicrosoftGuid, + GenerateSoapActionWithoutContractName = opt.GenerateSoapActionWithoutContractName, }; #pragma warning disable CS0612 // Type or member is obsolete