I have created and HTTP Triggered Azure Function (v2) using .NET Core with the hopes that I can execute the function while passing in some info in the request body and then have the function return/download a file in the browser. Unfortunately I am struggling to get this working.
Below is a snippet of Code
public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequest req, ILogger log)
{
string csv;
//Do some stuff to create a csv
byte[] filebytes = Encoding.UTF8.GetBytes(csv);
req.HttpContext.Response.Headers.Add("content-disposition", "attachment;filename=Export.csv");
req.HttpContext.Response.ContentType = "application/octet-stream";
return (ActionResult)new OkObjectResult(filebytes);
}
When I do a post using Postman the request is accepted but the response is 406 "unacceptable" and the output in the log states
"Microsoft.AspNetCore.Mvc.Infrastructure.DefaultOutputFormatterSelector[1]
No output formatter was found for content type 'application/octet-stream' to write the response."
I've tried multiple content types including text/plain and text/csv, all give the same response about output formatting.
If I remove or comment out the ContentType the request processes and returns a 200 but the filebytes are returned in the response body instead of being downloaded in the browser.
You'll need a FileContentResult for this:
public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequest req, ILogger log)
{
string csv;
//Do some stuff to create a csv
byte[] filebytes = Encoding.UTF8.GetBytes(csv);
return new FileContentResult(filebytes, "application/octet-stream") {
FileDownloadName = "Export.csv"
};
}
While the comments correctly point out that the ideal solution is to kick off processing in the HTTP function asynchronously, return a 202 Accepted response, save the result to blob storage, have the client wait for processing to complete before starting the blob download and then delete the blob once it's been downloaded, current Azure Functions pricing is only $0.000016/GB-s so you may find that to be unnecessarily complicated unless you have quite high traffic.
Related
I have a basic HTTP trigger azure function in Visual Studio (C#):
[FunctionName("HttpTriggerCSharp")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
return name != null
? (ActionResult)new OkObjectResult($"Hello, {name}")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}
But I'm having 2 issues with this:
(1) I only want it to receive 'Post' requests. But when I take out the "get" from the arguments the function no longer receives my requests in Azure. I get a 404 Not Found. But locally it stills receives my request and works fine.
(2) Additionally the body of the request is not received in Azure. Its empty. Once again when running locally no such problems occur.
I found several cases of people having the issue (2), but haven't found a way to solve. As to issue (1), haven't found any similar cases posted online yet.
Does anyone have any idea on why this is happening?
I have an Azure Function that sits behind a proxy. If an update occurs to the objects that get returned we want to deprecate the Function after a period of time. I'm trying to create a response with the expected content from an HTTP Header by using what was provided in this solution.
Warning: 299 - "Deprecated API"
I try to append the Azure Function like so:
[FunctionName("MyAPI")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function,
"post", Route = null)]
HttpRequestMessage req,
TraceWriter log)
{
object response = await someService.Get();
if (settingsService.IsDeprecated)
{
var httpresponse = req.CreateResponse(HttpStatusCode.OK, response, "application/json");
httpresponse.Content.Headers.Add("Warning", "299 - Deprecated API");
return httpresponse;
}
return req.CreateResponse(HttpStatusCode.OK, response, "application/json");
}
I get the Exception
Exception while executing function: MyAPI -> Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.
How do I append the "Deprecated" status warning in my API Http Response?
Change your line to
httpresponse.Headers.Add("Warning", "299 - \"Deprecated API\"");
The quotes seem to be important there to adhere to the format requirement.
I have the problem, that I can't retrieve the body of a POST statement of a JSON Object. Here is the function, which is called when executing the http-Request:
public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "HttpTriggerCSharp/name/{name}")]HttpRequestMessage req, string name, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
string output = JsonConvert.SerializeObject(req.Content.ToString());
// Fetching the name from the path parameter in the request URL
return req.CreateResponse(HttpStatusCode.OK, output);
}
Im executing the POST with Postman and the following URL: http://localhost:7071/api/HttpTriggerCSharp/name/test
In the Header I wrote "Content-Type: application/json" and the Body looks like this:
{
"Benutzer":"Nenad",
"Passwort":"test"
}
My result is this: "\"System.Net.Http.StreamContent\""
Thank you for your help!
Thanks for the answers guys,
i found now an other solution with the help of a friend, this is the following:
public static async System.Threading.Tasks.Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "HttpTriggerCSharp/name/{name}")]HttpRequestMessage req, string name, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
dynamic dataArray = await req.Content.ReadAsAsync<object>();
string output = dataArray.ToString();
var data = Newtonsoft.Json.JsonConvert.DeserializeObject<Benutzer>(output);
// Fetching the name from the path parameter in the request URL
return req.CreateResponse(HttpStatusCode.OK, data);
}
Content is an instance of HttpContent class (as you can see in your output). So you have to use an appropriate method to get the string.
As you can see, I am trying to send an image, and a name through a POST command to a local function.
How Can I read both of these parameters in C#?
This is what I have tried but It can only read File Image.
[FunctionName("Test")]
public static async Task<HttpResponseMessage>
Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route =
null)]HttpRequestMessage req, TraceWriter log)
{
//Check if the request contains multipart/form-data.
if (!req.Content.IsMimeMultipartContent())
{
return req.CreateResponse(HttpStatusCode.UnsupportedMediaType);
}
foreach (var stream in contents.Contents)
{
try
{
var fileBytes = await stream.ReadAsByteArrayAsync();
var fileinfo = new FileInfo(stream.Headers.ContentDisposition.FileName.Trim('"'));
//Can Read File image like this.
}
catch(Exception e)
{
return req.CreateErrorResponse(HttpStatusCode.Conflict, e);
}
Is there a workaround to do this like using memory stream?
According to your requirement, I assumed that you could use HttpContentMultipartExtensions.ReadAsMultipartAsync and you would get the MultipartMemoryStreamProvider, then you could leverage the following code for reading your uploaded files:
var multipartMemoryStreamProvider= await req.Content.ReadAsMultipartAsync();
foreach (HttpContent content in multipartMemoryStreamProvider.Contents)
{
// for reading the uploaded file
var filename= content.Headers.ContentDisposition.FileName.Trim('"');
var stream=await content.ReadAsStreamAsync();
//for formdata, you could check whether `content.Headers.ContentDisposition.FileName` is empty
log.Info($"name={content.Headers.ContentDisposition.Name},value={await content.ReadAsStringAsync()}");
}
Moreover, you could follow this issue about creating your custom MultipartFormDataMemoryStreamProvider based on MultipartMemoryStreamProvider. And this issue about building custom InMemoryMultipartFormDataStreamProvider based on MultipartStreamProvider.
You can use MultipartFormDataStreamProvider
Use FileData property to get the posted files
Use FormData property to get the values of any form data posted based upon the key.
Check this File Upload and Multipart MIME.
If have a simple HTTP triggered function in C# which just doesn't return the result:
public static HttpResponseMessage Run(HttpRequestMessage req, TraceWriter log)
{
string jobId = req.Headers.GetValues("scheduler-jobid").FirstOrDefault();
string executionTime = req.Headers.GetValues("scheduler-expected-execution-time").FirstOrDefault();
return req.CreateResponse(HttpStatusCode.OK,new {
JobId = jobId,
ExecutionTime = executionTime}
);
}
I checked with POSTMAN that HTTP headers are set correctly but just get a 200 OK without a response body.
In hindsight the solution is obvious:
I had to define a HTTP Response output and without changing the code above the Azure Functions runtime automatically wires in the req.CreateResponse.