diff --git a/.lutignore b/.lutignore new file mode 100644 index 0000000..6108a61 --- /dev/null +++ b/.lutignore @@ -0,0 +1,98 @@ +## The .lutignore file is used by Live Unit Testing to ignore Visual Studio temporary files, build results, +## and files generated by popular Visual Studio add-ons when creating a private copy of the source tree that +## Live Unit Testing uses for its build. +## +## This file has same format as git's .gitignore file (https://git-scm.com/docs/gitignore). In fact, in the +## case where a .lutignore file is not found, but a .gitignore file is found, Live Unit Testing will use the +## .gitignore file directly for the above purpose. + +# User-specific files +*.suo +*.user +*.userprefs +*.sln.docstates +.vs/ +.vscode/ +.packages/ +.dotnet/ +.tools/ +.idea/ + +# Build results +[Dd]ebug/ +[Rr]elease/ +[Bb]inaries/ +[Bb]in/ +[Oo]bj/ +x64/ +TestResults/ + +# Debug artifacts +launchSettings.json + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml + +# NuGet +packages/ +[Nn]u[Gg]et.exe +*-packages.config +*.nuget.props +*.nuget.targets +project.lock.json +msbuild.binlog +*.project.lock.json + +# Miscellaneous +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.wrn +*.vspscc +*.vssscc +.builds +*.pidb +*.scc +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# Cache and temp files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile +*.VC.opendb +*.VC.db +AppPackages/ +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings +$RECYCLE.BIN/ +.DS_Store +*wpftmp.* diff --git a/HttpClientToCurlGenerator.lutconfig b/HttpClientToCurlGenerator.lutconfig new file mode 100644 index 0000000..596a860 --- /dev/null +++ b/HttpClientToCurlGenerator.lutconfig @@ -0,0 +1,6 @@ + + + true + true + 180000 + \ No newline at end of file diff --git a/HttpClientToCurlGenerator.sln b/HttpClientToCurlGenerator.sln index 09a4b58..34648e8 100644 --- a/HttpClientToCurlGenerator.sln +++ b/HttpClientToCurlGenerator.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 17.7.34221.43 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpClientToCurl", "src\HttpClientToCurl\HttpClientToCurl.csproj", "{18B3309D-B84C-453D-8EF7-16CA9E58F5DC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpClientToCurlGeneratorTest", "tests\HttpClientToCurlTest\HttpClientToCurlTest.csproj", "{BF3321A5-A590-44DD-BA5D-44978D9B6125}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{A8574DB9-8411-4F81-A82E-F97AD00EF8AF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpClientToCurl.Sample.InConsole", "examples\HttpClientToCurl.Sample.InConsole\HttpClientToCurl.Sample.InConsole.csproj", "{323022D2-AAA7-443B-895C-77F5B1634D68}" @@ -25,7 +23,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solutionItems", "solutionIt pre-push = pre-push EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpRequestMessageToCurlGeneratorTest", "tests\HttpRequestMessageToCurlTest\HttpRequestMessageToCurlTest.csproj", "{007CA9E0-CDF0-4375-8E8C-A24C9A7BF531}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpClientToCurl.Sample.InGlobal", "examples\HttpClientToCurl.Sample.InGlobal\HttpClientToCurl.Sample.InGlobal.csproj", "{5A8427BC-0821-E973-7221-263D13156248}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpClientToCurlTest", "tests\HttpClientToCurlTest\HttpClientToCurlTest.csproj", "{8CC76F1F-5845-D81E-5E9A-113F913A444B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpRequestMessageToCurlTest", "tests\HttpRequestMessageToCurlTest\HttpRequestMessageToCurlTest.csproj", "{69E31075-F14E-1DE2-1D6E-D934A5C0480F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpClientToCurl.Sample.InSpecific", "examples\HttpClientToCurl.Sample.InSpecific\HttpClientToCurl.Sample.InSpecific.csproj", "{9D56718F-C9E6-4C45-926D-97599072DA35}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -37,10 +41,6 @@ Global {18B3309D-B84C-453D-8EF7-16CA9E58F5DC}.Debug|Any CPU.Build.0 = Debug|Any CPU {18B3309D-B84C-453D-8EF7-16CA9E58F5DC}.Release|Any CPU.ActiveCfg = Release|Any CPU {18B3309D-B84C-453D-8EF7-16CA9E58F5DC}.Release|Any CPU.Build.0 = Release|Any CPU - {BF3321A5-A590-44DD-BA5D-44978D9B6125}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BF3321A5-A590-44DD-BA5D-44978D9B6125}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BF3321A5-A590-44DD-BA5D-44978D9B6125}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BF3321A5-A590-44DD-BA5D-44978D9B6125}.Release|Any CPU.Build.0 = Release|Any CPU {323022D2-AAA7-443B-895C-77F5B1634D68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {323022D2-AAA7-443B-895C-77F5B1634D68}.Debug|Any CPU.Build.0 = Debug|Any CPU {323022D2-AAA7-443B-895C-77F5B1634D68}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -53,21 +53,35 @@ Global {403B236B-D7D8-43FB-B47E-DCCAF8BA96C4}.Debug|Any CPU.Build.0 = Debug|Any CPU {403B236B-D7D8-43FB-B47E-DCCAF8BA96C4}.Release|Any CPU.ActiveCfg = Release|Any CPU {403B236B-D7D8-43FB-B47E-DCCAF8BA96C4}.Release|Any CPU.Build.0 = Release|Any CPU - {007CA9E0-CDF0-4375-8E8C-A24C9A7BF531}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {007CA9E0-CDF0-4375-8E8C-A24C9A7BF531}.Debug|Any CPU.Build.0 = Debug|Any CPU - {007CA9E0-CDF0-4375-8E8C-A24C9A7BF531}.Release|Any CPU.ActiveCfg = Release|Any CPU - {007CA9E0-CDF0-4375-8E8C-A24C9A7BF531}.Release|Any CPU.Build.0 = Release|Any CPU + {5A8427BC-0821-E973-7221-263D13156248}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A8427BC-0821-E973-7221-263D13156248}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A8427BC-0821-E973-7221-263D13156248}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A8427BC-0821-E973-7221-263D13156248}.Release|Any CPU.Build.0 = Release|Any CPU + {8CC76F1F-5845-D81E-5E9A-113F913A444B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8CC76F1F-5845-D81E-5E9A-113F913A444B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8CC76F1F-5845-D81E-5E9A-113F913A444B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8CC76F1F-5845-D81E-5E9A-113F913A444B}.Release|Any CPU.Build.0 = Release|Any CPU + {69E31075-F14E-1DE2-1D6E-D934A5C0480F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {69E31075-F14E-1DE2-1D6E-D934A5C0480F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {69E31075-F14E-1DE2-1D6E-D934A5C0480F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {69E31075-F14E-1DE2-1D6E-D934A5C0480F}.Release|Any CPU.Build.0 = Release|Any CPU + {9D56718F-C9E6-4C45-926D-97599072DA35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D56718F-C9E6-4C45-926D-97599072DA35}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D56718F-C9E6-4C45-926D-97599072DA35}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D56718F-C9E6-4C45-926D-97599072DA35}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {18B3309D-B84C-453D-8EF7-16CA9E58F5DC} = {D3967ABF-F7C6-4432-9B67-A3F804CBC3E7} - {BF3321A5-A590-44DD-BA5D-44978D9B6125} = {E36BF269-7F5D-4DE7-99B0-14567F9CD6B3} {323022D2-AAA7-443B-895C-77F5B1634D68} = {A8574DB9-8411-4F81-A82E-F97AD00EF8AF} {F7B35144-A00C-45BE-BC41-36C1B21FCD18} = {A8574DB9-8411-4F81-A82E-F97AD00EF8AF} {403B236B-D7D8-43FB-B47E-DCCAF8BA96C4} = {A8574DB9-8411-4F81-A82E-F97AD00EF8AF} - {007CA9E0-CDF0-4375-8E8C-A24C9A7BF531} = {E36BF269-7F5D-4DE7-99B0-14567F9CD6B3} + {5A8427BC-0821-E973-7221-263D13156248} = {A8574DB9-8411-4F81-A82E-F97AD00EF8AF} + {8CC76F1F-5845-D81E-5E9A-113F913A444B} = {E36BF269-7F5D-4DE7-99B0-14567F9CD6B3} + {69E31075-F14E-1DE2-1D6E-D934A5C0480F} = {E36BF269-7F5D-4DE7-99B0-14567F9CD6B3} + {9D56718F-C9E6-4C45-926D-97599072DA35} = {A8574DB9-8411-4F81-A82E-F97AD00EF8AF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E5E0FFF6-54C3-4BA1-91F3-EF3513A18D5D} diff --git a/examples/HttpClientToCurl.Sample.InConsole/ApiCaller.cs b/examples/HttpClientToCurl.Sample.InConsole/ApiCaller.cs index 5e8f197..2e58582 100644 --- a/examples/HttpClientToCurl.Sample.InConsole/ApiCaller.cs +++ b/examples/HttpClientToCurl.Sample.InConsole/ApiCaller.cs @@ -1,4 +1,5 @@ using System.Text; +using HttpClientToCurl.Extensions; namespace HttpClientToCurl.Sample.InConsole; diff --git a/examples/HttpClientToCurl.Sample.InFile/ApiCaller.cs b/examples/HttpClientToCurl.Sample.InFile/ApiCaller.cs index 8df32d5..38aaabd 100644 --- a/examples/HttpClientToCurl.Sample.InFile/ApiCaller.cs +++ b/examples/HttpClientToCurl.Sample.InFile/ApiCaller.cs @@ -1,4 +1,5 @@ using System.Text; +using HttpClientToCurl.Extensions; namespace HttpClientToCurl.Sample.InFile; diff --git a/examples/HttpClientToCurl.Sample.InGlobal/Controllers/MyController.cs b/examples/HttpClientToCurl.Sample.InGlobal/Controllers/MyController.cs new file mode 100644 index 0000000..25600a8 --- /dev/null +++ b/examples/HttpClientToCurl.Sample.InGlobal/Controllers/MyController.cs @@ -0,0 +1,56 @@ +using System.Text; +using Microsoft.AspNetCore.Mvc; + +namespace HttpClientToCurl.Sample.InGlobal.Controllers; + +[ApiController] +[Route("[controller]")] +public class MyController(HttpClient httpClient) : ControllerBase +{ + private readonly HttpClient _httpClient = httpClient; + + [HttpGet] + public async Task Send() + { + string apiUrl = "https://jsonplaceholder.typicode.com/posts"; + + try + { + // Create a sample JSON payload + string jsonPayload = + $"{{\"title\":\"New Post\",\"body\":\"This is the body of the new post\",\"userId\":1}}"; + + // Create HttpRequestMessage with the POST verb + HttpRequestMessage request = new(HttpMethod.Post, apiUrl); + + // Set up the request headers + request.Headers.Add("Authorization", "Bearer YourAccessToken"); // Add any necessary headers + + // Set the request content with the JSON payload + request.Content = new StringContent(jsonPayload, Encoding.UTF8, "application/json"); + + // Log the curl command for debugging or testing. + // This generates a curl command that can be imported into Postman. + // Use it to check and compare against all the requirements. + + // Send the request + HttpResponseMessage response = await _httpClient.SendAsync(request); + + // Check if the request was successful (status code 200-299) + if (response.IsSuccessStatusCode) + { + // Read and print the response content as a string + string responseBody = await response.Content.ReadAsStringAsync(); + Console.WriteLine("Response from the API:\n" + responseBody); + } + else + { + Console.WriteLine($"Error: {response.StatusCode} - {response.ReasonPhrase}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"Exception: {ex.Message}"); + } + } +} diff --git a/examples/HttpClientToCurl.Sample.InGlobal/HttpClientToCurl.Sample.InGlobal.csproj b/examples/HttpClientToCurl.Sample.InGlobal/HttpClientToCurl.Sample.InGlobal.csproj new file mode 100644 index 0000000..99a0baf --- /dev/null +++ b/examples/HttpClientToCurl.Sample.InGlobal/HttpClientToCurl.Sample.InGlobal.csproj @@ -0,0 +1,14 @@ + + + + net9.0 + enable + enable + + + + + + + + diff --git a/examples/HttpClientToCurl.Sample.InGlobal/Program.cs b/examples/HttpClientToCurl.Sample.InGlobal/Program.cs new file mode 100644 index 0000000..bd5b837 --- /dev/null +++ b/examples/HttpClientToCurl.Sample.InGlobal/Program.cs @@ -0,0 +1,14 @@ +using static HttpClientToCurl.Extensions.ServiceCollectionExtensions; + +var builder = WebApplication.CreateBuilder(args); +builder.Services.AddControllers(); + +builder.Services.AddHttpClientToCurlInGeneralMode(builder.Configuration); +builder.Services.AddHttpClient(); + +var app = builder.Build(); + +app.UseHttpsRedirection(); +app.UseAuthorization(); +app.MapControllers(); +app.Run(); diff --git a/examples/HttpClientToCurl.Sample.InGlobal/Properties/launchSettings.json b/examples/HttpClientToCurl.Sample.InGlobal/Properties/launchSettings.json new file mode 100644 index 0000000..2dbac44 --- /dev/null +++ b/examples/HttpClientToCurl.Sample.InGlobal/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5272", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7038;http://localhost:5272", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/examples/HttpClientToCurl.Sample.InGlobal/appsettings.Development.json b/examples/HttpClientToCurl.Sample.InGlobal/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/examples/HttpClientToCurl.Sample.InGlobal/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/examples/HttpClientToCurl.Sample.InGlobal/appsettings.json b/examples/HttpClientToCurl.Sample.InGlobal/appsettings.json new file mode 100644 index 0000000..279e909 --- /dev/null +++ b/examples/HttpClientToCurl.Sample.InGlobal/appsettings.json @@ -0,0 +1,27 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "HttpClientToCurl": { + "TurnOnAll": true, + + "ShowOnConsole": { + "TurnOn": true, //CAUTION: It will be applied when TurnOnAll is 'true' + "NeedAddDefaultHeaders": true, + "EnableCompression": false, + "EnableCodeBeautification": true + }, + + "SaveToFile": { + "TurnOn": true, //CAUTION: It will be applied when TurnOnAll is 'true' + "NeedAddDefaultHeaders": true, + "EnableCompression": false, + "Filename": "curl_commands", + "Path": "C:\\Users\\Public" + } + } +} diff --git a/examples/HttpClientToCurl.Sample.InSpecific/Controllers/MyController.cs b/examples/HttpClientToCurl.Sample.InSpecific/Controllers/MyController.cs new file mode 100644 index 0000000..0094dbc --- /dev/null +++ b/examples/HttpClientToCurl.Sample.InSpecific/Controllers/MyController.cs @@ -0,0 +1,67 @@ +using System.Text; +using Microsoft.AspNetCore.Mvc; + +namespace HttpClientToCurl.Sample.InSpecific.Controllers; + +[ApiController] +[Route("[controller]")] +public class MyController(IHttpClientFactory httpClientFactory) : ControllerBase +{ + private readonly IHttpClientFactory _httpClientFactory = httpClientFactory; + + [HttpGet("send-and-show-curl")] + public async Task SendAndShowCurl() + { + await SendRemoteRequest("my-client1"); + } + + [HttpGet("send")] + public async Task Send() + { + await SendRemoteRequest("my-client2"); + } + + private async Task SendRemoteRequest(string httpClientName) + { + string apiUrl = "https://jsonplaceholder.typicode.com/posts"; + + try + { + // Create a sample JSON payload + string jsonPayload = + $"{{\"title\":\"New Post\",\"body\":\"This is the body of the new post\",\"userId\":1}}"; + + // Create HttpRequestMessage with the POST verb + HttpRequestMessage request = new(HttpMethod.Post, apiUrl); + + // Set up the request headers + request.Headers.Add("Authorization", "Bearer YourAccessToken"); // Add any necessary headers + + // Set the request content with the JSON payload + request.Content = new StringContent(jsonPayload, Encoding.UTF8, "application/json"); + + // Log the curl command for debugging or testing. + // This generates a curl command that can be imported into Postman. + // Use it to check and compare against all the requirements. + + // Send the request + HttpResponseMessage response = await _httpClientFactory.CreateClient(httpClientName).SendAsync(request); + + // Check if the request was successful (status code 200-299) + if (response.IsSuccessStatusCode) + { + // Read and print the response content as a string + string responseBody = await response.Content.ReadAsStringAsync(); + Console.WriteLine("Response from the API:\n" + responseBody); + } + else + { + Console.WriteLine($"Error: {response.StatusCode} - {response.ReasonPhrase}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"Exception: {ex.Message}"); + } + } +} diff --git a/examples/HttpClientToCurl.Sample.InSpecific/HttpClientToCurl.Sample.InSpecific.csproj b/examples/HttpClientToCurl.Sample.InSpecific/HttpClientToCurl.Sample.InSpecific.csproj new file mode 100644 index 0000000..100f3d9 --- /dev/null +++ b/examples/HttpClientToCurl.Sample.InSpecific/HttpClientToCurl.Sample.InSpecific.csproj @@ -0,0 +1,13 @@ + + + + net9.0 + enable + enable + + + + + + + diff --git a/examples/HttpClientToCurl.Sample.InSpecific/Program.cs b/examples/HttpClientToCurl.Sample.InSpecific/Program.cs new file mode 100644 index 0000000..3a6d93b --- /dev/null +++ b/examples/HttpClientToCurl.Sample.InSpecific/Program.cs @@ -0,0 +1,15 @@ +using static HttpClientToCurl.Extensions.ServiceCollectionExtensions; + +var builder = WebApplication.CreateBuilder(args); +builder.Services.AddControllers(); + +builder.Services.AddHttpClientToCurl(builder.Configuration); +builder.Services.AddHttpClient("my-client1", showCurl: true); +builder.Services.AddHttpClient("my-client2"); + +var app = builder.Build(); + +app.UseHttpsRedirection(); +app.UseAuthorization(); +app.MapControllers(); +app.Run(); diff --git a/examples/HttpClientToCurl.Sample.InSpecific/Properties/launchSettings.json b/examples/HttpClientToCurl.Sample.InSpecific/Properties/launchSettings.json new file mode 100644 index 0000000..c584543 --- /dev/null +++ b/examples/HttpClientToCurl.Sample.InSpecific/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5062", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7001;http://localhost:5062", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/examples/HttpClientToCurl.Sample.InSpecific/appsettings.Development.json b/examples/HttpClientToCurl.Sample.InSpecific/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/examples/HttpClientToCurl.Sample.InSpecific/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/examples/HttpClientToCurl.Sample.InSpecific/appsettings.json b/examples/HttpClientToCurl.Sample.InSpecific/appsettings.json new file mode 100644 index 0000000..279e909 --- /dev/null +++ b/examples/HttpClientToCurl.Sample.InSpecific/appsettings.json @@ -0,0 +1,27 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "HttpClientToCurl": { + "TurnOnAll": true, + + "ShowOnConsole": { + "TurnOn": true, //CAUTION: It will be applied when TurnOnAll is 'true' + "NeedAddDefaultHeaders": true, + "EnableCompression": false, + "EnableCodeBeautification": true + }, + + "SaveToFile": { + "TurnOn": true, //CAUTION: It will be applied when TurnOnAll is 'true' + "NeedAddDefaultHeaders": true, + "EnableCompression": false, + "Filename": "curl_commands", + "Path": "C:\\Users\\Public" + } + } +} diff --git a/examples/HttpClientToCurl.Sample.InString/ApiCaller.cs b/examples/HttpClientToCurl.Sample.InString/ApiCaller.cs index ccfeb20..9db59fd 100644 --- a/examples/HttpClientToCurl.Sample.InString/ApiCaller.cs +++ b/examples/HttpClientToCurl.Sample.InString/ApiCaller.cs @@ -1,4 +1,5 @@ using System.Text; +using HttpClientToCurl.Extensions; namespace HttpClientToCurl.Sample.InString; diff --git a/examples/HttpClientToCurl.Sample.InString/HttpClientToCurl.Sample.InString.csproj b/examples/HttpClientToCurl.Sample.InString/HttpClientToCurl.Sample.InString.csproj index f1bc580..837995e 100644 --- a/examples/HttpClientToCurl.Sample.InString/HttpClientToCurl.Sample.InString.csproj +++ b/examples/HttpClientToCurl.Sample.InString/HttpClientToCurl.Sample.InString.csproj @@ -11,4 +11,8 @@ + + + + diff --git a/src/HttpClientToCurl/Config/CompositConfig.cs b/src/HttpClientToCurl/Config/CompositConfig.cs new file mode 100644 index 0000000..d610b76 --- /dev/null +++ b/src/HttpClientToCurl/Config/CompositConfig.cs @@ -0,0 +1,21 @@ +namespace HttpClientToCurl.Config; + +public sealed class CompositConfig +{ + /// + /// Set true to create curl output; false to disable it. Default is true. + /// + public bool TurnOnAll { get; set; } = true; + + /// + /// Set true to show curl on the console; false to disable it. Default is true. + /// If TurnOnAll is set to false, it will be ignored. + /// + public ConsoleConfig ShowOnConsole { get; set; } + + /// + /// Set true to save the curl file; false to disable it. Default is true. + /// If TurnOnAll is set to false, it will be ignored. + /// + public FileConfig SaveToFile { get; set; } +} diff --git a/src/HttpClientToCurl/HttpClientExtensions.cs b/src/HttpClientToCurl/Extensions/HttpClientExtensions.cs similarity index 99% rename from src/HttpClientToCurl/HttpClientExtensions.cs rename to src/HttpClientToCurl/Extensions/HttpClientExtensions.cs index 499f5da..4b77bbc 100644 --- a/src/HttpClientToCurl/HttpClientExtensions.cs +++ b/src/HttpClientToCurl/Extensions/HttpClientExtensions.cs @@ -3,7 +3,7 @@ using HttpClientToCurl.Config; using HttpClientToCurl.Utility; -namespace HttpClientToCurl; +namespace HttpClientToCurl.Extensions; public static class HttpClientExtensions { diff --git a/src/HttpClientToCurl/HttpRequestMessageExtensions.cs b/src/HttpClientToCurl/Extensions/HttpRequestMessageExtensions.cs similarity index 98% rename from src/HttpClientToCurl/HttpRequestMessageExtensions.cs rename to src/HttpClientToCurl/Extensions/HttpRequestMessageExtensions.cs index 3437dda..a2dcd76 100644 --- a/src/HttpClientToCurl/HttpRequestMessageExtensions.cs +++ b/src/HttpClientToCurl/Extensions/HttpRequestMessageExtensions.cs @@ -2,7 +2,7 @@ using HttpClientToCurl.Config; using HttpClientToCurl.Utility; -namespace HttpClientToCurl; +namespace HttpClientToCurl.Extensions; public static class HttpRequestMessageExtensions { diff --git a/src/HttpClientToCurl/Extensions/ServiceCollectionExtensions.cs b/src/HttpClientToCurl/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..80ddb98 --- /dev/null +++ b/src/HttpClientToCurl/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,52 @@ +using HttpClientToCurl.Config; +using HttpClientToCurl.HttpMessageHandlers; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Http; + +namespace HttpClientToCurl.Extensions; + +public static class ServiceCollectionExtensions +{ + /// + /// Generating curl script for all HTTP requests. + /// By default, show it in the IDE console. + /// + public static void AddHttpClientToCurlInGeneralMode( + this IServiceCollection services, + IConfiguration configuration) + { + AddServices(services, configuration); + services.Add(ServiceDescriptor.Transient()); + } + + /// + /// Generating curl script for specific http client + /// + /// + /// + public static void AddHttpClientToCurl( + this IServiceCollection services, + IConfiguration configuration) + { + AddServices(services, configuration); + } + + public static IHttpClientBuilder AddHttpClient(this IServiceCollection services, string name, bool showCurl = false) + { + var httpClientBuilder = HttpClientFactoryServiceCollectionExtensions.AddHttpClient(services, name); + + if (showCurl) + { + httpClientBuilder.AddHttpMessageHandler(); + } + + return httpClientBuilder; + } + + private static void AddServices(IServiceCollection services, IConfiguration configuration) + { + services.Configure(configuration.GetSection("HttpClientToCurl")); + services.AddTransient(); + } +} diff --git a/src/HttpClientToCurl/HttpClientToCurl.csproj b/src/HttpClientToCurl/HttpClientToCurl.csproj index 68c7394..4cbe8fc 100644 --- a/src/HttpClientToCurl/HttpClientToCurl.csproj +++ b/src/HttpClientToCurl/HttpClientToCurl.csproj @@ -1,4 +1,4 @@ - + Amin Golmahalleh @@ -22,4 +22,10 @@ + + + + + + diff --git a/src/HttpClientToCurl/HttpMessageHandlers/CurlGeneratorHttpMessageHandler.cs b/src/HttpClientToCurl/HttpMessageHandlers/CurlGeneratorHttpMessageHandler.cs new file mode 100644 index 0000000..ea187b3 --- /dev/null +++ b/src/HttpClientToCurl/HttpMessageHandlers/CurlGeneratorHttpMessageHandler.cs @@ -0,0 +1,45 @@ +using HttpClientToCurl.Config; +using HttpClientToCurl.Extensions; +using Microsoft.Extensions.Options; + +namespace HttpClientToCurl.HttpMessageHandlers; + +public class CurlGeneratorHttpMessageHandler(IOptionsMonitor monitorConfig) : DelegatingHandler +{ + private readonly IOptionsMonitor _monitorConfig = monitorConfig; + + protected override Task SendAsync( + HttpRequestMessage httpRequestMessage, + CancellationToken cancellationToken) + { + var config = _monitorConfig.CurrentValue; + if (config.TurnOnAll) + { + if (config.ShowOnConsole?.TurnOn ?? false) + { + httpRequestMessage.GenerateCurlInConsole(httpRequestMessage.RequestUri, consoleConfig => + { + consoleConfig.TurnOn = true; + consoleConfig.EnableCodeBeautification = config.ShowOnConsole.EnableCodeBeautification; + consoleConfig.EnableCompression = config.ShowOnConsole.EnableCompression; + consoleConfig.NeedAddDefaultHeaders = config.ShowOnConsole.NeedAddDefaultHeaders; + }); + } + + if (config.SaveToFile?.TurnOn ?? false) + { + httpRequestMessage.GenerateCurlInFile(httpRequestMessage.RequestUri, fileConfig => + { + fileConfig.TurnOn = true; + fileConfig.EnableCompression = config.SaveToFile.EnableCompression; + fileConfig.NeedAddDefaultHeaders = config.SaveToFile.NeedAddDefaultHeaders; + fileConfig.Path = config.SaveToFile.Path; + fileConfig.Filename = config.SaveToFile.Filename; + }); + } + } + + var response = base.SendAsync(httpRequestMessage, cancellationToken); + return response; + } +} diff --git a/src/HttpClientToCurl/HttpMessageHandlers/HttpMessageHandlerAppender.cs b/src/HttpClientToCurl/HttpMessageHandlers/HttpMessageHandlerAppender.cs new file mode 100644 index 0000000..fa35ae5 --- /dev/null +++ b/src/HttpClientToCurl/HttpMessageHandlers/HttpMessageHandlerAppender.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Http; + +namespace HttpClientToCurl.HttpMessageHandlers; +public class HttpMessageHandlerAppender(IServiceProvider serviceProvider) : IHttpMessageHandlerBuilderFilter +{ + public Action Configure(Action next) => builder => + { + next(builder); + var handler = serviceProvider.GetRequiredService(); + builder.AdditionalHandlers.Add(handler); + }; +} diff --git a/tests/HttpClientToCurlTest/FunctionalTest/SuccessScenariosTests.cs b/tests/HttpClientToCurlTest/FunctionalTest/SuccessScenariosTests.cs index f8c7d0c..088c915 100644 --- a/tests/HttpClientToCurlTest/FunctionalTest/SuccessScenariosTests.cs +++ b/tests/HttpClientToCurlTest/FunctionalTest/SuccessScenariosTests.cs @@ -3,7 +3,7 @@ using System.Net.Mime; using System.Text; using FluentAssertions; -using HttpClientToCurl; +using HttpClientToCurl.Extensions; using HttpClientToCurl.Utility; using Microsoft.AspNetCore.WebUtilities; using Xunit; diff --git a/tests/HttpRequestMessageToCurlTest/FunctionalTest/SuccessScenariosTests.cs b/tests/HttpRequestMessageToCurlTest/FunctionalTest/SuccessScenariosTests.cs index ae88a67..c933c97 100644 --- a/tests/HttpRequestMessageToCurlTest/FunctionalTest/SuccessScenariosTests.cs +++ b/tests/HttpRequestMessageToCurlTest/FunctionalTest/SuccessScenariosTests.cs @@ -1,7 +1,7 @@ using System.Net.Mime; using System.Text; using FluentAssertions; -using HttpClientToCurl; +using HttpClientToCurl.Extensions; using HttpClientToCurl.Utility; using Microsoft.AspNetCore.WebUtilities; using Xunit;