'ASP.NET Core form ignoring asp-controller
I'm working on an ASP.NET Core application. Part of it is an image gallery which allows the user to upload images. On localhost this works as expected, but when I deploy (AWS Lambda with API Gateway) the upload button is missing the asp-controller
attribute.
I have a GalleryController.cs
with two methods:
public async Task<IActionResult> Index()
{
// Pulls images from aws S3, adds them to a list and passes them to then returns View();
}
[HttpPost("Upload")]
public async Task<IActionResult> Upload(List<IFormFile> files)
{
// For each iformFile in Files, create a thumbnail in memory then upload both the original and thumbnail to aws s3 bucket
return RedirectToAction("Index");
}
The routing configuration in my startup.cs
has been left default from the boilerplate:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
The folder structure for my Views
is as follows:
├── Views
│ ├── Gallery
│ │ ├── _GalleryPartial.cshtml
│ │ ├── Index.cshtml
│ │ └── _UploadPartial.cshtml
│ ├── Home
│ │ └── Index.cshtml
_UploadPartial.cshtml looks like this:
<div class="container">
<div class="col-md-12">
<form method="post" enctype="multipart/form-data" asp-controller="Gallery" asp-action="Upload">
<div class="form-group">
<div class="col-md-12">
<div class="col-md-6">
<input class="form-control-file" type="file" name="files" multiple />
</div>
<div class="col-md-6">
<input class="form-control-file" type="submit" value="Upload" />
</div>
</div>
</div>
</form>
</div>
</div>
<hr>
As you can see I have asp-controller="Gallery"
and asp-action="Upload"
, but when I inspect the form after deploying it to lambda it seems to be ignoring the controller tag.
Looks like I don't have enough points to embed an image therefore SO created a link
edit
As per request, here is the contents of Gallery/index.cshtml
@model Dictionary<string, string>
@{
ViewData["Title"] = "News page";
}
<center>
@await Html.PartialAsync("_uploadPartial")
@await Html.PartialAsync("_galleryPartial")
</center>
And the contents of my startup.cs
:
using Amazon.S3;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Redacted.Helpers;
namespace Redacted;
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews().AddRazorRuntimeCompilation();
services.AddAWSService<IAmazonS3>();
services.AddTransient<ImageHelper>();
services.AddRazorPages();
services.AddMvcCore();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
// Signin:
options.ResponseType = Environment.GetEnvironmentVariable("ResponseType");
options.MetadataAddress = Environment.GetEnvironmentVariable("MetadataAddress");
options.ClientId = Environment.GetEnvironmentVariable("ClientId");
options.ClientSecret = Environment.GetEnvironmentVariable("ClientSecret");
// Signout
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProviderForSignOut = OnRedirectToIdentityProviderForSignOut
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("Admins", policy => policy.RequireAssertion(context => context.User.HasClaim(c => c.Type == "cognito:groups" && c.Value == "admins")));
options.AddPolicy("Users", policy => policy.RequireAssertion(context => context.User.HasClaim(c => c.Type == "cognito:groups" && c.Value == "users" || c.Value == "admins")));
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
private Task OnRedirectToIdentityProviderForSignOut(RedirectContext context)
{
context.ProtocolMessage.Scope = "openid";
context.ProtocolMessage.ResponseType = "code";
var cognitoDomain = Environment.GetEnvironmentVariable("CognitoDomain");
var clientId = Environment.GetEnvironmentVariable("ClientId");
var logoutUrl = Environment.GetEnvironmentVariable("LogoutUri");
context.ProtocolMessage.IssuerAddress = $"{cognitoDomain}/logout?client_id={clientId}&logout_uri={logoutUrl}&redirect_uri={logoutUrl}";
// delete cookies
context.Properties.Items.Remove(CookieAuthenticationDefaults.AuthenticationScheme);
// close openid session
context.Properties.Items.Remove(OpenIdConnectDefaults.AuthenticationScheme);
return Task.CompletedTask;
}
}
Solution 1:[1]
After some time debugging I discovered the problem was with the infrastructure itsself. As i'm using API Gateway to route traffic to my lambda I was missing routes for POST. Simply adding a POST route method to /{proxy+}
under api gateway/routes in the aws console OR using a terraform resource block similar to this:
resource "aws_apigatewayv2_route" "get_route" {
api_id = aws_apigatewayv2_api.lambda.id
route_key = "GET /{proxy+}"
target = "integrations/${aws_apigatewayv2_integration.proxy_integration.id}"
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Mark Pendlebury |