/// <summary> /// Asynchronously writes the headers, (optional) Content-ID and the request line. /// </summary> /// <param name="writer">Writer to write to.</param> /// <param name="httpMethod">The Http method to be used for this request operation.</param> /// <param name="uri">The Uri to be used for this request operation.</param> /// <param name="baseUri">The service root Uri to be used for this request operation.</param> /// <param name="inChangesetBound">Whether we are in changeset bound.</param> /// <param name="contentId">The Content-ID value to write in changeset head.</param> /// <param name="payloadUriOption">The format of operation Request-URI, which could be AbsoluteUri, AbsoluteResourcePathAndHost, or RelativeResourcePath.</param> /// <returns>A task that represents the asynchronous write operation.</returns> internal static async Task WriteRequestPreambleAsync( TextWriter writer, string httpMethod, Uri uri, Uri baseUri, bool inChangesetBound, string contentId, BatchPayloadUriOption payloadUriOption) { Debug.Assert(writer != null, "writer != null"); Debug.Assert(uri != null, "uri != null"); Debug.Assert(uri.IsAbsoluteUri || UriUtils.UriToString(uri).StartsWith("$", StringComparison.Ordinal), "uri.IsAbsoluteUri || uri.OriginalString.StartsWith(\"$\")"); // Write the headers await WriteHeadersAsync(writer, inChangesetBound, contentId) .ConfigureAwait(false); // Write separator line between headers and the request line await writer.WriteLineAsync() .ConfigureAwait(false); // Write request line await WriteRequestUriAsync(writer, httpMethod, uri, baseUri, payloadUriOption) .ConfigureAwait(false); }
/// <summary> /// Creates an <see cref="ODataBatchOperationRequestMessage"/> for writing an operation of a batch request /// - implementation of the actual functionality. /// </summary> /// <param name="method">The Http method to be used for this request operation.</param> /// <param name="uri">The Uri to be used for this request operation.</param> /// <param name="contentId">The Content-ID value to write in ChangeSet head.</param> /// <param name="payloadUriOption"> /// The format of operation Request-URI, which could be AbsoluteUri, AbsoluteResourcePathAndHost, or RelativeResourcePath.</param> /// <param name="dependsOnIds">The prerequisite request ids of this request. By default its value should be null for Multipart/Mixed /// format and the dependsOnIds implicitly derived per the protocol will be used; Otherwise, non-null will be used as override after /// validation.</param> /// <returns>The message that can be used to write the request operation.</returns> protected override ODataBatchOperationRequestMessage CreateOperationRequestMessageImplementation( string method, Uri uri, string contentId, BatchPayloadUriOption payloadUriOption, IEnumerable <string> dependsOnIds) { // write pending message data (headers, response line) for a previously unclosed message/request this.WritePendingMessageData(true); // create the new request operation // For Multipart batch format, validate dependsOnIds if it is user explicit input, otherwise skip validation // when it is implicitly derived per protocol. ODataBatchOperationRequestMessage operationRequestMessage = BuildOperationRequestMessage( this.RawOutputContext.OutputStream, method, uri, contentId, this.changeSetBoundary, dependsOnIds); this.SetState(BatchWriterState.OperationCreated); // write the operation's start boundary string this.WriteStartBoundaryForOperation(); if (contentId != null) { this.dependsOnIdsTracker.AddDependsOnId(contentId); } // write the headers and request line ODataMultipartMixedBatchWriterUtils.WriteRequestPreamble(this.RawOutputContext.TextWriter, method, uri, this.RawOutputContext.MessageWriterSettings.BaseUri, changeSetBoundary != null, contentId, payloadUriOption); return(operationRequestMessage); }
private void WriteRequestUri(Uri uri, BatchPayloadUriOption payloadUriOption) { this.jsonWriter.WriteName(PropertyUrl); if (uri.IsAbsoluteUri) { Uri baseUri = this.OutputContext.MessageWriterSettings.BaseUri; string absoluteUriString = uri.AbsoluteUri; switch (payloadUriOption) { case BatchPayloadUriOption.AbsoluteUri: this.jsonWriter.WriteValue(UriUtils.UriToString(uri)); break; case BatchPayloadUriOption.AbsoluteUriUsingHostHeader: string absoluteResourcePath = absoluteUriString.Substring(absoluteUriString.IndexOf('/', absoluteUriString.IndexOf("//", StringComparison.Ordinal) + 2)); this.jsonWriter.WriteValue(absoluteResourcePath); this.CurrentOperationRequestMessage.SetHeader("host", string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}:{1}", uri.Host, uri.Port)); break; case BatchPayloadUriOption.RelativeUri: Debug.Assert(baseUri != null, "baseUri != null"); string baseUriString = UriUtils.UriToString(baseUri); Debug.Assert(uri.AbsoluteUri.StartsWith(baseUriString, StringComparison.Ordinal), "absoluteUriString.StartsWith(baseUriString)"); string relativeResourcePath = uri.AbsoluteUri.Substring(baseUriString.Length); this.jsonWriter.WriteValue(relativeResourcePath); break; } } else { this.jsonWriter.WriteValue(UriUtils.UriToString(uri)); } }
/// <summary> /// Writes the headers, (optional) Content-ID and the request line. /// </summary> /// <param name="writer">Writer to write to.</param> /// <param name="httpMethod">The Http method to be used for this request operation.</param> /// <param name="uri">The Uri to be used for this request operation.</param> /// <param name="baseUri">The service root Uri to be used for this request operation.</param> /// <param name="inChangeSetBound">Whether we are in ChangeSetBound.</param> /// <param name="contentId">The Content-ID value to write in ChangeSet head.</param> /// <param name="payloadUriOption">The format of operation Request-URI, which could be AbsoluteUri, AbsoluteResourcePathAndHost, or RelativeResourcePath.</param> internal static void WriteRequestPreamble( TextWriter writer, string httpMethod, Uri uri, Uri baseUri, bool inChangeSetBound, string contentId, BatchPayloadUriOption payloadUriOption) { Debug.Assert(writer != null, "writer != null"); Debug.Assert(uri != null, "uri != null"); Debug.Assert(uri.IsAbsoluteUri || UriUtils.UriToString(uri).StartsWith("$", StringComparison.Ordinal), "uri.IsAbsoluteUri || uri.OriginalString.StartsWith(\"$\")"); // write the headers WriteHeaders(writer, inChangeSetBound, contentId); // write separator line between headers and the request line writer.WriteLine(); // write request line WriteRequestUri(writer, httpMethod, uri, baseUri, payloadUriOption); }
/// <summary> /// Creates an <see cref="ODataBatchOperationRequestMessage"/> for writing an operation of a batch request /// - implementation of the actual functionality. /// </summary> /// <param name="method">The Http method to be used for this request operation.</param> /// <param name="uri">The Uri to be used for this request operation.</param> /// <param name="contentId">The Content-ID value to write in ChangeSet head.</param> /// <param name="payloadUriOption"> /// The format of operation Request-URI, which could be AbsoluteUri, AbsoluteResourcePathAndHost, or RelativeResourcePath.</param> /// <param name="dependsOnIds">The prerequisite request ids of this request.</param> /// <returns>The message that can be used to write the request operation.</returns> protected override ODataBatchOperationRequestMessage CreateOperationRequestMessageImplementation( string method, Uri uri, string contentId, BatchPayloadUriOption payloadUriOption, IEnumerable<string> dependsOnIds) { // write pending message data (headers, response line) for a previously unclosed message/request this.WritePendingMessageData(true); // create the new request operation ODataBatchOperationRequestMessage operationRequestMessage = BuildOperationRequestMessage( this.RawOutputContext.OutputStream, method, uri, contentId, /*groupId*/null, dependsOnIds, ODataFormat.Batch); this.SetState(BatchWriterState.OperationCreated); // write the operation's start boundary string this.WriteStartBoundaryForOperation(); // write the headers and request line ODataMultipartMixedBatchWriterUtils.WriteRequestPreamble(this.RawOutputContext.TextWriter, method, uri, this.RawOutputContext.MessageWriterSettings.BaseUri, changeSetBoundary != null, contentId, payloadUriOption); return operationRequestMessage; }
private void BatchRequestWithPayloadUriWritingOption(BatchPayloadUriOption option) { var writerSettings = new ODataMessageWriterSettings(); writerSettings.BaseUri = ServiceBaseUri; ODataMessageReaderSettings readerSettings = new ODataMessageReaderSettings() { BaseUri = ServiceBaseUri }; var accountType = Model.FindDeclaredType(NameSpacePrefix + "Account") as IEdmEntityType; var accountSet = Model.EntityContainer.FindEntitySet("Accounts"); var paymentInstrumentType = Model.FindDeclaredType(NameSpacePrefix + "PaymentInstrument") as IEdmEntityType; IEdmNavigationProperty navProp = accountType.FindProperty("MyPaymentInstruments") as IEdmNavigationProperty; var myPaymentInstrumentSet = accountSet.FindNavigationTarget(navProp); var requestMessage = new HttpWebRequestMessage(new Uri(ServiceBaseUri + "$batch")); requestMessage.SetHeader("Content-Type", "multipart/mixed;boundary=batch_01AD6766-4A45-47CC-9463-94D4591D8DA9"); requestMessage.SetHeader("OData-Version", "4.0"); requestMessage.Method = "POST"; using (var messageWriter = new ODataMessageWriter(requestMessage, writerSettings, Model)) { var batchWriter = messageWriter.CreateODataBatchWriter(); //Batch start. batchWriter.WriteStartBatch(); //A Get request. var batchOperation1 = batchWriter.CreateOperationRequestMessage("GET", new Uri(ServiceBaseUri + "Accounts(101)/MyPaymentInstruments"), null, option); batchOperation1.SetHeader("Accept", "application/json;odata.metadata=full"); //Get request ends. //Changeset start. batchWriter.WriteStartChangeset(); //The first operation in changeset is a Create request. ODataBatchOperationRequestMessage batchChangesetOperation1 = batchWriter.CreateOperationRequestMessage("POST", new Uri(ServiceBaseUri + "Accounts(102)/MyPaymentInstruments"), "1", option); batchChangesetOperation1.SetHeader("Content-Type", "application/json;odata.metadata=full"); batchChangesetOperation1.SetHeader("Accept", "application/json;odata.metadata=full"); var paymentInstrumentEntry = new ODataResource() { TypeName = NameSpacePrefix + "PaymentInstrument" }; var paymentInstrumentEntryP1 = new ODataProperty { Name = "PaymentInstrumentID", Value = 102910 }; var paymentInstrumentEntryP2 = new ODataProperty { Name = "FriendlyName", Value = "102 batch new PI" }; var paymentInstrumentEntryP3 = new ODataProperty { Name = "CreatedDate", Value = new DateTimeOffset(new DateTime(2013, 12, 29, 11, 11, 57)) }; paymentInstrumentEntry.Properties = new[] { paymentInstrumentEntryP1, paymentInstrumentEntryP2, paymentInstrumentEntryP3 }; using (var entryMessageWriter = new ODataMessageWriter(batchChangesetOperation1, writerSettings, Model)) { var odataEntryWriter = entryMessageWriter.CreateODataResourceWriter(myPaymentInstrumentSet, paymentInstrumentType); odataEntryWriter.WriteStart(paymentInstrumentEntry); odataEntryWriter.WriteEnd(); } //Changeset end. batchWriter.WriteEndChangeset(); //Another Get request. var batchOperation2 = batchWriter.CreateOperationRequestMessage("GET", new Uri(ServiceBaseUri + "Accounts(103)/MyPaymentInstruments(103901)/BillingStatements(103901001)"), null, option); batchOperation2.SetHeader("Accept", "application/json;odata.metadata=full"); //Batch end. batchWriter.WriteEndBatch(); } var responseMessage = requestMessage.GetResponse(); Assert.AreEqual(200, responseMessage.StatusCode); using (var innerMessageReader = new ODataMessageReader(responseMessage, readerSettings, Model)) { var batchReader = innerMessageReader.CreateODataBatchReader(); int batchOperationId = 0; while (batchReader.Read()) { switch (batchReader.State) { case ODataBatchReaderState.Initial: break; case ODataBatchReaderState.ChangesetStart: break; case ODataBatchReaderState.ChangesetEnd: break; case ODataBatchReaderState.Operation: ODataBatchOperationResponseMessage operationResponse = batchReader.CreateOperationResponseMessage(); using (var operationResponseReader = new ODataMessageReader(operationResponse, readerSettings, Model)) { if (batchOperationId == 0) { // the first response message is a feed var feedReader = operationResponseReader.CreateODataResourceSetReader(); Assert.AreEqual(200, operationResponse.StatusCode); List <ODataResource> pis = new List <ODataResource>(); while (feedReader.Read()) { switch (feedReader.State) { case ODataReaderState.ResourceEnd: ODataResource entry = feedReader.Item as ODataResource; Assert.IsNotNull(entry); pis.Add(entry); break; } } Assert.AreEqual(ODataReaderState.Completed, feedReader.State); Assert.AreEqual(3, pis.Count); } else if (batchOperationId == 1) { // the second response message is a creation response var entryReader = operationResponseReader.CreateODataResourceReader(); Assert.AreEqual(201, operationResponse.StatusCode); List <ODataResource> pis = new List <ODataResource>(); while (entryReader.Read()) { switch (entryReader.State) { case ODataReaderState.ResourceEnd: ODataResource entry = entryReader.Item as ODataResource; Assert.IsNotNull(entry); pis.Add(entry); break; } } Assert.AreEqual(ODataReaderState.Completed, entryReader.State); Assert.AreEqual(1, pis.Count); Assert.AreEqual(102910, pis[0].Properties.Single(p => p.Name == "PaymentInstrumentID").Value); } else if (batchOperationId == 2) { // the third response message is an entry var entryReader = operationResponseReader.CreateODataResourceReader(); Assert.AreEqual(200, operationResponse.StatusCode); List <ODataResource> statements = new List <ODataResource>(); while (entryReader.Read()) { switch (entryReader.State) { case ODataReaderState.ResourceEnd: ODataResource entry = entryReader.Item as ODataResource; Assert.IsNotNull(entry); statements.Add(entry); break; } } Assert.AreEqual(ODataReaderState.Completed, entryReader.State); Assert.AreEqual(1, statements.Count); Assert.AreEqual(103901001, statements[0].Properties.Single(p => p.Name == "StatementID").Value); } } batchOperationId++; break; } } Assert.AreEqual(ODataBatchReaderState.Completed, batchReader.State); } }
private byte[] ClientWriteAsyncBatchRequest(BatchPayloadUriOption payloadUriOption) { var stream = new MemoryStream(); IODataRequestMessage requestMessage = new InMemoryMessage { Stream = stream }; requestMessage.SetHeader("Content-Type", batchContentType); using (var messageWriter = new ODataMessageWriter(requestMessage, new ODataMessageWriterSettings { BaseUri = new Uri(serviceDocumentUri) })) { var batchWriter = messageWriter.CreateODataBatchWriter(); batchWriter.WriteStartBatch(); // Write a query operation. var queryOperationMessage = batchWriter.CreateOperationRequestMessage("GET", new Uri(serviceDocumentUri + "/Customers('ALFKI')"), /*contentId*/ null, payloadUriOption); // Write a changeset with multi update operation. batchWriter.WriteStartChangeset(); // Create a creation operation in the changeset. var updateOperationMessage = batchWriter.CreateOperationRequestMessage("POST", new Uri(serviceDocumentUri + "/Customers"), "1", payloadUriOption); // Use a new message writer to write the body of this operation. using (var operationMessageWriter = new ODataMessageWriter(updateOperationMessage)) { var entryWriter = operationMessageWriter.CreateODataResourceWriter(); var entry = new ODataResource() { TypeName = "MyNS.Customer", Properties = new[] { new ODataProperty() { Name = "Id", Value = "AFKIL" }, new ODataProperty() { Name = "Name", Value = "Bob" } } }; entryWriter.WriteStart(entry); entryWriter.WriteEnd(); } updateOperationMessage = batchWriter.CreateOperationRequestMessage("PATCH", new Uri(serviceDocumentUri + "/Customers('ALFKI')"), "2", payloadUriOption); using (var operationMessageWriter = new ODataMessageWriter(updateOperationMessage)) { var entryWriter = operationMessageWriter.CreateODataResourceWriter(); var entry = new ODataResource() { TypeName = "MyNS.Customer", Properties = new[] { new ODataProperty() { Name = "Name", Value = "Jack" } } }; entryWriter.WriteStart(entry); entryWriter.WriteEnd(); } batchWriter.WriteEndChangeset(); // Write a query operation. batchWriter.CreateOperationRequestMessage("GET", new Uri(serviceDocumentUri + "/Products"), /*contentId*/ null, payloadUriOption); batchWriter.WriteEndBatch(); stream.Position = 0; return(stream.ToArray()); } }
/// <summary> /// Writes the request line. /// </summary> /// <param name="writer">Writer to write request uri.</param> /// <param name="httpMethod">The Http method to be used for this request operation.</param> /// <param name="uri">The Uri to be used for this request operation.</param> /// <param name="baseUri">The service root Uri to be used for this request operation.</param> /// <param name="payloadUriOption">The format of operation Request-URI, which could be AbsoluteUri, AbsoluteResourcePathAndHost, or RelativeResourcePath.</param> private static void WriteRequestUri(TextWriter writer, string httpMethod, Uri uri, Uri baseUri, BatchPayloadUriOption payloadUriOption) { if (uri.IsAbsoluteUri) { string absoluteUriString = uri.AbsoluteUri; switch (payloadUriOption) { case BatchPayloadUriOption.AbsoluteUri: writer.WriteLine("{0} {1} {2}", httpMethod, UriUtils.UriToString(uri), ODataConstants.HttpVersionInBatching); break; case BatchPayloadUriOption.AbsoluteUriUsingHostHeader: string absoluteResourcePath = absoluteUriString.Substring(absoluteUriString.IndexOf('/', absoluteUriString.IndexOf("//", StringComparison.Ordinal) + 2)); writer.WriteLine("{0} {1} {2}", httpMethod, absoluteResourcePath, ODataConstants.HttpVersionInBatching); writer.WriteLine("Host: {0}:{1}", uri.Host, uri.Port); break; case BatchPayloadUriOption.RelativeUri: Debug.Assert(baseUri != null, "baseUri != null"); string baseUriString = UriUtils.UriToString(baseUri); Debug.Assert(absoluteUriString.StartsWith(baseUriString, StringComparison.Ordinal), "absoluteUriString.StartsWith(baseUriString)"); string relativeResourcePath = absoluteUriString.Substring(baseUriString.Length); writer.WriteLine("{0} {1} {2}", httpMethod, relativeResourcePath, ODataConstants.HttpVersionInBatching); break; } } else { writer.WriteLine("{0} {1} {2}", httpMethod, UriUtils.UriToString(uri), ODataConstants.HttpVersionInBatching); } }
/// <summary> /// Creates an <see cref="ODataBatchOperationRequestMessage"/> for writing an operation of a /// batch request - implementation of the actual functionality. /// </summary> /// <param name="method">The Http method to be used for this request operation.</param> /// <param name="uri">The Uri to be used for this request operation.</param> /// <param name="contentId">The Content-ID value to write in ChangeSet head.</param> /// <param name="payloadUriOption"> /// The format of operation Request-URI, which could be AbsoluteUri, AbsoluteResourcePathAndHost, or RelativeResourcePath.</param> /// <param name="dependsOnIds">The prerequisite request ids of this request.</param> /// <returns>The message that can be used to write the request operation.</returns> protected override ODataBatchOperationRequestMessage CreateOperationRequestMessageImplementation(string method, Uri uri, string contentId, BatchPayloadUriOption payloadUriOption, IEnumerable <string> dependsOnIds) { // write pending message data (headers, request line) for a previously unclosed message/request this.WritePendingMessageData(true); // For json batch request, content Id is required for single request or request within atomicityGroup. if (contentId == null) { contentId = Guid.NewGuid().ToString(); } AddGroupIdLookup(contentId); // create the new request operation this.CurrentOperationRequestMessage = BuildOperationRequestMessage( this.JsonLightOutputContext.GetOutputStream(), method, uri, contentId, this.atomicityGroupId, dependsOnIds, ODataFormat.Json); this.SetState(BatchWriterState.OperationCreated); // write the operation's start boundary string this.WriteStartBoundaryForOperation(); this.jsonWriter.WriteName(PropertyId); this.jsonWriter.WriteValue(contentId); if (this.atomicityGroupId != null) { this.jsonWriter.WriteName(PropertyAtomicityGroup); this.jsonWriter.WriteValue(this.atomicityGroupId); } if (this.CurrentOperationRequestMessage.DependsOnIds != null && this.CurrentOperationRequestMessage.DependsOnIds.Any()) { this.jsonWriter.WriteName(PropertyDependsOn); this.jsonWriter.StartArrayScope(); foreach (string dependsOnId in this.CurrentOperationRequestMessage.DependsOnIds) { ValidateDependsOnId(contentId, dependsOnId); this.jsonWriter.WriteValue(dependsOnId); } this.jsonWriter.EndArrayScope(); } this.jsonWriter.WriteName(PropertyMethod); this.jsonWriter.WriteValue(method); this.jsonWriter.WriteName(PropertyUrl); this.jsonWriter.WriteValue(UriUtils.UriToString(uri)); return(this.CurrentOperationRequestMessage); }
/// <summary> /// Asynchronously writes the request line. /// </summary> /// <param name="writer">Writer to write request uri.</param> /// <param name="httpMethod">The Http method to be used for this request operation.</param> /// <param name="uri">The Uri to be used for this request operation.</param> /// <param name="baseUri">The service root Uri to be used for this request operation.</param> /// <param name="payloadUriOption">The format of operation Request-URI, which could be AbsoluteUri, AbsoluteResourcePathAndHost, or RelativeResourcePath.</param> /// <returns>A task that represents the asynchronous write operation.</returns> private static async Task WriteRequestUriAsync(TextWriter writer, string httpMethod, Uri uri, Uri baseUri, BatchPayloadUriOption payloadUriOption) { if (uri.IsAbsoluteUri) { string absoluteUriString = uri.AbsoluteUri; switch (payloadUriOption) { case BatchPayloadUriOption.AbsoluteUri: await writer.WriteLineAsync(string.Concat(httpMethod, " ", UriUtils.UriToString(uri), " ", ODataConstants.HttpVersionInBatching)) .ConfigureAwait(false); break; case BatchPayloadUriOption.AbsoluteUriUsingHostHeader: string absoluteResourcePath = ExtractAbsoluteResourcePath(absoluteUriString); await writer.WriteLineAsync(string.Concat(httpMethod, " ", absoluteResourcePath, " ", ODataConstants.HttpVersionInBatching)) .ConfigureAwait(false); await writer.WriteLineAsync(string.Concat("Host: ", uri.Host, ":", uri.Port)) .ConfigureAwait(false); break; case BatchPayloadUriOption.RelativeUri: Debug.Assert(baseUri != null, "baseUri != null"); string baseUriString = UriUtils.UriToString(baseUri); Debug.Assert(absoluteUriString.StartsWith(baseUriString, StringComparison.Ordinal), "absoluteUriString.StartsWith(baseUriString)"); string relativeResourcePath = absoluteUriString.Substring(baseUriString.Length); await writer.WriteLineAsync(string.Concat(httpMethod, " ", relativeResourcePath, " ", ODataConstants.HttpVersionInBatching)) .ConfigureAwait(false); break; } } else { await writer.WriteLineAsync(string.Concat(httpMethod, " ", UriUtils.UriToString(uri), " ", ODataConstants.HttpVersionInBatching)) .ConfigureAwait(false); } }