/// <summary> /// Substitute the stream from the messageWriter before writing, then read out the substituted stream /// and translate, then write to the original response stream. /// </summary> /// <param name="messageWriter">ODataMessageWriter</param> /// <param name="edmType">Type of object to be serialized</param> /// <param name="writeAction">Writer's action to write to stream</param> public static void PreemptivelyTranslateResponseStream(this ODataMessageWriter messageWriter, IEdmTypeReference edmType, Action <ODataMessageWriter> writeAction) { Stream substituteStream = new MemoryStream(); Stream originalStream = messageWriter.SubstituteResponseStream(substituteStream); writeAction(messageWriter); // read fake stream, walk translate json object, add item JToken responsePayload; StreamReader reader = new StreamReader(substituteStream); substituteStream.Seek(0, SeekOrigin.Begin); string requestBody = reader.ReadToEnd(); if (!string.IsNullOrEmpty(requestBody)) { responsePayload = JToken.Parse(requestBody); WalkTranslateResponse(responsePayload, edmType); // Write to actual stream // We cannot dispose of the stream, the outside methods will close it StreamWriter streamWriter = new StreamWriter(originalStream); JsonTextWriter writer = new JsonTextWriter(streamWriter); JsonSerializer serializer = new JsonSerializer(); serializer.Serialize(writer, responsePayload); writer.Flush(); } messageWriter.SubstituteResponseStream(originalStream); }
/// <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); } }