|
Google for how to solve CORS problems, this is a well-documented problem. Your API and client are running on separate sites (different ports on the same machine count as different sites) so the API needs to add a header to its return to say that the client host can have access. There are various ways of doing this.
|
|
|
|
|
Hi, thank you I could able to resolve this issue by implementing CORS on my Web API, here is the Code change I did in my Web API. Thank you for all the support you Geeks have given me - thanks for the help
public void ConfigureServices(IServiceCollection services)
{
string configValue = Configuration.GetValue<string>("CORSComplianceDomains");
string[] CORSComplianceDomains = configValue.Split("|,|");
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder =>
{
builder.WithOrigins("http://localhost:3000");
});
options.AddPolicy("AnotherPolicy",
builder =>
{
builder.WithOrigins(CORSComplianceDomains)
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
}
And added the urls in the appsettings.json file so that any user can add the new urls without much sweating.
"CORSComplianceDomains": "http://localhost:3000|,|http://www.contoso.com"
Thank you very much - I put my answer here so that someone can get it - thanks for jumping in and helping please - I appreciated it - thank you so much.
|
|
|
|
|
Hi I have React application, which is calling api on download button click, on the button click the values are being posted properly and api is returning the zip file, but I want to let the zip file to be downloaded on the client side.
Here is my code for the API
[HttpPost]
public FileContentResult Post([FromForm] string communityName, [FromForm] string files)
{
var removedInvalidCharsFromFileName = removeInvalidCharsFromFileName(files);
var tFiles = removedInvalidCharsFromFileName.Split(',');
string rootPath = Configuration.GetValue<string>("ROOT_PATH");
string communityPath = rootPath + "\\" + communityName;
byte[] theZipFile = null;
using (MemoryStream zipStream = new MemoryStream())
{
using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
{
foreach (string attachment in tFiles)
{
var zipEntry = zip.CreateEntry(attachment);
using (FileStream fileStream = new FileStream(communityPath + "\\" + attachment, FileMode.Open))
using (Stream entryStream = zipEntry.Open())
{
fileStream.CopyTo(entryStream);
}
}
}
theZipFile = zipStream.ToArray();
}
return File(theZipFile, "application/zip", communityName + ".zip");
}
And my React components download method is as below:
handleDownload = (e) => {
e.preventDefault();
var formData = new FormData();
formData.append('communityname', this.state.selectedCommunity);
formData.append('files', JSON.stringify(this.state['checkedFiles']));
let url = clientConfiguration['filesApi.local'];
axios({
method: 'post',
url: url,
data: formData
});
window.open(url, '_blank');
}
This window.open(url, '_blank'); is not letting me download the zip files, aren't the path of the zip file and path of the api the same? What can I do to make sure the file downloads on the client side - any help - thanks in advance.
|
|
|
|
|
The problem is, you're making two separate requests - an AJAX POST request, which throws away the response; and a regular GET request with no parameters, which doesn't match your controller action.
Probably the simplest option would be to create a hidden <form> element, populate the values, and then submit that form.
handleDownload = (e) => {
e.preventDefault();
let form = document.createElement("form");
form.action = clientConfiguration['filesApi.local'];
form.method = "POST";
form.target = "_blank";
let input = document.createElement("input");
input.type = "hidden";
input.name = "communityname";
input.value = this.state.selectedCommunity;
form.appendChild(input);
this.state['checkedFiles'].forEach((f) =>
{
let input = document.createElement("input");
input.type = "hidden";
input.name = "files";
input.value = f;
});
document.body.appendChild(form);
form.submit();
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Hi Richard, after researching online a little bit I could able to do the following, now the zip file is being downloaded but the problem it seems its not downloading correctly
handleDownload = (e) => {
e.preventDefault();
var formData = new FormData();
formData.append('communityname', this.state.selectedCommunity);
formData.append('files', JSON.stringify(this.state['checkedFiles']));
let url = clientConfiguration['filesApi.local'];
axios({
method: 'post',
url: url,
data: formData
})
.then(res => {
console.log(res.data);
var binaryData = [];
binaryData.push(res.data);
const src = window.URL.createObjectURL(new Blob(binaryData, { type: "application/zip" }));
var fileName = `${this.state['selectedCommunity']}.zip`;
saveAs(src, fileName);
});
}
The error that I am getting when uzipping the file is as below:
Can not open the file as zip archive, is not archiving, Warning Headers error. Any help please?
modified 14-Oct-19 18:16pm.
|
|
|
|
|
What does your console.log output show?
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
It shows the byte Array, it is being returned but what is the problem in creating right zip file I am not able to understand I changed a little bit the API method to return the bytes instead of file, still not much help, its the same error message unable to open the file as zip I don't know is the encoding etc different or the bytes limit is the problem, I am not sure. But there is another app doing the same thing in jQuery but its able to download the file - thank you Richard.
|
|
|
|
|
Actually I downloaded the file from the Server code, so the bytes are correct, then I modified the api code to read that newly created zip file as byte array then return, the array, I thought maybe accuracy is lost because of that, then saving that file as zip, still getting unable to open the file, and it says I have to add files to the zip folder before extracting it. I am not understanding what could be the reason. It is working with jQuery though but that's direct submit - any help please?
|
|
|
|
|
You're going to need to compare the bytes of the file that works with the bytes of the file that doesn't to see if there's any clue what the problem is.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Hi Richard, when I checked the length of the array on the Server it is: 113175
and on at the client it is: 150900, why is client side the array length is more IDK - any idea please?
|
|
|
|
|
No idea. What does the axios method look like?
I've just done something similar today using jQuery, and I had to explicitly set xhrFields: { responseType: "blob" } to get the binary data to download properly. Maybe there's a similar issue with your method?
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
I could able to resolve that issue by returning byte array but lead wants it only by using FileStreamResult - hah, any help, how to handle FileStreamResult, I am not sure if this Result post-fix good for Web Api because it looks View specific to me. If you have some idea please help me, an another day in this industry
|
|
|
|
|
I don't follow - a FileStreamResult just returns the bytes from the stream. What are you doing differently if you return it as a byte array?
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
I am not sure but it is giving me error code 500 Richard, any help please - thank you
|
|
|
|
|
I completed the code I am pasting it here as answer to my own question
|
|
|
|
|
axios is same as fetch, we have fetch I think almost all js libraries including Typescript and Angular. I am not sure the difference but responseType: 'arraybuffer' worked for me, if the return type is FileStreamResult which is of 'octet-stream' on the api side. Yeah its little bit complicated as many return types and as we don't much of it - thanks for jumping in Richard.
|
|
|
|
|
Finally implemented by using the FileStreamResult as well maybe some people would be needed this, here is my API code and then I made call to the post method using axios, so here is my React code. In the axios call responseType becomes arraybuffer and the in the blob declaration it becomes the application/octet-stream type, Hence it completes everything, as I have imported the file-saver, I could able to use saveAs method of it. Finally after many efforts and hearing the screaming from PM, yes it is achieved - but that's the life of any Software Programmer.
Here is Web Api code C#:
[EnableCors("AnotherPolicy")]
[HttpPost]
public FileStreamResult Post([FromForm] string communityName, [FromForm] string files)
{
var removedInvalidCharsFromFileName = removeInvalidCharsFromFileName(files);
var tFiles = removedInvalidCharsFromFileName.Split(',');
string rootPath = Configuration.GetValue<string>("ROOT_PATH");
string communityPath = rootPath + "\\" + communityName;
MemoryStream zipStream = new MemoryStream();
using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
{
foreach (string attachment in tFiles)
{
var zipEntry = zip.CreateEntry(attachment);
using (FileStream fileStream = new FileStream(communityPath + "\\" + attachment, FileMode.Open))
{
using (Stream entryStream = zipEntry.Open())
{
fileStream.CopyTo(entryStream);
}
}
}
}
zipStream.Position = 0;
return File(zipStream, "application/octet-stream");
}
Then my client side React code is here:
handleDownload = (e) => {
e.preventDefault();
var formData = new FormData();
formData.append('communityname', this.state.selectedCommunity);
formData.append('files', JSON.stringify(this.state['checkedFiles']));
let url = clientConfiguration['filesApi.local'];
axios({
method: 'post',
responseType: 'arraybuffer',
url: url,
data: formData
})
.then(res => {
let extension = 'zip';
let tempFileName = `${this.state['selectedCommunity']}`
let fileName = `${tempFileName}.${extension}`;
const blob = new Blob([res.data], {
type: 'application/octet-stream'
})
saveAs(blob, fileName)
})
.catch(error => {
console.log(error.message);
});
};
this event is called when button is clicked or form submitted. Thanks for all the support the SO has given - thanks a lot.
|
|
|
|
|
Hi I have stored an Array of strings into a state object, when I am trying to retrieve it as appended string, it is giving me appended objects as below, my code is as below and I am getting the result as below:
handleDownload = (e) => {
e.preventDefault();
var formData = new FormData();
formData.append('communityname', this.state.selectedCommunity);
formData.append('files', this.state['files']);
alert(this.state['files'].join(','));
let url = clientConfiguration['filesApi.local'];
axios({
method: 'post',
url: url,
data: formData
});
}
My Results are coming as below:
[object Object],[object Object],[object Object],[object Object]
Any help would be very much appreciated - thanks in advance
|
|
|
|
|
Looks like the values stored in this.state['files'] aren't what you thought they are.
Try adding console.debug(this.state['files']); to the method, and look at the output in the browser's developer console.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Thanks Richard, it has been resolved, I have to stringify it before posting it into the Api, here is the code that resolved it. Thanks for jumping into help.
handleDownload = (e) => {
e.preventDefault();
var formData = new FormData();
formData.append('communityname', this.state.selectedCommunity);
formData.append('files', JSON.stringify(this.state['checkedFiles']));
let url = clientConfiguration['filesApi.local'];
axios({
method: 'post',
url: url,
data: formData
});
}
in the above code instead of files I am just passing the selected files and posting them, but I have a question, can't json or react post handle if I want to post Array as it is? If it can - I want to understand that as well - but thanks a lot again Richard.
|
|
|
|
|
I have an event like below:
handleDownload(e) {
e.preventDefault();
alert('Hi');
let communityName = this.state['selectedCommunity'];
let files = this.state[files];
fetch(clientConfiguration['filesApi.local'], {
method: 'POST',
headers: new Headers(),
body: JSON.stringify({ communityName: communityName, body: files })
}).then((res) => res.json())
.then((data) => console.log(data))
.catch((err) => console.log(err))
};
I have a button as below:
renderDownloadButton() {
if (this.state.files && this.state.files.filter(i => i.checked).length) {
return (
<button id="download" styles="display: none;" onClick={this.handleDownload} >
Download
</button>
);
}
};
It fires but it is giving following error, any help please - thank you. At
let communityName = this.state['selectedCommunity'];
its giving me the error;
Can not read property state undefined
Any help please?
|
|
|
|
|
state is undefined, it has no value and is not null either.
state is not inside the function, so you have to initialize earlier in code.
Or maybe pass the state into the function, but I can't see how you can pickup the state in the HTML section and not have it valid in the function. Check your renderDownloadButton
Not sure how React works but I would imagine this is required.
onClick={this.handleDownload($event)}
If it ain't broke don't fix it
Discover my world at jkirkerx.com
|
|
|
|
|
Yes it was taking the function as the current object that's how all javascript libraries work, so changing the scope worked for me - thanks a lot.
|
|
|
|
|
Hi mates, I am conditionally setting up the state in my React component, I did it in the following way, it works but please let me know if there is any better approach to do it in React - thanks a lot.
handleDDLCommunityChange = event => {
this.setState(
{
selectedCommunity: event.target.value
});
if (event.target.value == "") {
this.setState(
{
files: null
});
}
else {
fetch(clientConfiguration['filesApi.local'] + '/' + event.target.value)
.then((response) => { return response.json(); })
.then(data => {
this.setState({
files: data.map(file => {
return {
fileName: file,
checked: false
}
})
})
})
.catch(error => {
console.log(error);
debugger;
});
}
};
modified 9-Oct-19 21:09pm.
|
|
|
|
|
Why not this instead? Maybe the target value is null or undefined and not a blank string.
This should check all 3 conditions in TypeScript at least.
if (!event.target.value)
If it was me, I would not inline this, but if it's proven to always work guess it's ok.
fetch(clientConfiguration['filesApi.local'] + '/' + event.target.value)
const fApi = clientConfiguration['filesApi.local'];
if (fApi) {
fetch(fApi + '/' + event.target.value)
Have you considered trying Jet Brains Refactor Ultimate to sharpen your skills?
ReSharper: The Visual Studio Extension for .NET Developers by JetBrains
If it ain't broke don't fix it
Discover my world at jkirkerx.com
|
|
|
|
|