How to serve static files from .NET Core API? - asp.net

The API is using .NET Core 3.1 and the folder structure is as follows:
Solution
--Project1
--Project2
Where Project 2 depends on Project 1 and is the entry point.
The API is hosted on kubernetes and a NFS share is mounted at /storage. The API will need to retrieve and serve files in this file share. So what I did was using the following in my startup.cs in Project2:
app.UseFileServer(new FileServerOptions()
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), #"storage")),//this will return /app/storage
RequestPath = new PathString("/resources")
});
I know one of the file is there by checking:
System.IO.File.Exists(#"/app/storage/test.JPG");
However, when I tried to access the file by going to http://myapiurl/resources/test.JPG, I always got a 404 not found.
I was wondering what I was missing?

You're using UseFileServer, but you're not enabling directory browsing nor serving default files, so I'd recommend narrowing instead to using UseStaticFiles.
The easiest way to enable this is to just place the following in your Startup.cs file in your Configure() method.
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(env.ContentRootPath, "storage")),
RequestPath = "/resources"
});
My guess for why this isn't working for you is that Directory.GetCurrentDirectory, when running in K8 isn't yielding the same path you see when running locally, so it's not ultimately pointing at /app/storage (which is why your File.Exists check works fine because you're not building the path out dynamically).
Use some logging to verify that it's pulling from the right path in your configuration at runtime.

I had a similar problem with static files after switching from AppService to k8s with ingress. It turned out to be problem with case-sensitivity in the URL. Some directories were uppercase, but in paths I had all lowercase letters. It was working locally on Windows and on AppService but not on k8s.

Related

IIS Shadow Copied DLL accessing dependency DLLs from bin location

I am trying to resolve an issue with ASP.Net Framework 4.8 site using EFCore 3.1.16 in IIS. Microsoft.Data.SqlClient has a process lock on SNI.dll which causes issues with xcopy deployment in IIS.
I have tried a strategy of copying the SNI.dll to the same shadow copy location as Microsoft.Data.SqlClient so it doesn't have to try and access the DLL in the bin folder as outlined in https://github.com/lscorcia/sqlclient.snishadowcopy.
// Look for the main Microsoft.Data.SqlClient assembly in the
// shadow copy path
var sqlClientShadowAssembly = Directory.GetFiles(
currentShadowPath, "Microsoft.Data.SqlClient.dll",
SearchOption.AllDirectories).FirstOrDefault();
// Extract the directory information from the shadow assembly path
var sqlClientShadowPath =
Path.GetDirectoryName(sqlClientShadowAssembly);
// Find out the process bitness and choose the appropriate native
// assembly
var moduleName = Environment.Is64BitProcess ? "x86\\SNI.dll"
: "x64\\SNI.dll";
// Compute the source and target paths for the native assembly
var sourceFile = Path.Combine(currentPrivatePath, moduleName);
var targetFile = Path.Combine(sqlClientShadowPath, "SNI.dll");
File.Copy(sourceFile, targetFile);
However, it still tries to access the bin location first instead of the sni.dll that is in the same folder location.
I have checked that the Microsoft.Data.SqlClient in the shadow location is being used correctly by deleting the DLL and confirming that a FileNotFound exception is thrown.I have also tried copying directly into the same folder and also copying into an x64 sub folder in the shadow location.
In my case, the error occured only when my IIS application is located on an UNC path (e.g. "\\myserver\myshare\myapplication"). Here is a workaround that worked in my scenario.
Use P/Invoke to SetDllDirectory:
[DllImport(#"kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetDllDirectory(string lpPathName);
(See also this MSDN article about DLL load order)
Then, early in my Global.asax.cs Application_Start method (before any DB calls), call it like this:
SetDllDirectory(Server.MapPath(#"~/bin"))
After that, everything works as expected.
I still to consider this to be kind of a hack/workaround, but at least, it is working now in my scenario.
As I do understand, you can call SetDllDirectory multiple times to add additional directories (i.e. not overwrite the existing one).
So in case someone reading this might have other assemblies that refer to native DLLs in "x86" or "x64" folders, one might do something like this:
var bitness = Environment.Is64BitProcess ? #"x64" : #"x86";
SetDllDirectory(Server.MapPath($#"~/bin/{bitness}"));
I've also tried serving my test application from a local path (like "C:\Inetpub\wwwroot") and here, the error does not occur, even when not calling SetDllDirectory.
I'm still not sure why the error occurs for UNC paths only, and not for local paths, as I would expect that the shadow copied managed assemblies to fail the DllImports, too.
(I've also posted the above in this GitHub issue)

.NET Core, Parallel Run of the same application with different configuration possible?

I am trying to get my head around the .netcore way of configuring an application.
Szenario:
I am running the same rest api (application) for client1 and client2 on the same server in the DEV-Environment.
We have environment specific configurations as well as client specific configurations.
"Old" Way:
With web.config transformations, I created build configurations for dev-client1 and dev-client2 and set the values accordingly...this all worked fine.
"New" Way:
As the appsettings.json depends on the windows environment variable (one per server) and therefore run into troubles. I could not find a way so far to run the apps in parallel on the same server with different configurations.
I might have missunderstood the new way of configuring an application, therefore any help would be appreciated.
in a typical new .NET Core web app, you will see this code in the Startup.cs:
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{environment.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables()
.Build();
all you need to do is add another appsettings file with client-specific settings, like:
.AddJsonFile($"appsettings.client.json", optional: true)
then have your build script copy in the correct build configuration to appsettings.client.json. the ConfigurationBuilder will pick up the settings in all the sources and compile them into the configuration object.
also check out the full docs:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration

How to save generated file temporarily in servlet based web application

I am trying to generate a XML file and save it in /WEB-INF/pages/.
Below is my code which uses a relative path:
File folder = new File("src/main/webapp/WEB-INF/pages/");
StreamResult result = new StreamResult(new File(folder, fileName));
It's working fine when running as an application on my local machine (C:\Users\userName\Desktop\Source\MyProject\src\main\webapp\WEB-INF\pages\myFile.xml).
But when deploying and running on server machine, it throws the below exception:
javax.xml.transform.TransformerException:
java.io.FileNotFoundException
C:\project\eclipse-jee-luna-R-win32-x86_64\eclipse\src\main\webapp\WEB INF\pages\myFile.xml
I tried getServletContext().getRealPath() as well, but it's returning null on my server. Can someone help?
Never use relative local disk file system paths in a Java EE web application such as new File("filename.xml"). For an in depth explanation, see also getResourceAsStream() vs FileInputStream.
Never use getRealPath() with the purpose to obtain a location to write files. For an in depth explanation, see also What does servletcontext.getRealPath("/") mean and when should I use it.
Never write files to deploy folder anyway. For an in depth explanation, see also Recommended way to save uploaded files in a servlet application.
Always write them to an external folder on a predefined absolute path.
Either hardcoded:
File folder = new File("/absolute/path/to/web/files");
File result = new File(folder, "filename.xml");
// ...
Or configured in one of many ways:
File folder = new File(System.getProperty("xml.location"));
File result = new File(folder, "filename.xml");
// ...
Or making use of container-managed temp folder:
File folder = (File) getServletContext().getAttribute(ServletContext.TEMPDIR);
File result = new File(folder, "filename.xml");
// ...
Or making use of OS-managed temp folder:
File result = File.createTempFile("filename-", ".xml");
// ...
The alternative is to use a (embedded) database or a CDN host (e.g. S3).
See also:
Recommended way to save uploaded files in a servlet application
Where to place and how to read configuration resource files in servlet based application?
Simple ways to keep data on redeployment of Java EE 7 web application
Store PDF for a limited time on app server and make it available for download
What does servletcontext.getRealPath("/") mean and when should I use it
getResourceAsStream() vs FileInputStream
just use
File relpath = new File(".\pages\");
as application cursor in default stay into web-inf folder.

How can I get the directory of my deployed web application?

How to get current working directory for struts2 web application?
String currentDir = System.getProperty("user.dir");
String currentDir = new File(".").getAbsolutePath();
return /usr/share/tomcat.
I need path to web application, so I could read a file from war.
You need to get the ServletContext's real path: ServletContext.getRealPath(String)
That said, why do you want to read a file that's in a war file, and why do you want to do it this way?
Prefer putting the file on the classpath and using getResourceAsStream:
API
Docs

How to add a dynamic link for a service reference endpoint in Visual Studio 2010

I'm trying to define a service endpoint in my web.config file so that I can point our staging build to a staging web service and a different end point for production. There's a question here that deals with web references in Visual Studio 2005/2008. I'm adding a service reference and can't seem to find anything in properties that would allow me to define the Url Behaviour as dynamic.
I'd like to define the url in the appSettings. Does anyone know how this works in Visual Studio 2010 for Service References?
You can change the end point using what is known as a config transformation.
In short, a config transformation allows you to tweak various config settings depending on your deployment. This is a technique commonly used for changing connection strings as well.
Here's more reading: How to: Transform Web.config When Deploying a Web Application Project
You can set the URL at run time just after you create the web service but before you use it:
string localUrl = "localhost";
string stagingUrl = "http://staging.example.com"
string url = Request.IsLocal ? localUrl : stagingUrl;
var _webService = new YourWebService { Url = url };

Resources