|
10 | 10 | using System.Net.Mime; |
11 | 11 | using System.Threading.Tasks; |
12 | 12 |
|
13 | | -namespace CloudNative.CloudEvents.AspNetCore |
| 13 | +namespace CloudNative.CloudEvents.AspNetCore; |
| 14 | + |
| 15 | +/// <summary> |
| 16 | +/// Extension methods to convert between HTTP responses and CloudEvents. |
| 17 | +/// </summary> |
| 18 | +public static class HttpResponseExtensions |
14 | 19 | { |
15 | 20 | /// <summary> |
16 | | - /// Extension methods to convert between HTTP responses and CloudEvents. |
| 21 | + /// Copies a <see cref="CloudEvent"/> into an <see cref="HttpResponse" />. |
17 | 22 | /// </summary> |
18 | | - public static class HttpResponseExtensions |
| 23 | + /// <param name="cloudEvent">The CloudEvent to copy. Must not be null, and must be a valid CloudEvent.</param> |
| 24 | + /// <param name="destination">The response to copy the CloudEvent to. Must not be null.</param> |
| 25 | + /// <param name="contentMode">Content mode (structured or binary)</param> |
| 26 | + /// <param name="formatter">The formatter to use within the conversion. Must not be null.</param> |
| 27 | + /// <returns>A task representing the asynchronous operation.</returns> |
| 28 | + public static async Task CopyToHttpResponseAsync(this CloudEvent cloudEvent, HttpResponse destination, |
| 29 | + ContentMode contentMode, CloudEventFormatter formatter) |
19 | 30 | { |
20 | | - /// <summary> |
21 | | - /// Copies a <see cref="CloudEvent"/> into an <see cref="HttpResponse" />. |
22 | | - /// </summary> |
23 | | - /// <param name="cloudEvent">The CloudEvent to copy. Must not be null, and must be a valid CloudEvent.</param> |
24 | | - /// <param name="destination">The response to copy the CloudEvent to. Must not be null.</param> |
25 | | - /// <param name="contentMode">Content mode (structured or binary)</param> |
26 | | - /// <param name="formatter">The formatter to use within the conversion. Must not be null.</param> |
27 | | - /// <returns>A task representing the asynchronous operation.</returns> |
28 | | - public static async Task CopyToHttpResponseAsync(this CloudEvent cloudEvent, HttpResponse destination, |
29 | | - ContentMode contentMode, CloudEventFormatter formatter) |
30 | | - { |
31 | | - Validation.CheckCloudEventArgument(cloudEvent, nameof(cloudEvent)); |
32 | | - Validation.CheckNotNull(destination, nameof(destination)); |
33 | | - Validation.CheckNotNull(formatter, nameof(formatter)); |
| 31 | + Validation.CheckCloudEventArgument(cloudEvent, nameof(cloudEvent)); |
| 32 | + Validation.CheckNotNull(destination, nameof(destination)); |
| 33 | + Validation.CheckNotNull(formatter, nameof(formatter)); |
34 | 34 |
|
35 | | - ReadOnlyMemory<byte> content; |
36 | | - ContentType? contentType; |
37 | | - switch (contentMode) |
38 | | - { |
39 | | - case ContentMode.Structured: |
40 | | - content = formatter.EncodeStructuredModeMessage(cloudEvent, out contentType); |
41 | | - break; |
42 | | - case ContentMode.Binary: |
43 | | - content = formatter.EncodeBinaryModeEventData(cloudEvent); |
44 | | - contentType = MimeUtilities.CreateContentTypeOrNull(formatter.GetOrInferDataContentType(cloudEvent)); |
45 | | - break; |
46 | | - default: |
47 | | - throw new ArgumentOutOfRangeException(nameof(contentMode), $"Unsupported content mode: {contentMode}"); |
48 | | - } |
49 | | - if (contentType is object) |
50 | | - { |
51 | | - destination.ContentType = contentType.ToString(); |
52 | | - } |
53 | | - else if (content.Length != 0) |
54 | | - { |
55 | | - throw new ArgumentException("The 'datacontenttype' attribute value must be specified", nameof(cloudEvent)); |
56 | | - } |
| 35 | + ReadOnlyMemory<byte> content; |
| 36 | + ContentType? contentType; |
| 37 | + switch (contentMode) |
| 38 | + { |
| 39 | + case ContentMode.Structured: |
| 40 | + content = formatter.EncodeStructuredModeMessage(cloudEvent, out contentType); |
| 41 | + break; |
| 42 | + case ContentMode.Binary: |
| 43 | + content = formatter.EncodeBinaryModeEventData(cloudEvent); |
| 44 | + contentType = MimeUtilities.CreateContentTypeOrNull(formatter.GetOrInferDataContentType(cloudEvent)); |
| 45 | + break; |
| 46 | + default: |
| 47 | + throw new ArgumentOutOfRangeException(nameof(contentMode), $"Unsupported content mode: {contentMode}"); |
| 48 | + } |
| 49 | + if (contentType is object) |
| 50 | + { |
| 51 | + destination.ContentType = contentType.ToString(); |
| 52 | + } |
| 53 | + else if (content.Length != 0) |
| 54 | + { |
| 55 | + throw new ArgumentException("The 'datacontenttype' attribute value must be specified", nameof(cloudEvent)); |
| 56 | + } |
57 | 57 |
|
58 | | - // Map headers in either mode. |
59 | | - // Including the headers in structured mode is optional in the spec (as they're already within the body) but |
60 | | - // can be useful. |
61 | | - destination.Headers.Append(HttpUtilities.SpecVersionHttpHeader, HttpUtilities.EncodeHeaderValue(cloudEvent.SpecVersion.VersionId)); |
62 | | - foreach (var attributeAndValue in cloudEvent.GetPopulatedAttributes()) |
| 58 | + // Map headers in either mode. |
| 59 | + // Including the headers in structured mode is optional in the spec (as they're already within the body) but |
| 60 | + // can be useful. |
| 61 | + destination.Headers.Append(HttpUtilities.SpecVersionHttpHeader, HttpUtilities.EncodeHeaderValue(cloudEvent.SpecVersion.VersionId)); |
| 62 | + foreach (var attributeAndValue in cloudEvent.GetPopulatedAttributes()) |
| 63 | + { |
| 64 | + var attribute = attributeAndValue.Key; |
| 65 | + var value = attributeAndValue.Value; |
| 66 | + // The content type is already handled based on the content mode. |
| 67 | + if (attribute != cloudEvent.SpecVersion.DataContentTypeAttribute) |
63 | 68 | { |
64 | | - var attribute = attributeAndValue.Key; |
65 | | - var value = attributeAndValue.Value; |
66 | | - // The content type is already handled based on the content mode. |
67 | | - if (attribute != cloudEvent.SpecVersion.DataContentTypeAttribute) |
68 | | - { |
69 | | - string headerValue = HttpUtilities.EncodeHeaderValue(attribute.Format(value)); |
70 | | - destination.Headers.Append(HttpUtilities.HttpHeaderPrefix + attribute.Name, headerValue); |
71 | | - } |
| 69 | + string headerValue = HttpUtilities.EncodeHeaderValue(attribute.Format(value)); |
| 70 | + destination.Headers.Append(HttpUtilities.HttpHeaderPrefix + attribute.Name, headerValue); |
72 | 71 | } |
73 | | - |
74 | | - destination.ContentLength = content.Length; |
75 | | - await BinaryDataUtilities.CopyToStreamAsync(content, destination.Body).ConfigureAwait(false); |
76 | 72 | } |
77 | 73 |
|
78 | | - /// <summary> |
79 | | - /// Copies a <see cref="CloudEvent"/> batch into an <see cref="HttpResponse" />. |
80 | | - /// </summary> |
81 | | - /// <param name="cloudEvents">The CloudEvent batch to copy. Must not be null, and must be a valid CloudEvent.</param> |
82 | | - /// <param name="destination">The response to copy the CloudEvent to. Must not be null.</param> |
83 | | - /// <param name="formatter">The formatter to use within the conversion. Must not be null.</param> |
84 | | - /// <returns>A task representing the asynchronous operation.</returns> |
85 | | - public static async Task CopyToHttpResponseAsync(this IReadOnlyList<CloudEvent> cloudEvents, |
86 | | - HttpResponse destination, CloudEventFormatter formatter) |
87 | | - { |
88 | | - Validation.CheckCloudEventBatchArgument(cloudEvents, nameof(cloudEvents)); |
89 | | - Validation.CheckNotNull(destination, nameof(destination)); |
90 | | - Validation.CheckNotNull(formatter, nameof(formatter)); |
| 74 | + destination.ContentLength = content.Length; |
| 75 | + await BinaryDataUtilities.CopyToStreamAsync(content, destination.Body).ConfigureAwait(false); |
| 76 | + } |
91 | 77 |
|
92 | | - // TODO: Validate that all events in the batch have the same version? |
93 | | - // See https://github.com/cloudevents/spec/issues/807 |
| 78 | + /// <summary> |
| 79 | + /// Copies a <see cref="CloudEvent"/> batch into an <see cref="HttpResponse" />. |
| 80 | + /// </summary> |
| 81 | + /// <param name="cloudEvents">The CloudEvent batch to copy. Must not be null, and must be a valid CloudEvent.</param> |
| 82 | + /// <param name="destination">The response to copy the CloudEvent to. Must not be null.</param> |
| 83 | + /// <param name="formatter">The formatter to use within the conversion. Must not be null.</param> |
| 84 | + /// <returns>A task representing the asynchronous operation.</returns> |
| 85 | + public static async Task CopyToHttpResponseAsync(this IReadOnlyList<CloudEvent> cloudEvents, |
| 86 | + HttpResponse destination, CloudEventFormatter formatter) |
| 87 | + { |
| 88 | + Validation.CheckCloudEventBatchArgument(cloudEvents, nameof(cloudEvents)); |
| 89 | + Validation.CheckNotNull(destination, nameof(destination)); |
| 90 | + Validation.CheckNotNull(formatter, nameof(formatter)); |
94 | 91 |
|
95 | | - ReadOnlyMemory<byte> content = formatter.EncodeBatchModeMessage(cloudEvents, out var contentType); |
96 | | - destination.ContentType = contentType.ToString(); |
97 | | - destination.ContentLength = content.Length; |
98 | | - await BinaryDataUtilities.CopyToStreamAsync(content, destination.Body).ConfigureAwait(false); |
99 | | - } |
| 92 | + // TODO: Validate that all events in the batch have the same version? |
| 93 | + // See https://github.com/cloudevents/spec/issues/807 |
| 94 | + |
| 95 | + ReadOnlyMemory<byte> content = formatter.EncodeBatchModeMessage(cloudEvents, out var contentType); |
| 96 | + destination.ContentType = contentType.ToString(); |
| 97 | + destination.ContentLength = content.Length; |
| 98 | + await BinaryDataUtilities.CopyToStreamAsync(content, destination.Body).ConfigureAwait(false); |
100 | 99 | } |
101 | 100 | } |
0 commit comments