Skip to content

Commit

Permalink
Merge branch 'alistair-file_upload_support'
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremydmiller committed Nov 9, 2023
2 parents e5ba883 + e6fd371 commit d9e2a84
Show file tree
Hide file tree
Showing 116 changed files with 132 additions and 7,134 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ export default {
{text: 'Using as Mediator', link: '/guide/http/mediator'},
{text: 'Multi-Tenancy and ASP.Net Core', link: '/guide/http/multi-tenancy'},
{text: 'Publishing Messages', link: '/guide/http/messaging'},
{text: 'Uploading Files', link: '/guide/http/files'},
{text: 'Integration with Sagas', link: '/guide/http/sagas'},
{text: 'Integration with Marten', link: '/guide/http/marten'},
{text: 'Fluent Validation', link: '/guide/http/fluentvalidation'},
Expand Down
9 changes: 9 additions & 0 deletions docs/guide/http/files.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Uploading Files

As of 1.11.0, Wolverine supports file uploads through the standard ASP.Net Core `IFile` or `IFileCollection` types. All you need
to do to is to have an input parameter to your Wolverine.HTTP endpoint of these types like so:

snippet: sample_using_file_uploads

See [Upload files in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-7.0)
for more information about these types.
80 changes: 80 additions & 0 deletions src/Http/Wolverine.Http/CodeGen/FromFileStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System.Reflection;
using JasperFx.CodeGeneration;
using JasperFx.CodeGeneration.Frames;
using JasperFx.CodeGeneration.Model;
using Lamar;
using Microsoft.AspNetCore.Http;

namespace Wolverine.Http.CodeGen;

public class FromFileStrategy : IParameterStrategy
{
public bool TryMatch(HttpChain chain, IContainer container, ParameterInfo parameter, out Variable? variable)
{
if (parameter.ParameterType == typeof(IFormFile))
{
var frame = new FromFileValue(parameter);
chain.Middleware.Add(frame);
variable = frame.Variable;
return true;
} else if (parameter.ParameterType == typeof(IFormFileCollection))
{
var frame = new FromFileValues(parameter);
chain.Middleware.Add(frame);
variable = frame.Variable;
return true;
}
variable = null;
return false;
}
}

internal class FromFileValue : SyncFrame
{
private Variable? _httpContext;
public FromFileValue(ParameterInfo parameter)
{
Variable = new Variable(parameter.ParameterType, parameter.Name!, this);
}

public Variable Variable { get; }

public override IEnumerable<Variable> FindVariables(IMethodVariables chain)
{
_httpContext = chain.FindVariable(typeof(HttpContext));
yield return _httpContext;
}

public override void GenerateCode(GeneratedMethod method, ISourceWriter writer)
{
writer.WriteComment("Retrieve header value from the request");
writer.Write(
$"var {Variable.Usage} = {nameof(HttpHandler.ReadSingleFormFileValue)}({_httpContext!.Usage});");
Next?.GenerateCode(method, writer);
}
}

internal class FromFileValues : SyncFrame
{
private Variable? _httpContext;
public FromFileValues(ParameterInfo parameter)
{
Variable = new Variable(parameter.ParameterType, parameter.Name!, this);
}

public Variable Variable { get; }

public override IEnumerable<Variable> FindVariables(IMethodVariables chain)
{
_httpContext = chain.FindVariable(typeof(HttpContext));
yield return _httpContext;
}

public override void GenerateCode(GeneratedMethod method, ISourceWriter writer)
{
writer.WriteComment("Retrieve header value from the request");
writer.Write(
$"var {Variable.Usage} = {nameof(HttpHandler.ReadManyFormFileValues)}({_httpContext!.Usage});");
Next?.GenerateCode(method, writer);
}
}
4 changes: 4 additions & 0 deletions src/Http/Wolverine.Http/HttpGraph.ParameterMatching.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ public partial class HttpGraph
{
private readonly List<IParameterStrategy> _strategies = new()
{
<<<<<<< HEAD

Check failure on line 10 in src/Http/Wolverine.Http/HttpGraph.ParameterMatching.cs

View workflow job for this annotation

GitHub Actions / build

Merge conflict marker encountered

Check failure on line 10 in src/Http/Wolverine.Http/HttpGraph.ParameterMatching.cs

View workflow job for this annotation

GitHub Actions / build

Merge conflict marker encountered
=======

Check failure on line 11 in src/Http/Wolverine.Http/HttpGraph.ParameterMatching.cs

View workflow job for this annotation

GitHub Actions / build

Merge conflict marker encountered

Check failure on line 11 in src/Http/Wolverine.Http/HttpGraph.ParameterMatching.cs

View workflow job for this annotation

GitHub Actions / build

Merge conflict marker encountered
new FromFileStrategy(),
>>>>>>> alistair-file_upload_support

Check failure on line 13 in src/Http/Wolverine.Http/HttpGraph.ParameterMatching.cs

View workflow job for this annotation

GitHub Actions / build

Merge conflict marker encountered

Check failure on line 13 in src/Http/Wolverine.Http/HttpGraph.ParameterMatching.cs

View workflow job for this annotation

GitHub Actions / build

Merge conflict marker encountered
new HttpChainParameterAttributeStrategy(),
new FromServicesParameterStrategy(),
new MessageBusStrategy(),
Expand Down
10 changes: 10 additions & 0 deletions src/Http/Wolverine.Http/HttpHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ public static string[] ReadManyHeaderValues(HttpContext context, string headerKe
return context.Request.Headers[headerKey].ToArray()!;
}

public static IFormFile? ReadSingleFormFileValue(HttpContext context)
{
return context.Request.Form.Files.SingleOrDefault();
}

public static IFormFileCollection? ReadManyFormFileValues(HttpContext context)
{
return context.Request.Form.Files;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Task WriteString(HttpContext context, string text)
{
Expand Down
28 changes: 28 additions & 0 deletions src/Http/WolverineWebApi/FileUploadEndpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Wolverine.Http;

namespace WolverineWebApi;

#region sample_using_file_uploads

public class FileUploadEndpoint
{
// If you have exactly one file upload, take
// in IFormFile
[WolverinePost("/upload/file")]
public static Task Upload(IFormFile file)
{
// access the file data
return Task.CompletedTask;
}

// If you have multiple files at one time,
// use IFormCollection
[WolverinePost("/upload/files")]
public static Task Upload(IFormFileCollection files)
{
// access files
return Task.CompletedTask;
}
}

#endregion
Loading

0 comments on commit d9e2a84

Please sign in to comment.