We use cookies to give you the best experience on our website. If you continue to browse, then you agree to our privacy policy and cookie policy. Image for the cookie policy date

Server Side Validation on File Upload.

Hi,
I am trying to do server side validation of a file upload.
I tried using Headers like the following post has but the headers get stripped out and downgrading dotNet Core from 2.2 is not an option.
I did check out the messages in the linked AspNetCore issues but was not able to resolve it. https://github.com/aspnet/AspNetCore/issues/8567


My main question is why don't you expose the Response content/body that could contain any validation errors on failure or other results on success?
I feel like this would be the correct way to handle validation. Not by putting messages in the header.

The only way I see around this issue for me is to store each file on the server then automatically do a second call to validate the now uploaded file and return the errors there. Or save the import details to a database and read from there after processing.

My WebAPI method is returning a model state dictionary with a list of validation errors.
You can see the response body in the network tab in chrome but there is no way I see to access it in the failure event of the uploader control.
The only change I can make on the server side that I can see in the client is changing the Status Code.
Expected Headers:


The only headers I get are in the below screen shot logged in the console. But I only get the Content-Type header.



5 Replies

SP Sureshkumar P Syncfusion Team November 4, 2019 11:49 AM UTC

Hi Jake, 
 
Greetings from Syncfusion support. 
 
we have checked the shared code snippet. You no need to pass the success and failure in header and get in server side for validation. Instead for every request the statusCode will be sent to the server. From these, we can know the success and failure of the request and able to validate in server-side. Please refer the code, 
 
public IActionResult Save(IList < IFormFile > UploadFiles) 
{ 
    var status = Response.StatusCode; 
    try { 
        foreach(var file in UploadFiles) 
        { 
            if (UploadFiles != null) { 
                var filename = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"'); 
                filename = hostingEnv.WebRootPath + $@"{filename}"; 
                if (!System.IO.File.Exists(filename)) { 
                    using(FileStream fs = System.IO.File.Create(filename)) 
                    { 
                        file.CopyTo(fs); 
                        fs.Flush(); 
                    } 
                } 
                else { 
                    Response.Clear(); 
                    Response.StatusCode = 204; 
                    Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = "File already exists."; 
                } 
            } 
        } 
    } 
    catch (Exception e) 
    { 
        Response.Clear(); 
        Response.ContentType = "application/json; charset=utf-8"; 
        Response.StatusCode = 204; 
        Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = "No Content"; 
        Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = e.Message; 
    } 
    return Content(""); 
} 
 
 
Kindly refer the screen shot:  
 
 
 
 
 
Regards, 
Sureshkumar P 



JA Jake November 4, 2019 08:11 PM UTC

Hi,
Thank you for your response.

The code you sent me works. I was able to send my validation back. However, it still feels like a hack.

In the code below
Instead of return Content("");
It should be return Ok(<<object to return on success.>>) or BadRequest(<<Object to return on failure.>> 
In my case it would be
return BadRequest(ModelState);

Do you know the reason why the response body is not exposed?

Response.body/content should be exposed on this interface. So you can upload a file and validate it server side properly.
export interface ResponseEventArgs {
    headers?: string;
    readyState?: object;
    statusCode?: object;
    statusText?: string;
    withCredentials?: boolean;
}

 
public IActionResult Save(IList < IFormFile > UploadFiles) 
{ 
    var status = Response.StatusCode; 
    try { 
        foreach(var file in UploadFiles) 
        { 
            if (UploadFiles != null) { 
                var filename = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"'); 
                filename = hostingEnv.WebRootPath + $@"{filename}"; 
                if (!System.IO.File.Exists(filename)) { 
                    using(FileStream fs = System.IO.File.Create(filename)) 
                    { 
                        file.CopyTo(fs); 
                        fs.Flush(); 
                    } 
                } 
                else { 
                    Response.Clear(); 
                    Response.StatusCode = 204; 
                    Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = "File already exists."; 
                } 
            } 
        } 
    } 
    catch (Exception e) 
    { 
        Response.Clear(); 
        Response.ContentType = "application/json; charset=utf-8"; 
        Response.StatusCode = 204; 
        Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = "No Content"; 
        Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = e.Message; 
    } 
    return Content(""); 
} 
 
 



SP Sureshkumar P Syncfusion Team November 5, 2019 09:13 AM UTC

Hi Jake, 
 
We have checked the reported requirement. Based on the requirement we have returned the modelState. Please refer the below code 
 
public IActionResult Save(IList<IFormFile> UploadFiles) 
{ 
    var status = Response.StatusCode; 
    try 
    { 
        foreach (var file in UploadFiles) 
        { 
            if (UploadFiles != null) 
            { 
                var filename = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"'); 
                filename = hostingEnv.WebRootPath + $@"\{filename}"; 
                if (!System.IO.File.Exists(filename)) 
                { 
                    using (FileStream fs = System.IO.File.Create(filename)) 
                    { 
                        file.CopyTo(fs); 
                        fs.Flush(); 
                    } 
                } 
                else 
                { 
                    Response.Clear(); 
                    Response.StatusCode = 204; 
                    Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = "File already exists."; 
                } 
            } 
        } 
    } 
    catch (Exception e) 
    { 
        Response.Clear(); 
        Response.ContentType = "application/json; charset=utf-8"; 
        Response.StatusCode = 204; 
        Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = "No Content"; 
        Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = e.Message; 
    } 
    return Ok(BadRequest(ModelState)); 
} 
 
 
We can see the content in response tab. 
 
 
 
Also, in response interface below are the available properties. 
 
 
We can able to inherit and use only the available properties. Since it is developed by Microsoft. 
 
 
Regards, 
Sureshkumar P 



JA Jake November 5, 2019 02:45 PM UTC

So how do you access that content?
I was able to see that content before in the response.

From the Web API link you sent:

Response implements Body, so it also has the following properties available to it:

Body.body Read only
A simple getter used to expose a ReadableStream of the body contents.
Body.bodyUsed Read only
Stores a Boolean that declares whether the body has been used in a response yet.

When you log out arguments for Success event  you get:




Maybe I am using this control wrong.
Any Web API Post I have ever done I am able to return and access content in the Angular client.

Jake


SP Sureshkumar P Syncfusion Team November 6, 2019 09:55 AM UTC

Hi Jake, 
 
You can get returned content through the ajax success method. We can able to get only via ajax call and success method. Please refer the below code snippet, 
 
<form id="formElem" method="post"> 
    <ejs-uploader id="UploadFiles" autoUpload="false"></ejs-uploader> 
    <input type="button" value="Post" onclick="btnClick()" /> 
</form> 
 
<script> 
    function btnClick() { 
        var uploaderObj = document.getElementById('UploadFiles').ej2_instances[0]; 
        var form = $('form')[0]; 
        var formData = new FormData(form); 
        formData.append("file", uploaderObj.element.files[0]); 
        $.ajax({ 
            type: "POST", 
            url: "/Home/Save", 
            data: formData, 
            contentType: false, 
            processData: false, 
            success: function (data) { 
                console.log(data); 
            } 
        }); 
    } 
</script> 
 
 
 
We created a sample based on your requirement. please find the sample here: https://www.syncfusion.com/downloads/support/directtrac/general/ze/WebApplication31918671491  
 
Regards, 
Sureshkumar P 


Loader.
Up arrow icon