Last active
April 13, 2021 21:47
-
-
Save mjrousos/fc6eed8ba87fe33d86a3cc19002d6c89 to your computer and use it in GitHub Desktop.
CustomMiddleware
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
// The middleware delegate to call after this one finishes processing | |
private readonly RequestDelegate _next; | |
public SOAPEndpointMiddleware(RequestDelegate next) | |
{ | |
_next = next; | |
} |
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
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) | |
{ | |
loggerFactory.AddConsole(Configuration.GetSection("Logging")); | |
loggerFactory.AddDebug(); | |
app.UseMiddleware<SOAPEndpointMiddleware>(); | |
// This call to use MVC middleware from the project template is not necessary, but | |
// doesn't hurt anything so long as it comes after our UseMiddleware call. | |
app.UseMvc(); | |
} |
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
public class ContractDescription | |
{ | |
public ServiceDescription Service { get; private set; } | |
public string Name { get; private set; } | |
public string Namespace { get; private set; } | |
public Type ContractType { get; private set; } | |
public IEnumerable<OperationDescription> Operations { get; private set; } | |
public ContractDescription(ServiceDescription service, Type contractType, ServiceContractAttribute attribute) | |
{ | |
Service = service; | |
ContractType = contractType; | |
Namespace = attribute.Namespace ?? "http://tempuri.org/"; // Namespace defaults to http://tempuri.org/ | |
Name = attribute.Name ?? ContractType.Name; // Name defaults to the type name | |
var operations = new List<OperationDescription>(); | |
foreach (var operationMethodInfo in ContractType.GetTypeInfo().DeclaredMethods) | |
{ | |
foreach (var operationContract in operationMethodInfo.GetCustomAttributes<OperationContractAttribute>()) | |
{ | |
operations.Add(new OperationDescription(this, operationMethodInfo, operationContract)); | |
} | |
} | |
Operations = operations; | |
} | |
} |
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
private object[] GetRequestArguments(Message requestMessage, OperationDescription operation) | |
{ | |
var parameters = operation.DispatchMethod.GetParameters(); | |
var arguments = new List<object>(); | |
// Deserialize request wrapper and object | |
using (var xmlReader = requestMessage.GetReaderAtBodyContents()) | |
{ | |
// Find the element for the operation's data | |
xmlReader.ReadStartElement(operation.Name, operation.Contract.Namespace); | |
for (int i = 0; i < parameters.Length; i++) | |
{ | |
var parameterName = parameters[i].GetCustomAttribute<MessageParameterAttribute>()?.Name ?? parameters[i].Name; | |
xmlReader.MoveToStartElement(parameterName, operation.Contract.Namespace); | |
if (xmlReader.IsStartElement(parameterName, operation.Contract.Namespace)) | |
{ | |
var serializer = new DataContractSerializer(parameters[i].ParameterType, parameterName, operation.Contract.Namespace); | |
arguments.Add(serializer.ReadObject(xmlReader, verifyObjectName: true)); | |
} | |
} | |
} | |
return arguments.ToArray(); | |
} |
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
public async Task Invoke(HttpContext httpContext) | |
{ | |
Console.WriteLine($"Request for {httpContext.Request.Path} received ({httpContext.Request.ContentLength ?? 0} bytes)"); | |
// Call the next middleware delegate in the pipeline | |
await _next.Invoke(httpContext); | |
} |
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 CustomMiddleware; | |
namespace Microsoft.AspNetCore.Builder | |
{ | |
// Extension method used to add the middleware to the HTTP request pipeline. | |
public static class SOAPEndpointExtensions | |
{ | |
public static IApplicationBuilder UseSOAPEndpoint(this IApplicationBuilder builder) | |
{ | |
return builder.UseMiddleware<SOAPEndpointMiddleware>(); | |
} | |
} | |
} |
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
public class OperationDescription | |
{ | |
public ContractDescription Contract { get; private set; } | |
public string SoapAction { get; private set; } | |
public string ReplyAction { get; private set; } | |
public string Name { get; private set; } | |
public MethodInfo DispatchMethod { get; private set; } | |
public bool IsOneWay { get; private set; } | |
public OperationDescription(ContractDescription contract, MethodInfo operationMethod, OperationContractAttribute contractAttribute) | |
{ | |
Contract = contract; | |
Name = contractAttribute.Name ?? operationMethod.Name; | |
SoapAction = contractAttribute.Action ?? $"{contract.Namespace.TrimEnd('/')}/{contract.Name}/{Name}"; | |
IsOneWay = contractAttribute.IsOneWay; | |
ReplyAction = contractAttribute.ReplyAction; | |
DispatchMethod = operationMethod; | |
} | |
} |
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
POST http://localhost:5000/CalculatorService.svc HTTP/1.1 | |
Cache-Control: no-cache, max-age=0 | |
Content-Type: text/xml; charset=utf-8 | |
Accept-Encoding: gzip, deflate | |
SOAPAction: "http://tempuri.org/ICalculatorService/Add" | |
Connection: Keep-Alive | |
Content-Length: 183 | |
Host: localhost:5000 | |
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> | |
<s:Body> | |
<Add xmlns="http://tempuri.org/"> | |
<x>17.078485198821166</x> | |
<y>12.667884180633298</y> | |
</Add> | |
</s:Body> | |
</s:Envelope> |
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
HTTP/1.1 200 OK | |
Date: Tue, 15 Mar 2016 19:39:05 GMT | |
Content-Type: text/xml; charset=utf-8 | |
Server: Kestrel | |
Content-Length: 231 | |
<?xml version="1.0" encoding="utf-8"?> | |
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> | |
<s:Body> | |
<AddResponse xmlns="http://tempuri.org/"> | |
<AddResult>30.589212379692686</AddResult> | |
</AddResponse> | |
</s:Body> | |
</s:Envelope> |
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
public class ServiceBodyWriter : BodyWriter | |
{ | |
string ServiceNamespace; | |
string EnvelopeName; | |
string ResultName; | |
object Result; | |
public ServiceBodyWriter(string serviceNamespace, string envelopeName, string resultName, object result) : base(isBuffered: true) | |
{ | |
ServiceNamespace = serviceNamespace; | |
EnvelopeName = envelopeName; | |
ResultName = resultName; | |
Result = result; | |
} | |
protected override void OnWriteBodyContents(XmlDictionaryWriter writer) | |
{ | |
writer.WriteStartElement(EnvelopeName, ServiceNamespace); | |
var serializer = new DataContractSerializer(Result.GetType(), ResultName, ServiceNamespace); | |
serializer.WriteObject(writer, Result); | |
writer.WriteEndElement(); | |
} | |
} |
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
public class ServiceDescription | |
{ | |
public Type ServiceType { get; private set; } | |
public IEnumerable<ContractDescription> Contracts { get; private set; } | |
public IEnumerable<OperationDescription> Operations => Contracts.SelectMany(c => c.Operations); | |
public ServiceDescription(Type serviceType) | |
{ | |
ServiceType = serviceType; | |
var contracts = new List<ContractDescription>(); | |
foreach (var contractType in ServiceType.GetInterfaces()) | |
{ | |
foreach (var serviceContract in contractType.GetTypeInfo().GetCustomAttributes<ServiceContractAttribute>()) | |
{ | |
contracts.Add(new ContractDescription(this, contractType, serviceContract)); | |
} | |
} | |
Contracts = contracts; | |
} | |
} |
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
// The middleware delegate to call after this one finishes processing | |
private readonly RequestDelegate _next; | |
private readonly Type _serviceType; | |
private readonly string _endpointPath; | |
private readonly MessageEncoder _messageEncoder; | |
public SOAPEndpointMiddleware(RequestDelegate next, Type serviceType, string path, MessageEncoder encoder) | |
{ | |
_next = next; | |
_serviceType = serviceType; | |
_endpointPath = path; | |
_messageEncoder = encoder; | |
} |
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
private readonly ServiceDescription _service; | |
public SOAPEndpointMiddleware(RequestDelegate next, Type serviceType, string path, MessageEncoder encoder) | |
{ | |
_next = next; | |
_endpointPath = path; | |
_messageEncoder = encoder; | |
_service = new ServiceDescription(serviceType); | |
} |
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
public static IApplicationBuilder UseSOAPEndpoint<T>(this IApplicationBuilder builder, string path, MessageEncoder encoder) | |
{ | |
return builder.UseMiddleware<SOAPEndpointMiddleware>(typeof(T), path, encoder); | |
} |
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
public async Task Invoke(HttpContext httpContext) | |
{ | |
if (httpContext.Request.Path.Equals(_endpointPath, StringComparison.Ordinal)) | |
{ | |
// TODO : Reading message goes here | |
} | |
else | |
{ | |
await _next(httpContext); | |
} | |
} |
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
Message responseMessage; | |
// Read request message | |
var requestMessage = _messageEncoder.ReadMessage(httpContext.Request.Body, 0x10000, httpContext.Request.ContentType); | |
// TODO : Get requested action and invoke |
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
var soapAction = httpContext.Request.Headers["SOAPAction"].ToString().Trim('\"'); | |
if (!string.IsNullOrEmpty(soapAction)) | |
{ | |
requestMessage.Headers.Action = soapAction; | |
} | |
// TODO : Lookup operation and invoke |
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
var operation = _service.Operations.Where(o => o.SoapAction.Equals(requestMessage.Headers.Action, StringComparison.Ordinal)).FirstOrDefault(); | |
if (operation == null) | |
{ | |
throw new InvalidOperationException($"No operation found for specified action: {requestMessage.Headers.Action}"); | |
} | |
// TODO : Invoking the operation goes here |
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
// Get service type | |
var serviceInstance = serviceProvider.GetService(_service.ServiceType); | |
// Get operation arguments from message | |
var arguments = GetRequestArguments(requestMessage, operation); | |
// Invoke Operation method | |
var responseObject = operation.DispatchMethod.Invoke(serviceInstance, arguments.ToArray()); | |
// TODO : Encode responseObject into the response message |
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
// Create response message | |
var resultName = operation.DispatchMethod.ReturnParameter.GetCustomAttribute<MessageParameterAttribute>()?.Name ?? operation.Name + "Result"; | |
var bodyWriter = new ServiceBodyWriter(operation.Contract.Namespace, operation.Name + "Response", resultName, responseObject); | |
responseMessage = Message.CreateMessage(_messageEncoder.MessageVersion, operation.ReplyAction, bodyWriter); | |
httpContext.Response.ContentType = httpContext.Request.ContentType; // _messageEncoder.ContentType; | |
httpContext.Response.Headers["SOAPAction"] = responseMessage.Headers.Action; | |
_messageEncoder.WriteMessage(responseMessage, httpContext.Response.Body); |
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 System.ServiceModel; | |
namespace TestApp | |
{ | |
public class CalculatorService : ICalculatorService | |
{ | |
public double Add(double x, double y) => x + y; | |
public double Divide(double x, double y) => x / y; | |
public double Multiply(double x, double y) => x * y; | |
public double Subtract(double x, double y) => x - y; | |
} | |
[ServiceContract] | |
public interface ICalculatorService | |
{ | |
[OperationContract] double Add(double x, double y); | |
[OperationContract] double Subtract(double x, double y); | |
[OperationContract] double Multiply(double x, double y); | |
[OperationContract] double Divide(double x, double y); | |
} | |
} |
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 System; | |
using System.ServiceModel; | |
using System.ServiceModel.Channels; | |
namespace TestClient | |
{ | |
public class Program | |
{ | |
public static void Main(string[] args) | |
{ | |
if (args.Length < 1) | |
{ | |
Console.WriteLine("Please provide the remote URL of the calculator service"); | |
return; | |
} | |
// Create random inputs | |
Random numGen = new Random(); | |
double x = numGen.NextDouble() * 20; | |
double y = numGen.NextDouble() * 20; | |
var serviceAddress = $"{args[0]}/CalculatorService.svc"; | |
var client = new CalculatorServiceClient(new BasicHttpBinding(), new EndpointAddress(serviceAddress)); | |
Console.WriteLine($"{x} + {y} == {client.Add(x, y)}"); | |
Console.WriteLine($"{x} - {y} == {client.Subtract(x, y)}"); | |
Console.WriteLine($"{x} * {y} == {client.Multiply(x, y)}"); | |
Console.WriteLine($"{x} / {y} == {client.Divide(x, y)}"); | |
} | |
} | |
class CalculatorServiceClient : ClientBase<ICalculatorService> | |
{ | |
public CalculatorServiceClient(Binding binding, EndpointAddress remoteAddress) : base(binding, remoteAddress) { } | |
public double Add(double x, double y) => Channel.Add(x, y); | |
public double Subtract(double x, double y) => Channel.Subtract(x, y); | |
public double Multiply(double x, double y) => Channel.Multiply(x, y); | |
public double Divide(double x, double y) => Channel.Divide(x, y); | |
} | |
[ServiceContract] | |
public interface ICalculatorService | |
{ | |
[OperationContract] | |
double Add(double x, double y); | |
[OperationContract] | |
double Subtract(double x, double y); | |
[OperationContract] | |
double Multiply(double x, double y); | |
[OperationContract] | |
double Divide(double x, double y); | |
} | |
} |
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
public static IApplicationBuilder UseSOAPEndpoint<T>(this IApplicationBuilder builder, string path, Binding binding) | |
{ | |
var encoder = binding.CreateBindingElements().Find<MessageEncodingBindingElement>()?.CreateMessageEncoderFactory().Encoder; | |
return builder.UseMiddleware<SOAPEndpointMiddleware>(typeof(T), path, encoder); | |
} |
does it work with complex types?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
very good