Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/aspnetcore.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSafia Abdalla <safia@safia.rocks>2022-11-10 21:31:56 +0300
committerSafia Abdalla <safia@safia.rocks>2022-11-10 21:31:56 +0300
commit4c8ec476ba14d0ef5da55817025cc372a64fcb22 (patch)
treede7bddb7ba518d828f2cb0deedf30cb0dac0aa96
parent1c385f463e488dc9a280fc24352740e919c6bc3f (diff)
Support checking for required members in minimal APIssafia/asparams-required-members
-rw-r--r--src/Http/.vscode/launch.json26
-rw-r--r--src/Http/.vscode/tasks.json41
-rw-r--r--src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs152
3 files changed, 219 insertions, 0 deletions
diff --git a/src/Http/.vscode/launch.json b/src/Http/.vscode/launch.json
new file mode 100644
index 0000000000..14da52882b
--- /dev/null
+++ b/src/Http/.vscode/launch.json
@@ -0,0 +1,26 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "name": ".NET Core Launch (console)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "../artifacts/bin/Microsoft.AspNetCore.Http.Extensions.Tests/Debug/net7.0/Microsoft.AspNetCore.Http.Extensions.Tests.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/Http.Extensions/test",
+ // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
+ "console": "internalConsole",
+ "stopAtEntry": false
+ },
+ {
+ "name": ".NET Core Attach",
+ "type": "coreclr",
+ "request": "attach"
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/Http/.vscode/tasks.json b/src/Http/.vscode/tasks.json
new file mode 100644
index 0000000000..f6ad7fd5c2
--- /dev/null
+++ b/src/Http/.vscode/tasks.json
@@ -0,0 +1,41 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/Http.Extensions/test/Microsoft.AspNetCore.Http.Extensions.Tests.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "publish",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "publish",
+ "${workspaceFolder}/Http.Extensions/test/Microsoft.AspNetCore.Http.Extensions.Tests.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "watch",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "watch",
+ "run",
+ "--project",
+ "${workspaceFolder}/Http.Extensions/test/Microsoft.AspNetCore.Http.Extensions.Tests.csproj"
+ ],
+ "problemMatcher": "$msCompile"
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs
index d4a3ee1e42..17eea596ea 100644
--- a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs
+++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs
@@ -7047,6 +7047,158 @@ public partial class RequestDelegateFactoryTests : LoggedTest
Assert.Same(options.EndpointBuilder.Metadata, result.EndpointMetadata);
}
+ private class ParameterListRequiredStringFromDifferentSources
+ {
+ public HttpContext? HttpContext { get; set; }
+
+ [FromRoute]
+ public required string RequiredRouteParam { get; set; }
+
+ [FromQuery]
+ public required string RequiredQueryParam { get; set; }
+
+ [FromHeader]
+ public required string RequiredHeaderParam { get; set; }
+ }
+
+ [Fact]
+ public async Task RequestDelegateFactory_AsParameters_SupportsRequiredMember()
+ {
+ // Arrange
+ static void TestAction([AsParameters] ParameterListRequiredStringFromDifferentSources args) { }
+
+ var httpContext = CreateHttpContext();
+
+ var factoryResult = RequestDelegateFactory.Create(TestAction);
+ var requestDelegate = factoryResult.RequestDelegate;
+
+ // Act
+ await requestDelegate(httpContext);
+
+ // Assert that the required modifier disables optionality
+ // on members when they are not nullable.
+ Assert.Equal(400, httpContext.Response.StatusCode);
+
+ var logs = TestSink.Writes.ToArray();
+
+ Assert.Equal(3, logs.Length);
+
+ Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[0].EventId);
+ Assert.Equal(LogLevel.Debug, logs[0].LogLevel);
+ Assert.Equal(@"Required parameter ""string RequiredRouteParam"" was not provided from route.", logs[0].Message);
+
+ Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[1].EventId);
+ Assert.Equal(LogLevel.Debug, logs[1].LogLevel);
+ Assert.Equal(@"Required parameter ""string RequiredQueryParam"" was not provided from query string.", logs[1].Message);
+
+ Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[2].EventId);
+ Assert.Equal(LogLevel.Debug, logs[2].LogLevel);
+ Assert.Equal(@"Required parameter ""string RequiredHeaderParam"" was not provided from header.", logs[2].Message);
+ }
+
+ private class ParameterListRequiredNullableStringFromDifferentSources
+ {
+ public HttpContext? HttpContext { get; set; }
+
+ [FromRoute]
+ public required StringValues? RequiredRouteParam { get; set; }
+
+ [FromQuery]
+ public required StringValues? RequiredQueryParam { get; set; }
+
+ [FromHeader]
+ public required StringValues? RequiredHeaderParam { get; set; }
+ }
+
+ [Fact]
+ public async Task RequestDelegateFactory_AsParameters_SupportsNullableRequiredMember()
+ {
+ // Arrange
+ static void TestAction([AsParameters] ParameterListRequiredStringFromDifferentSources args)
+ {
+ args.HttpContext!.Items.Add("RequiredRouteParam", args.RequiredRouteParam);
+ args.HttpContext!.Items.Add("RequiredQueryParam", args.RequiredQueryParam);
+ args.HttpContext!.Items.Add("RequiredHeaderParam", args.RequiredHeaderParam);
+ }
+
+ var httpContext = CreateHttpContext();
+
+ var factoryResult = RequestDelegateFactory.Create(TestAction);
+ var requestDelegate = factoryResult.RequestDelegate;
+
+ // Act
+ await requestDelegate(httpContext);
+
+ // Assert that when properties are required but nullable
+ // we evaluate them as optional because required members
+ // must be initialized but they can be initialized to null
+ // when an NRT is required.
+ Assert.Equal(200, httpContext.Response.StatusCode);
+
+ Assert.Null(httpContext.Items["RequiredRouteParam"]);
+ Assert.Null(httpContext.Items["RequiredQueryParam"]);
+ Assert.Null(httpContext.Items["RequiredHeaderParam"]);
+ }
+
+#nullable disable
+ private class ParameterListMixedRequiredStringsFromDifferentSources
+ {
+ public HttpContext HttpContext { get; set; }
+
+ [FromRoute]
+ public required string RequiredRouteParam { get; set; }
+
+ [FromRoute]
+ public string OptionalRouteParam { get; set; }
+
+ [FromQuery]
+ public required string RequiredQueryParam { get; set; }
+
+ [FromQuery]
+ public string OptionalQueryParam { get; set; }
+
+ [FromHeader]
+ public required string RequiredHeaderParam { get; set; }
+
+ [FromHeader]
+ public string OptionalHeaderParam { get; set; }
+ }
+
+ [Fact]
+ public async Task RequestDelegateFactory_AsParameters_SupportsRequiredMember_NullabilityDisabled()
+ {
+ // Arange
+ static void TestAction([AsParameters] ParameterListMixedRequiredStringsFromDifferentSources args) { }
+
+ var httpContext = CreateHttpContext();
+
+ var factoryResult = RequestDelegateFactory.Create(TestAction);
+ var requestDelegate = factoryResult.RequestDelegate;
+
+ // Act
+ await requestDelegate(httpContext);
+
+ // Assert that we only execute required parameter
+ // checks for members that have the required modifier
+ Assert.Equal(400, httpContext.Response.StatusCode);
+
+ var logs = TestSink.Writes.ToArray();
+ Assert.Equal(3, logs.Length);
+
+ Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[0].EventId);
+ Assert.Equal(LogLevel.Debug, logs[0].LogLevel);
+ Assert.Equal(@"Required parameter ""string RequiredRouteParam"" was not provided from route.", logs[0].Message);
+
+ Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[1].EventId);
+ Assert.Equal(LogLevel.Debug, logs[1].LogLevel);
+ Assert.Equal(@"Required parameter ""string RequiredQueryParam"" was not provided from query string.", logs[1].Message);
+
+ Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[2].EventId);
+ Assert.Equal(LogLevel.Debug, logs[2].LogLevel);
+ Assert.Equal(@"Required parameter ""string RequiredHeaderParam"" was not provided from header.", logs[2].Message);
+ }
+#nullable enable
+
private DefaultHttpContext CreateHttpContext()
{
var responseFeature = new TestHttpResponseFeature();