1

I have an ASP .Net Core 2.2 Web API. In it, I have an endpoint where the front-end (Angular app) can send a file to the API, and the API in turn uploads this file to an Amazon S3 bucket (us-east-1 region).

This works perfectly while I'm debugging in Visual Studio, but when I publish to my server (Windows Server 2016) I get the following exception:

Error: Exception has been thrown by the target of an invocation. at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Amazon.Extensions.NETCore.Setup.ClientFactory.CreateClient(Type serviceInterfaceType, AWSCredentials credentials, ClientConfig config) at Amazon.Extensions.NETCore.Setup.ClientFactory.CreateServiceClient(ILogger logger, Type serviceInterfaceType, AWSOptions options) at Amazon.Extensions.NETCore.Setup.ClientFactory.CreateServiceClient(IServiceProvider provider) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(IServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitSingleton(SingletonCallSite singletonCallSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(IServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(IServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitSingleton(SingletonCallSite singletonCallSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(IServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0. b__0(ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType) at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method(Closure , IServiceProvider , Object[] ) at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0. b__0(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0. g__CreateController|0(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext) at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext) at PropWorx.API.Middlewares.TenantIdentifier.Invoke(HttpContext httpContext, SharedContext sharedContext) in C:\Users\fabsr\source\repos\PropWorx.API\PropWorx.API\Middlewares\TenantIdentifier.cs:line 73 at Microsoft.AspNetCore.Builder.Extensions.MapWhenMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Builder.Extensions.MapWhenMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.Invoke(HttpContext context)

I've created the "credentials" file in the %UserProfile%.aws folder.

I've added the following two NuGet packages to the project:

  • AWSSDK.Extensions.NETCore.Setup
  • AWSSDK.S3

I have this in my Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAWSService<IAmazonS3>();
    services.AddSingleton<IS3Service, S3Service>();
}

S3Service.cs looks like this:

public class S3Service : IS3Service
{
    private readonly IAmazonS3 _client;

    public S3Service(IAmazonS3 client)
    {
        _client = client;
    }

    // Some methods to upload and delete objects... not important
}

I inject this service to one of my controllers:

public class FilesController : ControllerBase
{
    private readonly IS3Service _s3Service;

    public FilesController(IS3Service s3Service)
    {
        _s3Service = s3Service;
    }

    // Some actions to receive the file... not important
}

The error happens as soon as I call any action in this controller. In fact, the error appears to be happening when the S3Service is injected into the controller's constructor. If I remove the constructor injection, i.e.:

public class FilesController : ControllerBase
{
    private readonly IS3Service _s3Service;

    public FilesController()
    {
    }

    // Some actions to receive the file... not important
}

The error goes away (but of course doesn't work properly)

Any ideas? Like I said, this works perfectly on my laptop while debugging in VS2017. But it doesn't work when published to the server. All other controllers in the API work fine. It appears to be a problem at the point of injecting the S3Service in the constructor...

Fabricio Rodriguez
  • 3,769
  • 11
  • 48
  • 101
  • 1
    I faced the same issue while working with S3 buckets. If I used DI to inject IAmazonS3, it didn't pick the configuration values and so was not able to resolve the object. I used it without DI and it was able to work. – mukesh joshi Feb 13 '19 at 09:31

2 Answers2

1

I changed my code to get IAmazonS3 insatance not through but directly instantiating it inside my IS3Serice method and it worked. Somehow the DI is not able to pick config values and that's why was not able to resolve the object.

You can get some information about this from here

I went through the actual documentation https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-netcore.html

and found following:

If you look at the Debug tab in your project's properties, you can see this file is set to Development. This works great for local testing because you can put your configuration in the appsettings.Development.json file, which is read-only during local testing. When you deploy an Amazon EC2 instance that has EnvironmentName set to Production, this file is ignored and the AWS SDK for .NET falls back to the IAM credentials and region configured for the Amazon EC2 instance.

So this could be the reason that your initial configurations didn't work as you were trying to use access keys on production.

Also have a look at following answer

How to set credentials on AWS SDK on NET Core?

mukesh joshi
  • 584
  • 5
  • 19
  • Thanks mukesh... I am testing that now... will let you know asap – Fabricio Rodriguez Feb 13 '19 at 14:22
  • Quick question mukesh... perhaps you know... I'm following your suggestion, and I think I'm making progress, but I get the feeling that it's not reading the settings... I have the settings in both the %UserProfile%/.aws/credentials file, as well as in appsettings.json and appsettings.Development.json. I also have this line in my ConfigureServices method (in Startup.cs): services.AddDefaultAWSOptions(Configuration.GetAWSOptions()); which I believe is for it to read the settings from the appsettings.json file? Am I missing something? – Fabricio Rodriguez Feb 14 '19 at 05:53
1

I kept it simple. In my local machines, I was keeping the keys in the user-secrets and on the prod server I was keeping those in the IIS configuration file. and was just accessing these values from ConfigurationManager. Basically it was using the new AmazonS3Client(string, string, string) syntax. Also I was using a farily older version of .net core there is a possibility that the issue was fixed. Check out this which seems working.

mukesh joshi
  • 584
  • 5
  • 19
  • Thank you, I'm reading it now... will get back to you shortly – Fabricio Rodriguez Feb 14 '19 at 07:47
  • I don't know what I'm doing wrong... At the moment of uploading the file, it throws an exception, and the only error message is "The operation was canceled." I've added the AWS credentials to the credentials file in %UserProfile%/.aws/credentials, I've added it using Powershell and the AWS toolkit, and I've added it to my appconfig.json file. None have worked. I've even tried adding it as environmental variables. I'm wondering if it is even a credentials issue? The exact same code runs perfectly on my laptop. As soon as I publish to server, it doesnt. – Fabricio Rodriguez Feb 15 '19 at 11:13
  • Can you try adding your key/value pair to IIS configuration files and test it ? Also I hope you know that usually there are different keys for development and production environments, if you have different buckets for prod/dev. Also IAM (identity and access managment) on the AWS bucket can also be done at server level and then you will not even need the key/value (which is a recommended way) – mukesh joshi Feb 15 '19 at 11:19
  • Thanks for all your support mukesh. For now, I give up lol. I've hard-coded the access key and secret key into the source code when creating the S3Client. It works now. FOr some reason, when in production, my code doesn't want to read the credentials from the SDK store nor from the shared credentials file (in %UserProfile%\.aws\credentials). I don't know why. It works perfectly when in development (i.e. when running from Visual Studio). But hard coding the keys into the source code works for now... – Fabricio Rodriguez Feb 18 '19 at 09:08
  • If you don't want to hard code, you can store them in the IIS configuration settings (if you are using IIS) and then use configuration manager to pick these things up. – mukesh joshi Feb 18 '19 at 09:18