How to download file using IHttpClientFactory in .Net Core 2.2? - asp.net-web-api

In my web application (.net Core 2.2) I want to add the functionality of downloading a PDF file.
When the user clicks "download" - POST is sent to WebAPI and the server ask different server for PDF file by hardcoded URL (invisible to the user) and the file is passed to the user.
I was trying to use IHttpClientFactory
Startup:
services.AddHttpClient("demo", c =>
{
c.BaseAddress = new Uri("http://www.africau.edu/");
c.DefaultRequestHeaders.Add("Accept", "application/pdf");
});
Method for returns PDF file:
public async Task<FileStreamResult> GetPdfFile()
{
var client = _httpClientFactory.CreateClient("demo");
var uri = new UriBuilder(client.BaseAddress + "images/default/sample.pdf").Uri;
var stream = await client.GetStreamAsync(uri);
return new FileStreamResult(stream, new MediaTypeHeaderValue("application/pdf"));
}
Unfortunately it returns file download.json with: {"fileStream":{"needsDrain":true,"canRead":true,"canWrite":false,"canSeek":false
#EDIT
it seems that it's because I had Task<IActionResult> in my Controller instead of Task<FileStreamResult> but what if i prefer to returns ActionResult?
public async Task<IActionResult> GetPdf()
{
var result = await _service.GetPdfFile();
return Ok(result);
}

In your GetPdf method, you're passing a FileStreamResult to the Ok() method. This will serialize the FileStreamResult using the default serializer and send it to the client, hence why you're seeing the JSON.
Usually your service layer should not return an MVC WhateverResult object but rather an object intended to represent the actual data being retrieved - in your case this would be a PDF file. The easiest way to solve your particular case would probably be to do the following:
change GetPdfFile to return Task<FileStream> instead of Task<FileStreamResult>
remember to change its return statement to return stream;
change the return statement of GetPdf to return File(result, "application/pdf");
This will allow your service layer to return the PDF file as a stream, and then have your GetPdf endpoint return that stream as a file with the application/pdf file type.

Related

What is the alternative for HttpContext.Response.OutputStream to use in WebAPI's HttpResponseMessage

I'm writing a WebAPI for handling PDF documents. It was written in a ashx page earlier implementing IHttpHandler and getting the context using HttpContext. I'm now writing it using WebAPI. In WebAPI we have HttpResponseMessage. For HttpContext.Response.BinaryWrite we have new ByteArrayContent in HttpResponseMessage. But what is the alternative for HttpContext.Response.OutputStream in WebAPI? I need to have the alternative of OutputStram in WebAPI because im passing this OutputStream as a parameter to another dll.
Code in ashx:
SomeReport.PdfReport rpt = new SomeReport.PdfReport(docID);
rpt.CreateReport(context.Response.OutputStream);
Actually you can use any stream for example MemoryStream but result should be wrapped into StreamContent.
public HttpResponseMessage Get()
{
var response = Request.CreateResponse();
var outputStream = new MemoryStream();
//write data to output stream
//or passing it to somewhere
outputStream.WriteByte(83);
outputStream.Position = 0;
response.Content = new StreamContent(outputStream);
return response;
}
If you need direct writing to output stream, please consider using PushStreamContent. Example

Globally formatting .net Web Api response

I have a Web Api service that retrieves data from another service, which returns Json. I don't want to do anything to the response, I just want to return it directly to the client.
Since the response is a string, if I simply return the response, it contains escape characters and messy formatting. If I convert the response in to an object, the WebApi will use Json.Net to automatically format the response correctly.
public IHttpActionResult GetServices()
{
var data = _dataService.Get(); //retrieves data from a service
var result = JsonConvert.DeserializeObject(data); //convert to object
return Ok(result);
}
What I would like is to either A: Be able to return the exact string response from the service, without any of the escape characters and with the proper formatting, or B: Set a global settings that will automatically Deserialize the response so that the Web Api can handle it the way I am doing it already.
On Startup I am setting some values that describe how formatting should be handled, but apparently these aren't correct for what im trying to do.
HttpConfiguration configuration = new HttpConfiguration();
var settings = configuration.Formatters.JsonFormatter.SerializerSettings;
settings.Formatting = Formatting.Indented;
settings.ContractResolver = new DefaultContractResolver();
Do I need to create a custom ContractResolver or something? Is there one that already handles this for me?
Thanks
If you want to just pass through the json (Option A), you can do this
public IHttpActionResult GetServices() {
var json = _dataService.Get(); //retrieves data from a service
HttpContent content = new System.Net.Http.StringContent(json, Encoding.UTF8, "application/json");
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = content;
return ResponseMessage(response);
}

Compression response filter fails on breeze.js Metadata call

I have an http module where I'm adding a response filter below for compression. This works for all API calls except for 1, the call to MetaData. If I remove the [BreezeController] decoration it works fine. I think it has to do with action filter attribute that converts the string return type into an HttpResponse return type with string content.
The error I'm getting is " Exception message: The stream state of the underlying compression routine is inconsistent."
I've done some testing where a method thats defined to return an HttpResponse works fine. So I think its the scenario where the method is defined to return string, and then the action filter changes it to HttpResponse at runtime.
Any ideas how I can get this to work?
Here's the response filter being added in BeginRequest:
HttpApplication app = (HttpApplication)sender;
// Check the header to see if it can accept compressed output
string encodings = app.Request.Headers.Get("Accept-Encoding");
if (encodings == null)
return;
Stream s = app.Response.Filter;
encodings = encodings.ToLower();
if (encodings.Contains("gzip"))
{
app.Response.Filter = new GZipStream(s, CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", "gzip");
}
Don't know the specifics of what you're doing but I know that the [BreezeController] attribute strips out filters and adds back just the ones that breeze wants.
One approach might be to define a separate controller (ModelMetadataController) that only serves the metadata. This controller doesn't have the [BreezeController] attribute; it's a plain old Web API controller.
Then you create a "Breeze controller" (ModelController) with all of the usual methods except the Metadata method.
You call the metadata controller from the client during app launch via MetadataStore.fetchMetadata just to get metadata.
Once you have populated a metadataStore in this fashion, you use it in your EntityManager which sends query and save requests to the "real" Web API data controller.
The client code might look something like this:
var ds = new breeze.DataService({
serviceName: 'breeze/Model' // the breeze query & save controller
});
var ms = new MetadataStore({
namingConvention: breeze.NamingConvention.camelCase, // assuming that's what you want
});
ms.addDataService(ds); // associate the metadata-to-come with the "real" dataService
var manager = new breeze.EntityManager({
dataService: ds,
metadataStore: ms
});
// the fun bit: fetch the metadata from a different controller
var promise = ms.fetchMetadata('breeze/ModelMetadata') // the metadata-only controller!
return promise; // wait on it appropriately

how to get a httppostedfile from a ASP.NET Web API (POST or PUT) call?

Actually my question is short.
How can I get a HttpPostedFile from a ASP.NET Web API POST or PUT?
I did see that I can get various information from the Request like Request.Header, Request.Content, Request.Properties. Where in there can I find the file I passed and how can I create a HttpPostedFile from it?
Thanks in advance!
Check out the great article from Henrik Nielsen to post multi-part content (i.e posting a form with file)
UPDATE: Add simple code for a controller to receive a file without multipart content
If you only need your controller to receive a file (i.e. no multipart content), you could do something like the above. The request only contains the file binary and the filename is passed within the URL.
public Task<HttpResponseMessage> Post([FromUri]string filename)
{
Guid uploadedFile = Guid.NewGuid();
Task<HttpResponseMessage> task = Request.Content.ReadAsStreamAsync().ContinueWith<HttpResponseMessage>(t =>
{
if (t.IsFaulted || t.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
try
{
using (Stream stream = t.Result)
{
//TODO: Write the stream to file system / db as you need
}
}
catch (Exception e)
{
Object o = e;
return Request.CreateResponse(HttpStatusCode.InternalServerError, e.GetBaseException().Message);
}
return Request.CreateResponse(HttpStatusCode.Created, uploadedFile.ToString());
});
return task;
}
Your short question does not have a short answer I am afraid.
ASP.NET Web API exposes you to the wonders of HTTP while ASP.NET MVC abstracted some of it - in this case for HttpPostedFile.
So a bit of background:
HTTP posts where a file is involved usually has multipart form data content. This means that you are mixing different kind of content-type: your normal form data will be sent using formurlencoded while the files will be sent application/octent-stream.
So in Web API, all you have to do is to say
var contents = message.Content.ReadAsMultipartAsync(); // message is HttpRequestMessage
One of the contents will contain your file.

MVC3 - How to output a file to download without saving it on the server first?

In PHP, it is possible to return a file to the browser by echoing it out with the correct header. You do not need to save a copy of it prior on the server.
So assuming I have a bunch of data I wish to return as a excel file - after creating the data structure using OpenXML, how can I serve the file to the user without saving it on the server first?
It's pretty simple actually. In your controller you can have an action like this:
public ActionResult GetMyFile()
{
//dynamically generate a file
System.IO.MemoryStream ms;
ms = GenerateFile(); // Some function to return a memorystream object
// return the file
return File(ms.ToArray(), "filename.pdf");
}
The user will be presented with a dialog box asking if they want to open the file or save it.
Write your data to a stream and return it from your controller action method in a FileStreamResult by setting the FileStream, ContentType and FileDownloadName properties.
[HttpGet]
public FileStreamResult MyFile()
{
var fileStreamResult = new FileStreamResult (GetMyContentAsStream(), "my/content-type-here");
fileStreamResult.FileDownloadName = "my-file-download-name.here";
return fileStreamResult ;
}
Update:
A short cut for doing this, is to use the Controller.File() method.
[HttpGet]
public FileStreamResult MyFile()
{
return File(GetMyContentAsStream(), "my/content-type-here");
}
You can save the contents of your dynamically generated file in a MemoryStream object. When you return a file you can use MemoryStream's GetBuffer() method to pass an array of bytes as the first parameter. Then set ContentType and FileDownloadName parameters.
Regards

Resources