diff --git a/src/Meceqs.AspNetCore/Receiving/DefaultHttpRequestReader.cs b/src/Meceqs.AspNetCore/Receiving/DefaultHttpRequestReader.cs index a31ee2a..0ae3e67 100644 --- a/src/Meceqs.AspNetCore/Receiving/DefaultHttpRequestReader.cs +++ b/src/Meceqs.AspNetCore/Receiving/DefaultHttpRequestReader.cs @@ -31,7 +31,9 @@ public Envelope ConvertToEnvelope(HttpContext httpContext, Type messageType) var requestHeaders = httpContext.Request.GetTypedHeaders(); string contentType = requestHeaders.ContentType.MediaType.ToString(); - object message = _serializationProvider.Deserialize(contentType, messageType, httpContext.Request.Body); + var serializer = _serializationProvider.GetSerializer(messageType, contentType); + + object message = serializer.Deserialize(messageType, httpContext.Request.Body); Guid messageId = GetGuidHeader(requestHeaders.Headers, TransportHeaderNames.MessageId) ?? Guid.NewGuid(); diff --git a/src/Meceqs.AspNetCore/Receiving/DefaultHttpResponseWriter.cs b/src/Meceqs.AspNetCore/Receiving/DefaultHttpResponseWriter.cs index 31c0c2c..664e250 100644 --- a/src/Meceqs.AspNetCore/Receiving/DefaultHttpResponseWriter.cs +++ b/src/Meceqs.AspNetCore/Receiving/DefaultHttpResponseWriter.cs @@ -22,7 +22,7 @@ public void WriteResponse(object response, HttpContext httpContext) IEnumerable supportedContentTypes = GetSupportedContentTypes(httpContext); - ISerializer serializer = _serializationProvider.GetSerializer(supportedContentTypes); + ISerializer serializer = _serializationProvider.GetSerializer(response.GetType(), supportedContentTypes); httpContext.Response.ContentType = serializer.ContentType; diff --git a/src/Meceqs.AzureEventHubs/Internal/DefaultEventDataConverter.cs b/src/Meceqs.AzureEventHubs/Internal/DefaultEventDataConverter.cs index 68f68e6..c54d9d6 100644 --- a/src/Meceqs.AzureEventHubs/Internal/DefaultEventDataConverter.cs +++ b/src/Meceqs.AzureEventHubs/Internal/DefaultEventDataConverter.cs @@ -25,7 +25,7 @@ public EventData ConvertToEventData(Envelope envelope) { Guard.NotNull(envelope, nameof(envelope)); - ISerializer serializer = _serializationProvider.GetSerializer(envelope.Message.GetType()); + ISerializer serializer = _serializationProvider.GetSerializer(envelope.GetType()); byte[] serializedEnvelope = serializer.SerializeToByteArray(envelope); @@ -47,10 +47,11 @@ public Envelope ConvertToEnvelope(EventData eventData) string messageType = (string)eventData.Properties[TransportHeaderNames.MessageType]; Type envelopeType = _envelopeTypeLoader.LoadEnvelopeType(messageType); + ISerializer serializer = _serializationProvider.GetSerializer(envelopeType, contentType); using (var envelopeStream = new MemoryStream(eventData.Body.Array)) { - return (Envelope)_serializationProvider.Deserialize(contentType, envelopeType, envelopeStream); + return (Envelope)serializer.Deserialize(envelopeType, envelopeStream); } } } diff --git a/src/Meceqs.AzureServiceBus/Internal/DefaultServiceBusMessageConverter.cs b/src/Meceqs.AzureServiceBus/Internal/DefaultServiceBusMessageConverter.cs index 7a43174..a032ed5 100644 --- a/src/Meceqs.AzureServiceBus/Internal/DefaultServiceBusMessageConverter.cs +++ b/src/Meceqs.AzureServiceBus/Internal/DefaultServiceBusMessageConverter.cs @@ -25,7 +25,7 @@ public Message ConvertToServiceBusMessage(Envelope envelope) { Guard.NotNull(envelope, nameof(envelope)); - ISerializer serializer = _serializationProvider.GetSerializer(envelope.Message.GetType()); + ISerializer serializer = _serializationProvider.GetSerializer(envelope.GetType()); byte[] serializedEnvelope = serializer.SerializeToByteArray(envelope); @@ -50,15 +50,15 @@ public Envelope ConvertToEnvelope(Message serviceBusMessage) { Guard.NotNull(serviceBusMessage, nameof(serviceBusMessage)); - // TODO @cweiss validations? string contentType = serviceBusMessage.ContentType ?? (string)serviceBusMessage.UserProperties[TransportHeaderNames.ContentType]; string messageType = (string)serviceBusMessage.UserProperties[TransportHeaderNames.MessageType]; Type envelopeType = _envelopeTypeLoader.LoadEnvelopeType(messageType); + ISerializer serializer = _serializationProvider.GetSerializer(envelopeType, contentType); using (var envelopeStream = new MemoryStream(serviceBusMessage.Body)) { - return (Envelope)_serializationProvider.Deserialize(contentType, envelopeType, envelopeStream); + return (Envelope)serializer.Deserialize(envelopeType, envelopeStream); } } } diff --git a/src/Meceqs.HttpSender/HttpSenderMiddleware.cs b/src/Meceqs.HttpSender/HttpSenderMiddleware.cs index 88407d4..dea6fbc 100644 --- a/src/Meceqs.HttpSender/HttpSenderMiddleware.cs +++ b/src/Meceqs.HttpSender/HttpSenderMiddleware.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Threading.Tasks; @@ -65,10 +66,9 @@ public async Task Invoke(MessageContext context) string contentType = response.Content.Headers.ContentType?.ToString(); if (!string.IsNullOrEmpty(contentType)) { - if (!_serializationProvider.TryGetSerializer(contentType, out ISerializer serializer)) - { - throw new NotSupportedException($"ContentType '{contentType}' is not supported."); - } + var contentTypeList = new List { contentType }; + + var serializer = _serializationProvider.GetSerializer(context.ExpectedResponseType, contentTypeList); context.Response = serializer.Deserialize(context.ExpectedResponseType, await response.Content.ReadAsStreamAsync()); } diff --git a/src/Meceqs/Serialization/DefaultSerializationProvider.cs b/src/Meceqs/Serialization/DefaultSerializationProvider.cs index f5f7e22..c56aafe 100644 --- a/src/Meceqs/Serialization/DefaultSerializationProvider.cs +++ b/src/Meceqs/Serialization/DefaultSerializationProvider.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; -using System.IO; +using System.Linq; using Microsoft.Extensions.Options; namespace Meceqs.Serialization { public class DefaultSerializationProvider : ISerializationProvider { + private static readonly IEnumerable EmptyContentTypes = new List().AsReadOnly(); + private readonly IReadOnlyList _serializers; private readonly IReadOnlyList _supportedContentTypes; @@ -24,43 +26,6 @@ public DefaultSerializationProvider(IOptions options) _supportedContentTypes = GetSupportedContentTypes(_serializers); } - public ISerializer GetSerializer(Type objectType) - { - Guard.NotNull(objectType, nameof(objectType)); - - foreach (var serializer in _serializers) - { - if (serializer.CanSerializeType(objectType)) - return serializer; - } - - throw new InvalidOperationException($"No serializer found that can handle the type '{objectType.FullName}'."); - } - - public ISerializer GetSerializer(IEnumerable supportedContentTypes) - { - Guard.NotNull(supportedContentTypes, nameof(supportedContentTypes)); - - bool hasItems = false; - foreach (var supportedContentType in supportedContentTypes) - { - hasItems = true; - if (TryGetSerializer(supportedContentType, out ISerializer serializer)) - { - return serializer; - } - } - - if (hasItems) - { - throw new InvalidOperationException($"No serializer matching content types '{string.Join(",", supportedContentTypes)}'."); - } - else - { - throw new ArgumentException("The list may not be empty", nameof(supportedContentTypes)); - } - } - public IReadOnlyList GetSupportedContentTypes(Type objectType = null) { if (objectType == null) @@ -80,36 +45,69 @@ public IReadOnlyList GetSupportedContentTypes(Type objectType = null) return supportedContentTypes; } - public bool TryGetSerializer(string contentType, out ISerializer serializer) + public ISerializer GetSerializer(Type objectType) + { + return GetSerializer(objectType, EmptyContentTypes); + } + + public ISerializer GetSerializer(Type objectType, string supportedContentType) { - Guard.NotNull(contentType, nameof(contentType)); + Guard.NotNullOrWhiteSpace(supportedContentType, nameof(supportedContentType)); - serializer = null; + return GetSerializer(objectType, new List { supportedContentType }); + } - foreach (var supportedSerializer in _serializers) + public ISerializer GetSerializer(Type objectType, IEnumerable supportedContentTypes) + { + if (!TryGetSerializer(objectType, supportedContentTypes, out var serializer)) { - if (supportedSerializer.ContentType == contentType) - { - serializer = supportedSerializer; - return true; - } + throw new InvalidOperationException( + $"No serializer matches object type '{objectType.FullName}' " + + $"and content types '{string.Join(",", supportedContentTypes)}'."); } - return false; + return serializer; } - public object Deserialize(string contentType, Type objectType, Stream serializedObject) + public bool TryGetSerializer(Type objectType, out ISerializer serializer) + { + return TryGetSerializer(objectType, EmptyContentTypes, out serializer); + } + + public bool TryGetSerializer(Type objectType, string supportedContentType, out ISerializer serializer) + { + Guard.NotNullOrWhiteSpace(supportedContentType, nameof(supportedContentType)); + + return TryGetSerializer(objectType, new List { supportedContentType }, out serializer); + } + + public bool TryGetSerializer(Type objectType, IEnumerable supportedContentTypes, out ISerializer serializer) { - Guard.NotNull(contentType, nameof(contentType)); Guard.NotNull(objectType, nameof(objectType)); - Guard.NotNull(serializedObject, nameof(serializedObject)); + Guard.NotNull(supportedContentTypes, nameof(supportedContentTypes)); - if (!TryGetSerializer(contentType, out var serializer)) + foreach (var existingSerializer in _serializers) { - throw new NotSupportedException($"ContentType '{contentType}' is not supported."); + if (existingSerializer.CanSerializeType(objectType)) + { + if (!supportedContentTypes.Any()) + { + // The client doesn't have any preference regarding the content type + // so we can return the "best" (= the first that matches) + serializer = existingSerializer; + return true; + } + + if (supportedContentTypes.Any(x => string.Equals(x, existingSerializer.ContentType, StringComparison.OrdinalIgnoreCase))) + { + serializer = existingSerializer; + return true; + } + } } - return serializer.Deserialize(objectType, serializedObject); + serializer = null; + return false; } private static IReadOnlyList GetSupportedContentTypes(IEnumerable serializers) diff --git a/src/Meceqs/Serialization/ISerializationProvider.cs b/src/Meceqs/Serialization/ISerializationProvider.cs index af05ffc..cf91ffc 100644 --- a/src/Meceqs/Serialization/ISerializationProvider.cs +++ b/src/Meceqs/Serialization/ISerializationProvider.cs @@ -1,19 +1,22 @@ using System; using System.Collections.Generic; -using System.IO; namespace Meceqs.Serialization { public interface ISerializationProvider { - bool TryGetSerializer(string contentType, out ISerializer serializer); + IReadOnlyList GetSupportedContentTypes(Type objectType = null); ISerializer GetSerializer(Type objectType); - ISerializer GetSerializer(IEnumerable supportedContentTypes); + ISerializer GetSerializer(Type objectType, string supportedContentType); - IReadOnlyList GetSupportedContentTypes(Type objectType = null); + ISerializer GetSerializer(Type objectType, IEnumerable supportedContentTypes); + + bool TryGetSerializer(Type objectType, out ISerializer serializer); + + bool TryGetSerializer(Type objectType, string supportedContentType, out ISerializer serializer); - object Deserialize(string contentType, Type objectType, Stream serializedObject); + bool TryGetSerializer(Type objectType, IEnumerable supportedContentTypes, out ISerializer serializer); } }