Skip to content

Commit 6b65c96

Browse files
committed
Add support for accessing file body metadata from content headers: MimeType and FileName
1 parent 153334a commit 6b65c96

27 files changed

Lines changed: 250 additions & 117 deletions

shared/Http/HttpParameterName.cs

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Dibix.Http
2+
{
3+
internal static class SpecialHttpParameterName
4+
{
5+
public const string Body = "body";
6+
public const string MediaType = "mediaType";
7+
public const string FileName = "fileName";
8+
}
9+
}

shared/ParameterSources/BodyParameterSource.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@
44
internal sealed class BodyParameterSource : ActionParameterSourceDefinition<BodyParameterSource>
55
{
66
public const string RawPropertyName = "$RAW";
7+
public const string MediaTypePropertyName = "$MEDIATYPE";
8+
public const string FileNamePropertyName = "$FILENAME";
79
}
810
}

src/Dibix.Http.Host/Model/HttpRequestDescriptor.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.Linq;
55
using System.Net;
66
using System.Security.Claims;
7-
using System.Threading.Tasks;
87
using Dibix.Http.Server;
98
using Dibix.Http.Server.AspNetCore;
109
using Microsoft.AspNetCore.Authentication;
@@ -23,7 +22,11 @@ public HttpRequestDescriptor(HttpRequest request)
2322

2423
public string GetPath() => $"{_request.PathBase}{_request.Path}";
2524

26-
public Task<Stream> GetBody() => Task.FromResult(_request.Body);
25+
public Stream GetBody() => _request.Body;
26+
27+
public string? GetBodyMediaType() => _request.GetTypedHeaders().ContentType?.MediaType.Value;
28+
29+
public string? GetBodyFileName() => _request.GetTypedHeaders().ContentDisposition?.FileName.Value;
2730

2831
public IEnumerable<string> GetHeaderValues(string name) => _request.Headers[name];
2932

src/Dibix.Http.Server.AspNet/HttpRequestMessageDescriptor.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ public HttpRequestMessageDescriptor(HttpRequestMessage request)
2121

2222
public string GetPath() => RequestMessage.RequestUri!.AbsolutePath;
2323

24-
public async Task<Stream> GetBody() => RequestMessage.Content != null ? await RequestMessage.Content.ReadAsStreamAsync().ConfigureAwait(false) : null;
24+
public Stream GetBody() => RequestMessage.Content?.ReadAsStreamAsync().GetAwaiter().GetResult();
25+
26+
public string GetBodyMediaType() => RequestMessage.Content?.Headers.ContentType?.MediaType;
27+
28+
public string GetBodyFileName() => RequestMessage.Content?.Headers.ContentDisposition?.FileName;
2529

2630
public IEnumerable<string> GetHeaderValues(string name) => RequestMessage.Headers.TryGetValues(name, out IEnumerable<string> values) ? values : Enumerable.Empty<string>();
2731

src/Dibix.Http.Server/Dibix.Http.Server.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
<Compile Include="..\..\shared\Http\HttpApiMethod.cs" Link="Model\%(Filename)%(Extension)" />
1313
<Compile Include="..\..\shared\Http\HttpErrorResponseUtility.cs" Link="Runtime\%(Filename)%(Extension)" />
1414
<Compile Include="..\..\shared\Http\HttpParameterUtility.cs" Link="Runtime\%(Filename)%(Extension)" />
15-
<Compile Include="..\..\shared\Http\HttpParameterName.cs" Link="Runtime\%(Filename)%(Extension)" />
1615
<Compile Include="..\..\shared\Http\ModelContextProtocolType.cs" Link="Runtime\%(Filename)%(Extension)" />
1716
<Compile Include="..\..\shared\Http\RouteBuilder.cs" Link="Utilities\%(Filename)%(Extension)" />
17+
<Compile Include="..\..\shared\Http\SpecialHttpParameterName.cs" Link="Runtime\%(Filename)%(Extension)" />
1818
<Compile Include="..\..\shared\Metadata\IPropertyDescriptor.cs" Link="Model\%(Filename)%(Extension)" />
1919
<Compile Include="..\..\shared\Metadata\NestedEnumerablePair.cs" Link="Model\%(Filename)%(Extension)" />
2020
<Compile Include="..\..\shared\Metadata\PrimitiveType.cs" Link="Model\%(Filename)%(Extension)" />

src/Dibix.Http.Server/Model/IHttpRequestDescriptor.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Security.Claims;
5-
using System.Threading.Tasks;
65

76
namespace Dibix.Http.Server
87
{
98
public interface IHttpRequestDescriptor
109
{
1110
string GetPath();
12-
Task<Stream> GetBody();
11+
Stream GetBody();
12+
string GetBodyMediaType();
13+
string GetBodyFileName();
1314
IEnumerable<string> GetHeaderValues(string name);
1415
IEnumerable<string> GetAcceptLanguageValues();
1516
ClaimsPrincipal GetUser();

src/Dibix.Http.Server/Providers/BodyParameterSourceProvider.cs

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
using System;
2-
using System.IO;
32
using System.Linq.Expressions;
4-
using System.Runtime.CompilerServices;
5-
using System.Threading.Tasks;
63

74
namespace Dibix.Http.Server
85
{
@@ -12,19 +9,36 @@ internal sealed class BodyParameterSourceProvider : NonUserParameterSourceProvid
129

1310
public override void Resolve(IHttpParameterResolutionContext context)
1411
{
15-
if (context.PropertyPath != BodyParameterSource.RawPropertyName)
12+
switch (context.PropertyPath)
1613
{
17-
Type instanceType = context.ActionMetadata.SafeGetBodyContract();
18-
Expression instanceValue = Expression.Call(typeof(HttpParameterResolverUtility), nameof(HttpParameterResolverUtility.ReadBody), new[] { instanceType }, context.ArgumentsParameter);
19-
context.ResolveUsingInstanceProperty(instanceType, instanceValue, ensureNullPropagation: true);
20-
}
21-
else
22-
{
23-
// TODO: Can be null!
24-
Expression getBodyCall = Expression.Call(context.RequestParameter, nameof(IHttpRequestDescriptor.GetBody), Type.EmptyTypes);
25-
Expression getAwaiterCall = Expression.Call(getBodyCall, typeof(Task<Stream>).SafeGetMethod(nameof(Task<Stream>.GetAwaiter)));
26-
Expression getResultCall = Expression.Call(getAwaiterCall, nameof(TaskAwaiter.GetResult), Type.EmptyTypes);
27-
context.ResolveUsingValue(getResultCall);
14+
case BodyParameterSource.RawPropertyName:
15+
{
16+
Expression getBodyCall = Expression.Call(context.RequestParameter, nameof(IHttpRequestDescriptor.GetBody), Type.EmptyTypes);
17+
context.ResolveUsingValue(getBodyCall);
18+
break;
19+
}
20+
21+
case BodyParameterSource.MediaTypePropertyName:
22+
{
23+
Expression getBodyMediaTypeCall = Expression.Call(context.RequestParameter, nameof(IHttpRequestDescriptor.GetBodyMediaType), Type.EmptyTypes);
24+
context.ResolveUsingValue(getBodyMediaTypeCall);
25+
break;
26+
}
27+
28+
case BodyParameterSource.FileNamePropertyName:
29+
{
30+
Expression getBodyFileNameCall = Expression.Call(context.RequestParameter, nameof(IHttpRequestDescriptor.GetBodyFileName), Type.EmptyTypes);
31+
context.ResolveUsingValue(getBodyFileNameCall);
32+
break;
33+
}
34+
35+
default:
36+
{
37+
Type instanceType = context.ActionMetadata.SafeGetBodyContract();
38+
Expression instanceValue = Expression.Call(typeof(HttpParameterResolverUtility), nameof(HttpParameterResolverUtility.ReadBody), [instanceType], context.ArgumentsParameter);
39+
context.ResolveUsingInstanceProperty(instanceType, instanceValue, ensureNullPropagation: true);
40+
break;
41+
}
2842
}
2943
}
3044
}

src/Dibix.Http.Server/Runtime/HttpParameterResolver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,7 @@ private static void CollectApiParameters(HttpParameterResolutionMethod method, I
825825
// If the target method does not expect a parameter of the body type, we have to add a pseudo parameter
826826
// That way ASP.NET will use a formatter binding and read the body for us, which we can later read from the arguments dictionary.
827827
if (parameters.All(x => x.ParameterType != bodyContract))
828-
method.AddParameter(HttpParameterName.Body, bodyContract, HttpParameterLocation.NonUser, isOptional: false);
828+
method.AddParameter(SpecialHttpParameterName.Body, bodyContract, HttpParameterLocation.NonUser, isOptional: false);
829829
}
830830

831831
foreach (HttpParameterInfo parameter in parameters.Where(x => x.Location != HttpParameterLocation.NonUser).DistinctBy(x => x.ApiParameterName))

src/Dibix.Http.Server/Runtime/HttpParameterResolverUtility.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public static TResult ReadArgument<TResult>(IDictionary<string, object> argument
2222
return (TResult)value;
2323
}
2424

25-
public static TBody ReadBody<TBody>(IDictionary<string, object> arguments) => ReadArgument<TBody>(arguments, HttpParameterName.Body);
25+
public static TBody ReadBody<TBody>(IDictionary<string, object> arguments) => ReadArgument<TBody>(arguments, SpecialHttpParameterName.Body);
2626

2727
public static Expression BuildWritableArgumentAccessorExpression(Expression argumentsParameter, string key)
2828
{

0 commit comments

Comments
 (0)