I have an MVC4 WebAPI project and have a controller FileController that has this Get method in it:
public HttpResponseMessage Get(string id)
{
if (String.IsNullOrEmpty(id))
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "File Name Not Specified");
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
var stream = fileService.GetFileStream(id);
if (stream == null)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "File Not Found");
}
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = id;
return response;
}
In the browser going to localhost:9586/File/myfile.mp3it dispatches the file correctly as an attachment and you can save it. If it is an audio file, you can stream it from the HTML5 audio tag.
Now, I need to call this WebAPI method from an MVC4 web app, basically wrapping it. Here it comes:
public HttpResponseMessage DispatchFile(string id)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:8493/");
HttpResponseMessage response = client.GetAsync("api/File/"+id).Result;
return response;
}
Going to localhost:8493/File/DispatchFile/my.mp3 returns:
StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers: { Pragma: no-cache Connection: Close Cache-Control: no-cache Date: Thu, 05 Sep 2013 15:33:23 GMT Server: ASP.NET Server: Development Server: Server/10.0.0.0 X-AspNet-Version: 4.0.30319 Content-Length: 13889 Content-Disposition: attachment; filename=horse.ogg Content-Type: application/octet-stream Expires: -1 }
So it looks like the Content is indeed StreamContent, but it doesn't return it as a saveable file. Now the question is, how to mirror the behavior when calling the API directly? Any suggestions much appreciated.
I believe the use of HttpClient.Result is not the correct approach here. I think you may need to use the 'Content' property and then call ReadAsStreamAsync to get a handle on the file stream that is returned by your WebAPI method. At that point, you should be able to just write this stream to the response stream, allowing the file to be downloaded / audio to be streamed via HTML5.
See here for an example of using HttpClient to get a file (the link shows how to handle large files, but I believe the approach used there is what you will need to do):
http://developer.greenbutton.com/downloading-large-files-with-the-net-httpclient/
Related
I use Asp.Net API (.Net 5) in my backend.
I use blazor wasm in my frontend.
A method of my controller is:
public FileContentResult GetExport()
{
var IndividualBl = new IndividualBl();
return ResultBlob(IndividualBl.GetExport(), "individual-export.xlsx");
}
protected FileContentResult ResultBlob(byte[] byteArray, string fileName)
{
return File(byteArray, "application/octet-stream", fileName);
}
When I use Swagger, I download the file with the name included in the header.
My Response Headers:
access-control-allow-origin: *
content-disposition: attachment; filename=individual-export.xlsx; filename*=UTF-8''individual-export.xlsx
content-length: 4218
content-type: application/octet-stream
date: Mon, 23 Nov 2020 14:39:34 GMT
server: Microsoft-IIS/10.0
status: 200
x-powered-by: ASP.NET
Now I try to get this file by service :
public async Task<StreamFileModel> GetExport()
{
var url = $"{_httpClient.BaseAddress}{IndividualUrls.IndividualController}/{IndividualUrls.GetExport}";
var result = await _httpClient.GetAsync(url);
var header = result.Content.Headers.ContentDisposition;
var content = await result.Content.ReadAsByteArrayAsync();
return new StreamFileModel
{
File = content,
Name = header.FileName
};
}
My content is OK but the header is null (= ContentDisposition is null)
It is not the good method to get the content-disposition value?
Thanks
In API project, you need to use Cors policy to allow content-disposition in header:
app.UseCors(x => x.WithExposedHeaders("content-disposition"));
I try to call an external api, when I am using the Postman, it is working and returning value as follows:
Post to URL: https://test.com/api/v1/users/check
Data Raw Jason to post:
{
"access_token":"4444-EA444B6-2844C7-A09C-44B05CA78E42A3",
"email":"test#test.com",
"create_user": true,
"first_name": "test4",
"last_name": "test",
"phone": 3104054512
}
So this is working and returning me response model.
but when try this code to call the api:
Controller:
[Route("CreateUser")]
public Task<UserReturn> CreateUser([FromBody] User user)
{
return homebirdRepository.CreateUser(user);
}
public async Task<UserReturn> CreateUser(User userCheck)
{
using (GetWSObject<UserReturn> addObjectInt = new GetWSObject<UserReturn>())
{
return await addObjectInt.PostWSObjectModel("api/v1/users/check", userCheck, "API_URI");
}
}
public async Task<T> PostWSObjectModel(string uriActionString, Object model, string apiKey)
{
T returnValue = default(T);
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(WebConfigurationManager.AppSettings[apiKey]);
var content = new StringContent(JsonConvert.SerializeObject(model));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await client.PostAsync(uriActionString, content);
response.EnsureSuccessStatusCode();
var test = response.Content.ReadAsStringAsync().Result;
returnValue = JsonConvert.DeserializeObject<T>(((HttpResponseMessage)response).Content.ReadAsStringAsync().Result);
}
return returnValue;
}
catch (Exception e)
{
throw (e);
}
}
This code is returning me this error:
StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content:
System.Net.Http.StreamContent,
Headers:
{
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-
With
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Cache-Control: no-cache, private
Date: Sat, 28 Dec 2019 00:00:04 GMT
Set-Cookie:laravel_session=eyJpdiI6IlI2MUdzOFJmS0RcL1k1VmJCeTc4bk1nPT0iLCJ2YWx1ZSI6IlZXNW11MGw2bXk0ajFEaTM2VnhmbUZjQnFzdnRDRHV5ejJMaDRqTVJYQm1yclNyUUkweDNRMUhpZDZwblpES1MiLCJtYWMiOiI0NmFiODA4YzEyNTkxZDllNDViNGUwOGIzYjY2ZWYxZGQwNzI1NmZmYzYxYTBkZGU0M2NmMDBlYzIzN2E3OTFjIn0%3D; expires=Sat, 28-Dec-2019 02:00:04 GMT; Max-Age=7200; path=/; httponly
Server: Apache
Content-Length: 21
Content-Type: text/html; charset=UTF-8
}}
You most likely have a header, cookie, etc. that is not set in C# which is set in Postman. There are several ways you can determine which properties are being set in Postman. I've answered a similar question here. Fiddler or some other separate tool shouldn't be necessary.
you must add the Authorization header, this one is added and calculated by postman, you can copy/post. the following if you are using a Basic authentication.
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "value to copy from postman");
I have a problem when I´m trying sending post and put requests to my webapi (the server side is coded in php and works with mysql).
If I try to send a get request there is no problem at all, the API responds properly, the problem comes when I try to send a body in request (that´s why I think post and put fails).
I am sure that my api works well, as long as I have done tests with postman and another clients and they all follow a correct way with these requests.
The API responds 400 Bad Request and I don´t know what to do. I´ve done the same api coded in C# and my code in client-side works, there´s any incompatibility between Universal Windows Platform and PHP API´s?
My PUT method:
public async Task<int> updateFestivalDAL(Festival festival)
{
int resultado = 0;
Uri miConexion = (new clsMyConnection().getConnection());
HttpClient miCliente = new HttpClient();
String body = "";
HttpStringContent contenido;
HttpResponseMessage miRespuesta = new HttpResponseMessage();
try
{
body = JsonConvert.SerializeObject(festival);
contenido = new HttpStringContent(body, Windows.Storage.Streams.UnicodeEncoding.Utf8, "application/json");
miRespuesta = await miCliente.PutAsync(new Uri(miConexion + "/" + festival.ID), contenido);
if (miRespuesta.IsSuccessStatusCode)
{
resultado = 1;
}
}
catch (SqlException e)
{
throw e;
}
return resultado;
}
That´s my body in request:
"{\"ID\":1,\"edicion\":\"Prueba año anterior\",\"nombre\":\"fgh\",\"fecha_inicio\":\"2017-10-01T00:00:00\",\"fecha_fin\":\"2017-10-03T00:00:00\",\"coordenadas\":\"asdf\",\"twitter\":\"\",\"facebook\":\"\",\"instagram\":\"\",\"web\":\"\",\"curiosidades\":\"\"}"
And that´s my API response (miRespuesta variable value):
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 2, Content:
Windows.Web.Http.HttpStreamContent, Headers:
{
Connection: close
Server: Apache/2.4.34 (Win32) OpenSSL/1.0.2o PHP/5.6.38
Date: Wed, 31 Oct 2018 21:47:36 GMT
X-Powered-By: PHP/5.6.38
}{
Content-Length: 0
Content-Type: text/html; charset=UTF-8
}}
Please help me if you know something.
UPDATE: When I see content of miCliente variable (the one with httpclient), I can see there´s a list element called DefaultRequestHeaders. Maybe there´s the problem? I have to edit these to make them compatible with PHP?
UPDATE 2:
I´ve changed two dates elements (fecha_inicio, fecha_fin) in database and my Festival class, so they are now varchar (string at class), trying if the problem was parsing datetimes and try saving as date in database, but still not work.
Postman successfully request:
PUT /festival/1 HTTP/1.1
Host: notrelevantbutwellformed.com
Content-Type: application/json
cache-control: no-cache
Postman-Token: 100313d6-7087-4712-8b93-17873e1db14b
{
"ID": "1",
"edicion": "fgh",
"nombre": "fgh",
"fecha_inicio": "2018-11-01",
"fecha_fin": "2018-11-01",
"coordenadas": "asdf",
"twitter": "asfd",
"facebook": "ffsd",
"instagram": "asrss",
"web": "noo va a petar",
"curiosidades": "sdfsdfdsf",
"url_logo": "",
"url_cartel": ""
}------WebKitFormBoundary7MA4YWxkTrZu0gW--
I'm developing an app in Xamarin.Forms (Shared Project Library) that sends a list of custom types (just a document number and a file type enumeration) to a locally hosted API.
If I capture the JSON string and send it from Postman, everything works fine, but as soon as I run the httpClient.PostAsync from the app, I receive the following error:
{StatusCode: 500, ReasonPhrase: 'Internal Server Error', Version: 1.1,
Content: System.Net.Http.StreamContent, Headers: { Cache-Control:
no-cache Pragma: no-cache Server: Microsoft-IIS/7.5 X-AspNet-Version:
4.0.30319 X-Powered-By: ASP.NET Date: Thu, 25 May 2017 16:00:44 GMT Content-Type: application/json; charset=utf-8 Expires: -1
Content-Length: 36 }}
I'm at a loss a to what I'm doing wrong. Can anyone help, please?
Type:
class EmailFiles
{
public string docNumber { get; set; }
public EmailFileTypes.EmailFileType type { get; set; }
}
Request:
List<EmailFiles> files = new List<EmailFiles>();
if (chkShipping.IsToggled)
{
EmailFiles file = new EmailFiles();
file.type = EmailFileTypes.EmailFileType.CustomerFile;
file.docNumber = Helpers.GlobalVariables.gSOLookup.PackList;
files.Add(file);
}
if (chkClosed.IsToggled)
{
EmailFiles file = new EmailFiles();
file.type = EmailFileTypes.EmailFileType.ClosedFile;
file.docNumber = Helpers.GlobalVariables.gSOLookup.Invoice;
files.Add(file);
}
if (chkInvoice.IsToggled)
{
EmailFiles file = new EmailFiles();
file.type = EmailFileTypes.EmailFileType.Invoice;
file.docNumber = Helpers.GlobalVariables.gSOLookup.Invoice;
files.Add(file);
}
string url = SetUrls.urlMtApi + "/api/EmailFile/?emailAddresses=" + strEmails;
string strJson = JsonConvert.SerializeObject(files);
//Results in: "[{\"docNumber\":\"234273\",\"type\":1},{\"docNumber\":\"633007\",\"type\":2}]" - which works in Postman!!
StringContent content = new StringContent(strJson, Encoding.UTF8, "application/json");
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.PostAsync(url, content);
Web Service:
[System.Web.Http.HttpPost]
public string EmailFile([FromBody]string jsonfiles, [FromUri] string emailAddresses)
{
List<EmailFiles> files = JsonConvert.DeserializeObject<List<EmailFiles>>(jsonfiles);
...
}
No need to manually deserialize the json body, just let the model binder do it for you by using correct parameters:
[System.Web.Http.HttpPost]
public MyModelReturnType EmailFile([FromBody] List<EmailFiles> files, [FromUri] string emailAddresses)
{
// the framework has already deserialized the json request for you
}
If you use string instead of the true model for your parameter the server will not be able to bind your request body to it, because it will expect a JSON string (surrounded by double quotes "), and this could cause a model binding exception that will lead to a 500 error in your client.
The same is for the return type, just use whatever class you want your client to receive as return type and do not use string if you want to send it Json.
I'm trying to use HttpClient to read the response content from a 3rd party API (Rackspace Cloud Files). Here's what I have so far. I can't seem to get the content.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Auth_User", username);
client.DefaultRequestHeaders.Add("X-Auth-Key", api);
client.GetAsync("identity.api.rackspacecloud.com".ToAbsoluteUrl()).ContinueWith(
(requestTask) =>
{
HttpResponseMessage response = requestTask.Result;
response.EnsureSuccessStatusCode();
response.Content.ReadAsAsync<string>().ContinueWith(
(readTask) =>
{
var result = readTask.Result;
});
});
This gives me "No 'MediaTypeFormatter' is available to read an object of type 'String' with the media type 'text/html'." error.
I need to retrieve the response details as noted in the Rackspace docs (example):
HTTP/1.1 204 No Content
Date: Mon, 12 Nov 2007 15:32:21 GMT
X-Storage-Url: https://storage.clouddrive.com/v1/CF_xer7_34
X-CDN-Management-Url: https://cdn.clouddrive.com/v1/CF_xer7_34
X-Auth-Token: eaaafd18-0fed-4b3a-81b4-663c99ec1cbb
Content-Length: 0
Content-Type: text/plain; charset=UTF-8
How do I get the response?
When I use ReadAsStringAsync, it gives my the HTML source of my page.
Thank you.