I have some variables which are going to be used by the business logic part of a function. Therefore, instead of adding them inside the appsetting.json file, I have added a separated file as variable.json
Testing on my machine works but after deploy, it seems function can not find it. and I got an error:
The properties for this file is like the below image. (The build action was None before, but nothing has been changed even by content)
and the below image shows how it looks like in root
And because of that reason, any call the response will be "Function host is not running."
The code for reading this file (path = "Variables.json")
private static List<Variable> GetVariables(string path)
{
string json = File.ReadAllText(path);
var variables = JsonConvert.DeserializeObject<List<Variable>>(json);
return variables;
}
Does anyone have any clue why this is happening?
Problem was because when we start Azure Function locally the file varibale.json is available by Directory.GetCurrentDirectory(), but published on azure portal it's Directory.GetCurrentDirectory() + #"\site\wwwroot"
To get the correct folder path you can use following code:
public static HttpResponseMessage Run(HttpRequestMessage req, ExecutionContext context)
{
var path = System.IO.Path.Combine(context.FunctionDirectory, "varibale.json");
// ...
}
For startup.cs, you can use the following code:
var executioncontextoptions = builder.Services.BuildServiceProvider()
.GetService<IOptions<ExecutionContextOptions>>().Value;
var currentDirectory = executioncontextoptions.AppDirectory;
Related
I am using Azure Function App
I am using CSVHelper package to create file, But CSVHelper needs local file path first to Create/Write file.
using (var writer = new StreamWriter(filePath))
using (var csvData = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
// Write input in csv
csvData.WriteRecords(input);
}
What path can I use to create file in Azure Function App?
Since it looks like you're using a StreamWriter, you could also write to a MemoryStream instead of creating an actual file. This feels like a better route to take with Azure Functions.
If you're really set on creating an actual file, you can do so by using System.IO.Path.GetTempPath(), which will always return a valid path for any given system. Create your temporary file there, then continue with the process.
Please take into account that your Function might run multiple times on the same environment, so be sure to use a unique filename.
For future reference:
private static void ExportContentToCsv(ILogger log, IEnumerable<T> content)
{
var path = Path.Combine(Path.GetTempPath(), "content.csv");
log.LogInformation($"Writing csv file at {path}");
if (File.Exists(path))
{
log.LogInformation("Deleting existent resources...");
File.Delete(path);
}
using (var writer = new StreamWriter(path))
{
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(content);
}
}
}
I have a Problem with my Azure Function. I use a Linux based App service Plan (B1). After deploying my Code and running the Azure function I get this Error.
2020-03-11T07:02:55.985 [Error] Executed 'PdfRender_dotnet_framework' (Failed, Id=52201ad7-8012-4f93-bc17-0accae6a1540)
No such file or directory
The strange thing about this is that I can change to this Directory in the console and in Kudu with bash/ssh. So it seems that everything was deployed fine, but why is it still showing me this error message.
I checked many times if everything was deployed right and i didn't find any issues.
Error Message
Directory in Console
Directory in Bash (Kudu)
This is my function.json
I also noticed that the azure function reach the code.
My Function Code.
public static class dotnet_core_pdf
{
[FunctionName("dotnet_core_pdf")]
public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequest req, TraceWriter log, ExecutionContext executionContext)
{
string name = req.Query["url"];
log.Info(name);
//Initialize HTML to PDF converter
HtmlToPdfConverter htmlConverter = new HtmlToPdfConverter();
WebKitConverterSettings settings = new WebKitConverterSettings();
//Set WebKit path
settings.WebKitPath = Path.Combine(executionContext.FunctionAppDirectory, "QtBinariesWindows");
//Assign WebKit settings to HTML converter
htmlConverter.ConverterSettings = settings;
//Convert URL to PDF
PdfDocument document = htmlConverter.Convert(name);
MemoryStream ms = new MemoryStream();
//Save the PDF document
document.Save(ms);
ms.Position = 0;
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new ByteArrayContent(ms.ToArray());
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "HTMLToPDFAzure.pdf"
};
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
return response;
}
}
}
NOTICE: I changed the folders Name, I am using .Net Core not .Net Framework
Look at below c# code as I running this code on the Local server it's working but after deploying in Azure it's not working.
Someone, please help me to resolve this issue
public async Task<IHttpActionResult> ptoexport()
{
string file = #"filepath";
FileInfo fileInfo = new FileInfo(file);
ExcelPackage p = new ExcelPackage(fileInfo);
ExcelWorksheet myWorksheet = p.Workbook.Worksheets["Sheet1"];
myWorksheet.Cells[5, 5].Value = 34;
p.Save();
return Ok("Success");
}
I running this code on the Local server it's working but after deploying in Azure it's not working.
As I have tested, I think the file path you provided in code is your local xlsx file path. So when you run in local, the method could find the file, but when it publish to azure it failed.
So, I suggest that you could upload file to KUDU and change the file path.
I upload the xlsx file here and use the following, it works fine.
public async Task<IHttpActionResult> ptoexport()
{
string file = #"D:/home/site/wwwroot/new.xlsx";
FileInfo fileInfo = new FileInfo(file);
ExcelPackage p = new ExcelPackage(fileInfo);
ExcelWorksheet myWorksheet = p.Workbook.Worksheets["Sheet1"];
myWorksheet.Cells[5, 5].Value = 34;
p.Save();
return Ok("Success");
}
Here is a case about why Azure deploy can't find file, you could also refer to it.
I am trying out Azure Function Apps.
The first one following the example in a tutorial with Open Weather map, stopped working after I used log.WriteLine(), which correctly threw a compiler error. I changed to log.Info() and it kept complaining about TraceWriter not containing a definition for WriteLine.
After a lengthy troubleshooting session, I created a new function, copying all the content of the broken one, and it worked immediately.
Created a new function, as before, and began making changes to the Run() method, and running this function yields:
"The resource you are looking for has been removed, had its name
changed, or is temporarily unavailable."
Bearing in mind, the function URL is based on the default key Azure generates when the function is created: https://.azurewebsites.net/api/WeatherWhereYouAre?code=my1really2RAndom3defauLT4Key5from6Azure==
Created yet another function, with no changes from the default "Hello Azure" sample, and it yields a 500 error with:
"Exception while executing function: Functions.HttpTriggerCSharp2 ->
One or more errors occurred. -> Exception binding parameter 'req' ->
Input string was not in a correct format."
This is the content of the project.json file:
{
"frameworks": {
"net46": {
"dependencies": {
"Microsoft.IdentityModel.Clients.ActiveDirectory": "3.16.0",
"Microsoft.Azure.KeyVault": "2.3.2",
"Microsoft.AspNet.WebApi.Client": "5.2.3"
}
}
}
}
And the run.csx:
using System.Net;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
// parse query parameter
string name = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
.Value;
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
// Set name to query string or body data
name = name ?? data?.name;
return name == null
? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
: req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
}
EDIT
In the above image, note that this is httpTriggerFSharp1, but the exception is HttpTriggerCSharp2 (which is the only one that works!)
Is there a way I can properly troubleshoot these?
For the default HttpTrigger template for C#, you could call it as follows:
Get https://brucefunapp.azurewebsites.net/api/HttpTriggerCSharp3?name=bruce&code=ItDhLMxwDYmTvMTYzVbbALtL5GEcmaL5DlzSaD4FRIuFdh17ZkY71g==
Or
Post https://brucefunapp.azurewebsites.net/api/HttpTriggerCSharp3?code=ItDhLMxwDYmTvMTYzVbbALtL5GEcmaL5DlzSaD4FRIuFdh17ZkY71g==
Content-type: application/json
{"name": "bruce"}
For more details about Azure Functions C# script, you could refer to here.
Is there a way I can properly troubleshoot these?
Per my understanding, you could leverage Precompiled functions and use Visual Studio 2017 Tools for Azure Functions for creating, local debugging, and publishing to Azure.
Github link for reproduction.
I have an ASP.NET Core (RC1) application that works fine locally. The issue I'm having is that my connection string is not being picked up by my Azure app. I've asked similar questions to this, but I've narrowed down the issue on my end in this app. Note, it requires an app on Azure to reproduce it.
Here's the issue I'm seeing.
First, my configuration is setup as such:
public Startup()
{
var builder = new ConfigurationBuilder()
.AddJsonFile("config.json")
.AddEnvironmentVariables();
mConfiguration = builder.Build();
}
And EF7 is setup here:
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<FooDbContext>(options =>
{
// I'm assuming it's failing here.
// I'm not sure how to debug it running on Azure.
// All the developer exception page shows is:
// 500 Internal Server Error "An error occurred while starting the application."
options.UseSqlServer(mConfiguration["Data:ConnectionStringTest:ConnectionString"]);
});
services.AddScoped<IFooDataService, FooSqlDataService>();
}
My config.json has:
{
"Data": {
"ConnectionStringTest": {
"ConnectionString": "Data Source=(localdb)\\mssqllocaldb;Initial Catalog=ConnectionStringTest"
}
}
}
And this should be overridden by the connection string I've setup in Azure:
When going to the Kudu SCM and looking at the environment variables on the Azure web app instance, I see the following:
SQLAZURECONNSTR_Data:ConnectionStringTest:ConnectionString = my_connection_string_here
I am assuming this is the class that is being used under the hood when my environment variable is used at runtime: EnvironmentVariablesConfigurationProvider
Ok here's what I found, and this feels awkward.
It seems that you need to use Data:{my_connection_string_key}:ConnectionString everywhere EXCEPT in Azure. This environment variable converter will construct the proper connection string using this format automatically if the connection string is prefixed with SQLAZURECONNSTR_.
This means when you setup your connection string in Azure, you need to omit EVERYTHING except the key to your connection string. Do not insert Data: or :ConnectionString... simply use {connection_string_key} (refer to the above format) instead. If you include the entire format in your Azure key/value pair, the EnvironmentVariablesConfigurationProvider will add another Data: and :ConnectionString around it, resulting in something like Data:Data:{my_connection_string_key}:ConnectionString:ConnectionString.
In ConfigureServices(...), use the format that ASP expects:
... options.UseSqlServer(mConfiguration["Data:ConnectionStringTest:ConnectionString"]);
You can therefore locally use this for your local JSON, for testing in development/fallback:
{
"Data": {
"ConnectionStringTest": {
"ConnectionString": "Data Source=(localdb)\\mssqllocaldb;Initial Catalog=ConnectionStringTest"
}
}
}
Just make sure your Azure connection string has the middle part of that format (ConnectionStringTest using this example).
This will make your environment variable in Azure look like this in raw format:
SQLAZURECONNSTR_ConnectionStringTest = {insert connection string here}
And the EnvironmentVariablesConfigurationProvider will strip off the Azure prefix string, and wrap your key in the hardcoded format: Data:{0}:ConnectionString
Experiment Results
To augment your excellent answer, I did a local experiment to confirm that the SQLAZURECONNSTR_connection_string_key environmental variable becomes this configuration:
mConfiguration["Data:connection_string_key:ConnectionString"]
Local Experiment
A local environmental variable emulates an Azure SQL Database connection string named connection_string_key.
PS> $env:SQLAZURECONNSTR_connection_string_key = "an azure conn string"
The following code dumps all the environmental variables and configuration sections to the page.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Run(async (context) =>
{
await context.Response.WriteAsync("# Environmental Variables \r\n");
await DumpAllEnvVariables(context, Environment.GetEnvironmentVariables());
await context.Response.WriteAsync("# Configuration Sections \r\n");
await DumpAllConfigItems(context, mConfiguration.GetChildren());
});
}
private async Task DumpAllEnvVariables(HttpContext context, IDictionary envVariables)
{
foreach (var envVar in envVariables.Cast<DictionaryEntry>())
{
await context.Response.WriteAsync($"{envVar.Key}"); // : {envVar.Value}
await context.Response.WriteAsync($"\r\n");
}
}
private async Task DumpAllConfigItems(HttpContext context,
IEnumerable<IConfigurationSection> sections, string prefix = "")
{
foreach (var section in sections)
{
await context.Response.WriteAsync($"{prefix}{section.Key}"); // : {envVar.Value}
await context.Response.WriteAsync($"\r\n");
if(section.GetChildren().Any())
{
await DumpAllConfigItems(context, section.GetChildren(), prefix + " ");
}
}
}