From 88077bd98560d9bd98291a6b9a8e9a29ced9b970 Mon Sep 17 00:00:00 2001 From: CarloToso <105941898+CarloToso@users.noreply.github.com> Date: Wed, 14 Dec 2022 05:48:13 +0100 Subject: [PATCH] InvokeRestMethodCommand.Common cleanup and merge partials (#18736) --- .../Common/InvokeRestMethodCommand.Common.cs | 286 ++++++++---------- 1 file changed, 133 insertions(+), 153 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs index 3847fd9bab..9876d0a2ca 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs @@ -13,7 +13,15 @@ using Newtonsoft.Json.Linq; namespace Microsoft.PowerShell.Commands { - public partial class InvokeRestMethodCommand + /// + /// The Invoke-RestMethod command + /// This command makes an HTTP or HTTPS request to a web service, + /// and returns the response in an appropriate way. + /// Intended to work against the wide spectrum of "RESTful" web services + /// currently deployed across the web. + /// + [Cmdlet(VerbsLifecycle.Invoke, "RestMethod", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096706", DefaultParameterSetName = "StandardMethod")] + public class InvokeRestMethodCommand : WebRequestPSCmdlet { #region Parameters @@ -83,8 +91,124 @@ namespace Microsoft.PowerShell.Commands #endregion Parameters + #region Virtual Method Overrides + + /// + /// Process the web response and output corresponding objects. + /// + /// + internal override void ProcessResponse(HttpResponseMessage response) + { + if (response == null) { throw new ArgumentNullException(nameof(response)); } + + var baseResponseStream = StreamHelper.GetResponseStream(response); + + if (ShouldWriteToPipeline) + { + using var responseStream = new BufferingStreamReader(baseResponseStream); + + // First see if it is an RSS / ATOM feed, in which case we can + // stream it - unless the user has overridden it with a return type of "XML" + if (TryProcessFeedStream(responseStream)) + { + // Do nothing, content has been processed. + } + else + { + // determine the response type + RestReturnType returnType = CheckReturnType(response); + + // Try to get the response encoding from the ContentType header. + Encoding encoding = null; + string charSet = response.Content.Headers.ContentType?.CharSet; + if (!string.IsNullOrEmpty(charSet)) + { + StreamHelper.TryGetEncoding(charSet, out encoding); + } + + object obj = null; + Exception ex = null; + + string str = StreamHelper.DecodeStream(responseStream, ref encoding); + + string encodingVerboseName; + try + { + encodingVerboseName = string.IsNullOrEmpty(encoding.HeaderName) ? encoding.EncodingName : encoding.HeaderName; + } + catch (NotSupportedException) + { + encodingVerboseName = encoding.EncodingName; + } + + // NOTE: Tests use this verbose output to verify the encoding. + WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Content encoding: {0}", encodingVerboseName)); + + bool convertSuccess = false; + + if (returnType == RestReturnType.Json) + { + convertSuccess = TryConvertToJson(str, out obj, ref ex) || TryConvertToXml(str, out obj, ref ex); + } + // default to try xml first since it's more common + else + { + convertSuccess = TryConvertToXml(str, out obj, ref ex) || TryConvertToJson(str, out obj, ref ex); + } + + if (!convertSuccess) + { + // fallback to string + obj = str; + } + + WriteObject(obj); + } + } + else if (ShouldSaveToOutFile) + { + StreamHelper.SaveStreamToFile(baseResponseStream, QualifiedOutFile, this, response.Content.Headers.ContentLength.GetValueOrDefault(), _cancelToken.Token); + } + + if (!string.IsNullOrEmpty(StatusCodeVariable)) + { + PSVariableIntrinsics vi = SessionState.PSVariable; + vi.Set(StatusCodeVariable, (int)response.StatusCode); + } + + if (!string.IsNullOrEmpty(ResponseHeadersVariable)) + { + PSVariableIntrinsics vi = SessionState.PSVariable; + vi.Set(ResponseHeadersVariable, WebResponseHelper.GetHeadersDictionary(response)); + } + } + + #endregion Virtual Method Overrides + #region Helper Methods + private static RestReturnType CheckReturnType(HttpResponseMessage response) + { + if (response == null) { throw new ArgumentNullException(nameof(response)); } + + RestReturnType rt = RestReturnType.Detect; + string contentType = ContentHelper.GetContentType(response); + if (string.IsNullOrEmpty(contentType)) + { + rt = RestReturnType.Detect; + } + else if (ContentHelper.IsJson(contentType)) + { + rt = RestReturnType.Json; + } + else if (ContentHelper.IsXml(contentType)) + { + rt = RestReturnType.Xml; + } + + return rt; + } + private bool TryProcessFeedStream(Stream responseStream) { bool isRssOrFeed = false; @@ -111,7 +235,8 @@ namespace Microsoft.PowerShell.Commands if (isRssOrFeed) { XmlDocument workingDocument = new(); - // performing a Read() here to avoid rrechecking + + // Performing a Read() here to avoid rechecking // "rss" or "feed" items reader.Read(); while (!reader.EOF) @@ -122,7 +247,7 @@ namespace Microsoft.PowerShell.Commands string.Equals("Entry", reader.Name, StringComparison.OrdinalIgnoreCase)) ) { - // this one will do reader.Read() internally + // This one will do reader.Read() internally XmlNode result = workingDocument.ReadNode(reader); WriteObject(result); } @@ -178,7 +303,7 @@ namespace Microsoft.PowerShell.Commands doc = null; } - return (doc != null); + return doc != null; } private static bool TryConvertToJson(string json, out object obj, ref Exception exRef) @@ -226,7 +351,7 @@ namespace Microsoft.PowerShell.Commands return converted; } - #endregion + #endregion Helper Methods /// /// Enum for rest return type. @@ -265,20 +390,11 @@ namespace Microsoft.PowerShell.Commands private readonly MemoryStream _streamBuffer; private readonly byte[] _copyBuffer; - public override bool CanRead - { - get { return true; } - } + public override bool CanRead => true; - public override bool CanSeek - { - get { return true; } - } + public override bool CanSeek => true; - public override bool CanWrite - { - get { return false; } - } + public override bool CanWrite => false; public override void Flush() { @@ -359,140 +475,4 @@ namespace Microsoft.PowerShell.Commands } } } - - // TODO: Merge Partials - - /// - /// The Invoke-RestMethod command - /// This command makes an HTTP or HTTPS request to a web service, - /// and returns the response in an appropriate way. - /// Intended to work against the wide spectrum of "RESTful" web services - /// currently deployed across the web. - /// - [Cmdlet(VerbsLifecycle.Invoke, "RestMethod", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096706", DefaultParameterSetName = "StandardMethod")] - public partial class InvokeRestMethodCommand : WebRequestPSCmdlet - { - #region Virtual Method Overrides - - /// - /// Process the web response and output corresponding objects. - /// - /// - internal override void ProcessResponse(HttpResponseMessage response) - { - if (response == null) { throw new ArgumentNullException(nameof(response)); } - - var baseResponseStream = StreamHelper.GetResponseStream(response); - - if (ShouldWriteToPipeline) - { - using var responseStream = new BufferingStreamReader(baseResponseStream); - - // First see if it is an RSS / ATOM feed, in which case we can - // stream it - unless the user has overridden it with a return type of "XML" - if (TryProcessFeedStream(responseStream)) - { - // Do nothing, content has been processed. - } - else - { - // determine the response type - RestReturnType returnType = CheckReturnType(response); - - // Try to get the response encoding from the ContentType header. - Encoding encoding = null; - string charSet = response.Content.Headers.ContentType?.CharSet; - if (!string.IsNullOrEmpty(charSet)) - { - StreamHelper.TryGetEncoding(charSet, out encoding); - } - - object obj = null; - Exception ex = null; - - string str = StreamHelper.DecodeStream(responseStream, ref encoding); - - string encodingVerboseName; - try - { - encodingVerboseName = string.IsNullOrEmpty(encoding.HeaderName) ? encoding.EncodingName : encoding.HeaderName; - } - catch (NotSupportedException) - { - encodingVerboseName = encoding.EncodingName; - } - // NOTE: Tests use this verbose output to verify the encoding. - WriteVerbose(string.Format - ( - System.Globalization.CultureInfo.InvariantCulture, - "Content encoding: {0}", - encodingVerboseName) - ); - bool convertSuccess = false; - - if (returnType == RestReturnType.Json) - { - convertSuccess = TryConvertToJson(str, out obj, ref ex) || TryConvertToXml(str, out obj, ref ex); - } - // default to try xml first since it's more common - else - { - convertSuccess = TryConvertToXml(str, out obj, ref ex) || TryConvertToJson(str, out obj, ref ex); - } - - if (!convertSuccess) - { - // fallback to string - obj = str; - } - - WriteObject(obj); - } - } - else if (ShouldSaveToOutFile) - { - StreamHelper.SaveStreamToFile(baseResponseStream, QualifiedOutFile, this, response.Content.Headers.ContentLength.GetValueOrDefault(), _cancelToken.Token); - } - - if (!string.IsNullOrEmpty(StatusCodeVariable)) - { - PSVariableIntrinsics vi = SessionState.PSVariable; - vi.Set(StatusCodeVariable, (int)response.StatusCode); - } - - if (!string.IsNullOrEmpty(ResponseHeadersVariable)) - { - PSVariableIntrinsics vi = SessionState.PSVariable; - vi.Set(ResponseHeadersVariable, WebResponseHelper.GetHeadersDictionary(response)); - } - } - - #endregion Virtual Method Overrides - - #region Helper Methods - - private static RestReturnType CheckReturnType(HttpResponseMessage response) - { - if (response == null) { throw new ArgumentNullException(nameof(response)); } - - RestReturnType rt = RestReturnType.Detect; - string contentType = ContentHelper.GetContentType(response); - if (string.IsNullOrEmpty(contentType)) - { - rt = RestReturnType.Detect; - } - else if (ContentHelper.IsJson(contentType)) - { - rt = RestReturnType.Json; - } - else if (ContentHelper.IsXml(contentType)) - { - rt = RestReturnType.Xml; - } - - return (rt); - } - - #endregion Helper Methods - } }