コード例 #1
0
        /// <summary>
        /// Customized write method (derived from ODataOutputFormatter) which translates odata context
        /// and uses ODataMigration serializer provider
        /// </summary>
        /// <param name="context">OutputFormatterWriteContext</param>
        /// <param name="selectedEncoding">Encoding</param>
        /// <returns>Indication that writing is complete</returns>
        public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
        {
            Type type = context.ObjectType;

            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            type = TypeHelper.GetTaskInnerTypeOrSelf(type);

            HttpRequest request = context.HttpContext.Request;

            if (request == null)
            {
                throw new InvalidOperationException("Write to stream async must have request");
            }

            try
            {
                HttpResponse         response    = context.HttpContext.Response;
                Uri                  baseAddress = GetBaseAddressInternal(request);
                MediaTypeHeaderValue contentType = GetContentType(response.Headers[HeaderNames.ContentType].FirstOrDefault());

                Func <ODataSerializerContext> getODataSerializerContext = () =>
                {
                    return(new ODataSerializerContext()
                    {
                        Request = request,
                    });
                };


                ODataSerializerProvider serializerProvider = new ODataMigrationSerializerProvider(customContainer);

                WriteToStream(
                    type,
                    context.Object,
                    request.GetModel(),
                    baseAddress,
                    contentType,
                    request.GetUrlHelper(),
                    request,
                    request.Headers,
                    (services) => ODataMigrationMessageWrapper.Create(response.Body, response.Headers, null, services),
                    (edmType) => serializerProvider.GetEdmTypeSerializer(edmType),
                    (objectType) => serializerProvider.GetODataPayloadSerializer(objectType, request),
                    getODataSerializerContext);

                return(Task.CompletedTask);
            }
            catch (Exception ex)
            {
                return(Task.FromException(ex));
            }
        }
コード例 #2
0
        /// <summary>
        /// If the request has OData v3 headers in it, then process using V3 deserializer provider.
        /// Otherwise, process as base class.
        /// </summary>
        /// <param name="context">InputFormatter context</param>
        /// <param name="encoding">Encoding of request body</param>
        /// <returns>InputFormatterResult</returns>
        public override Task <InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            Type type = context.ModelType;

            if (type == null)
            {
                throw new ArgumentException("Model type for this request body is null", nameof(type));
            }

            HttpRequest request = context.HttpContext.Request;

            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            // If content length is 0 then return default value for this type
            RequestHeaders contentHeaders = request.GetTypedHeaders();
            object         defaultValue   = GetDefaultValueForType(type);

            if (contentHeaders == null || contentHeaders.ContentLength == 0)
            {
                return(Task.FromResult(InputFormatterResult.Success(defaultValue)));
            }

            try
            {
                Func <ODataDeserializerContext> getODataDeserializerContext = () =>
                {
                    return(new ODataDeserializerContext
                    {
                        Request = request,
                    });
                };

                Action <Exception> logErrorAction = (ex) =>
                {
                    ILogger logger = context.HttpContext.RequestServices.GetService <ILogger>();
                    if (logger == null)
                    {
                        throw ex;
                    }

                    logger.LogError(ex, String.Empty);
                };


                List <IDisposable> toDispose = new List <IDisposable>();

                IServiceProvider          fakeProvider         = (new ServiceCollection()).BuildServiceProvider();
                ODataDeserializerProvider deserializerProvider = new ODataMigrationDeserializerProvider(fakeProvider);

                object result = ReadFromStream(
                    type,
                    defaultValue,
                    request.GetModel(),
                    GetBaseAddressInternal(request),
                    request,
                    () => ODataMigrationMessageWrapper.Create(request.Body, request.Headers, request.GetODataContentIdMapping(), request.GetRequestContainer()),
                    (objectType) => deserializerProvider.GetEdmTypeDeserializer(objectType),
                    (objectType) => deserializerProvider.GetODataDeserializer(objectType, request),
                    getODataDeserializerContext,
                    (disposable) => toDispose.Add(disposable),
                    logErrorAction);

                foreach (IDisposable obj in toDispose)
                {
                    obj.Dispose();
                }

                return(Task.FromResult(InputFormatterResult.Success(result)));
            }
            catch (Exception ex)
            {
                context.ModelState.AddModelError(context.ModelName, ex, context.Metadata);
                return(Task.FromResult(InputFormatterResult.Failure()));
            }
        }
コード例 #3
0
        /// <summary>
        /// If the request has OData v3 headers in it, then process using V3 deserializer provider.
        /// Otherwise, process as base class.
        /// </summary>
        /// <param name="context">InputFormatter context</param>
        /// <param name="encoding">Encoding of request body</param>
        /// <returns>InputFormatterResult</returns>
        public override Task <InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            Type type = context.ModelType;

            if (type == null)
            {
                throw new ArgumentException("Model type for this request body is null", nameof(type));
            }

            HttpRequest request = context.HttpContext.Request;

            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            // Migration extension need AllowSynchronousIO to be true. (The value is false by default for .Net Core 3.0+, refer to https://github.com/dotnet/docs/issues/14835)
            var syncIOFeature = context.HttpContext.Features.Get <IHttpBodyControlFeature>();

            if (syncIOFeature != null)
            {
                syncIOFeature.AllowSynchronousIO = true;
            }

            // If content length is 0 then return default value for this type
            RequestHeaders contentHeaders = request.GetTypedHeaders();
            object         defaultValue   = GetDefaultValueForType(type);

            if (contentHeaders == null || contentHeaders.ContentLength == 0)
            {
                return(Task.FromResult(InputFormatterResult.Success(defaultValue)));
            }

            try
            {
                Func <ODataDeserializerContext> getODataDeserializerContext = () =>
                {
                    return(new ODataDeserializerContext
                    {
                        Request = request,
                    });
                };

                Action <Exception> logErrorAction = (ex) =>
                {
                    ILogger logger = context.HttpContext.RequestServices.GetService <ILogger>();
                    if (logger == null)
                    {
                        throw ex;
                    }

                    logger.LogError(ex, String.Empty);
                };


                List <IDisposable> disposeList = new List <IDisposable>();

                IServiceProvider          fakeProvider         = (new ServiceCollection()).BuildServiceProvider();
                ODataDeserializerProvider deserializerProvider = new ODataMigrationDeserializerProvider(fakeProvider);

                object result = ReadFromStream(
                    type,
                    defaultValue,
                    request.GetModel(),
                    GetBaseAddressInternal(request),
                    request,
                    // Use GetODataContentIdMapping() from the namespace Microsoft.AspNet.OData.Batch. Reference: https://github.com/OData/WebApi/pull/2012
                    () => ODataMigrationMessageWrapper.Create(request.Body, request.Headers, ODataBatchHttpRequestExtensions.GetODataContentIdMapping(request), request.GetRequestContainer()),
                    (objectType) => deserializerProvider.GetEdmTypeDeserializer(objectType),
                    (objectType) => deserializerProvider.GetODataDeserializer(objectType, request),
                    getODataDeserializerContext,
                    (disposable) => disposeList.Add(disposable),
                    logErrorAction);

                foreach (IDisposable obj in disposeList)
                {
                    obj.Dispose();
                }

                return(Task.FromResult(InputFormatterResult.Success(result)));
            }
            catch (Exception ex)
            {
                context.ModelState.AddModelError(context.ModelName, ex, context.Metadata);
                return(Task.FromResult(InputFormatterResult.Failure()));
            }
        }
コード例 #4
0
        /// <summary>
        /// Read and serialize outgoing object to HTTP request stream.
        /// </summary>
        internal static void WriteToStream(
            Type type,
            object value,
            IEdmModel model,
            Uri baseAddress,
            MediaTypeHeaderValue contentType,
            IUrlHelper internalUrlHelper,
            HttpRequest internalRequest,
            IHeaderDictionary internalRequestHeaders,
            Func <IServiceProvider, ODataMigrationMessageWrapper> getODataMessageWrapper,
            Func <IEdmTypeReference, ODataSerializer> getEdmTypeSerializer,
            Func <Type, ODataSerializer> getODataPayloadSerializer,
            Func <ODataSerializerContext> getODataSerializerContext)
        {
            if (model == null)
            {
                throw new InvalidOperationException("Request must have model");
            }

            ODataSerializer serializer = GetSerializer(type, value, internalRequest, getEdmTypeSerializer, getODataPayloadSerializer);

            // special case: if the top level serializer is an ODataPrimitiveSerializer then swap it out for an ODataMigrationPrimitiveSerializer
            // This only applies to the top level because inline primitives are translated but top level primitives are not, unless we use a customized serializer.
            if (serializer is ODataPrimitiveSerializer)
            {
                serializer = new ODataMigrationPrimitiveSerializer();
            }

            ODataPath            path = internalRequest.ODataFeature().Path;
            IEdmNavigationSource targetNavigationSource = path == null ? null : path.NavigationSource;

            // serialize a response
            string preferHeader     = GetRequestPreferHeader(internalRequestHeaders);
            string annotationFilter = null;

            if (!String.IsNullOrEmpty(preferHeader))
            {
                ODataMigrationMessageWrapper messageWrapper = getODataMessageWrapper(null);
                messageWrapper.SetHeader("Prefer", preferHeader);
                annotationFilter = messageWrapper.PreferHeader().AnnotationFilter;
            }

            ODataMigrationMessageWrapper responseMessageWrapper = getODataMessageWrapper(internalRequest.GetRequestContainer());
            IODataResponseMessage        responseMessage        = responseMessageWrapper;

            if (annotationFilter != null)
            {
                responseMessage.PreferenceAppliedHeader().AnnotationFilter = annotationFilter;
            }

            ODataMessageWriterSettings writerSettings = internalRequest.GetWriterSettings();

            writerSettings.BaseUri     = baseAddress;
            writerSettings.Version     = ODataVersion.V4; // Todo how to specify v3?  Maybe don't because reading as v4
            writerSettings.Validations = writerSettings.Validations & ~ValidationKinds.ThrowOnUndeclaredPropertyForNonOpenType;

            string metadataLink = internalUrlHelper.CreateODataLink(MetadataSegment.Instance);

            if (metadataLink == null)
            {
                throw new SerializationException("Unable to determine metadata url");
            }

            writerSettings.ODataUri = new ODataUri
            {
                ServiceRoot = baseAddress,

                SelectAndExpand = internalRequest.ODataFeature()?.SelectExpandClause,
                Apply           = internalRequest.ODataFeature().ApplyClause,
                Path            = (path == null || IsOperationPath(path)) ? null : path.Path,
            };

            ODataMetadataLevel metadataLevel = ODataMetadataLevel.MinimalMetadata;

            if (contentType != null)
            {
                IEnumerable <KeyValuePair <string, string> > parameters =
                    contentType.Parameters.Select(val => new KeyValuePair <string, string>(val.Name.Value, val.Value.Value));
                metadataLevel = ODataMediaTypes.GetMetadataLevel(contentType.MediaType.Value, parameters);
            }

            using (ODataMessageWriter messageWriter = new ODataMessageWriter(responseMessage, writerSettings, model))
            {
                ODataSerializerContext writeContext = getODataSerializerContext();
                writeContext.NavigationSource = targetNavigationSource;
                writeContext.Model            = model;
                writeContext.RootElementName  = GetRootElementName(path) ?? "root";
                writeContext.SkipExpensiveAvailabilityChecks = serializer.ODataPayloadKind == ODataPayloadKind.ResourceSet;
                writeContext.Path = path;
                writeContext.SelectExpandClause = internalRequest.ODataFeature()?.SelectExpandClause;
                writeContext.MetadataLevel      = metadataLevel;

                // Substitute stream to swap @odata.context
                Stream substituteStream = new MemoryStream();
                Stream originalStream   = messageWriter.SubstituteResponseStream(substituteStream);

                serializer.WriteObject(value, type, messageWriter, writeContext);
                StreamReader reader = new StreamReader(substituteStream);
                substituteStream.Seek(0, SeekOrigin.Begin);
                JToken responsePayload = JToken.Parse(reader.ReadToEnd());

                // If odata context is present, replace with odata metadata
                if (responsePayload["@odata.context"] != null)
                {
                    responsePayload["odata.metadata"] = responsePayload["@odata.context"].ToString().Replace("$entity", "@Element");
                    ((JObject)responsePayload).Property("@odata.context").Remove();
                }

                // Write to actual stream
                // We cannot dispose of the stream because this method does not own the stream (subsequent methods will close the streamwriter)
                StreamWriter   streamWriter   = new StreamWriter(originalStream);
                JsonTextWriter writer         = new JsonTextWriter(streamWriter);
                JsonSerializer jsonSerializer = new JsonSerializer();
                jsonSerializer.Serialize(writer, responsePayload);
                writer.Flush();
                messageWriter.SubstituteResponseStream(originalStream);
            }
        }