How to update ModelStateError after file download? - asp.net-mvc

[Web dev noob - is not familiar with the terms javascript, jquery, ajax and similar terms, and cannot quite comprehend its concepts, and therefore most "basic" solutions presented in SO. Came from Winforms development]
What I'm trying to do:
Upload an excel file (multiple rows) that was encoded based on a template (there is an input type="file", and an Upload button)
Perform all kinds of validation (e.g. no file was selected, a file other than xls/xlsx is selected, the actual parsing and validation of the uploaded file, etc) - which is done using ModelState.AddModelError()
If on parsing the file, there were errors found by the validation logic, the excel file that was uploaded is modified, and a new column "Errors" is added to it, which contains the errors found per row, and is downloaded as some log file (still an excel file), where I used EPPlus.
If no errors, just display a message informing the user that the upload was a success.
My problem:
So far I can already upload a file, and then validate if success or with errors. If the latter, I can already download the error log file. What I can't do is try to combine actions that produces a validation error, and then followed by intentionally uploading an excel file that has errors. Please refer below for the actual steps to be taken.
Step 1: For example, the user clicked the "Upload" button without first browsing for the relevant file, the message for example "Error: No file was selected" is displayed.
Step 2: The user now selects a file after performing step 1, but this file has errors that won't pass the validation logic.
private void CreateErrorLog(Stream stream, string fileName)
{
this.ModelState.AddModelError("BulkUploadError", "Please refer to the downloaded file for errors.");
// generation of the excel log file
...
this.DownloadExcel(excelPackage, Path.GetFileName(newFileName));
}
public void DownloadExcel(ExcelPackage package, string fileName)
{
this.Response.Clear();
this.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
this.Response.AddHeader("content-disposition", $"attachment;filename={fileName}");
this.Response.BinaryWrite(package.GetAsByteArray());
this.Response.End();
}
Now as I expect it, this code should replace the ModelState error that was shown in step 1: "Error: No file was selected" → "Please refer to the downloaded file for errors."
The actual scenario:
The log file is downloaded, but the error message is not updated.
What I tried to do (considering my zero knowledge in web dev):
Tried some random debugging - I commented out the line wherein DownloadExcel method is called, and with this, the error message successfully updates.
What I can formulate from this:
Is that the download definitely blocks the updating of the ModelState error message that is displayed.
Requirement:
Definitely need to download the error log file
Also definitely need the error message to match the actual scenario
How can I now do both?
Also, my View is something like this:
#{
ViewBag.Title = "Bulk Upload";
}
<h2>Bulk Upload</h2>
#using (Html.BeginForm("Bulk", "MyModel", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.ValidationMessage("BulkUploadError", new { #class = "text-danger" })<br>
<label for="file">File path:</label>
<input type="file" name="file" id="file" accept=".xls, .xlsx" /><br><br>
<input type="submit" value="Upload" class="btn main-button" />
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}

Related

Is there a way to get a QR code image with Google Apps Script using the POST method of the Google Charts API?

I am using a Google Script to generate tickets to an event, and the ticket includes a QR code which goes to a pre-filled Google Form link. Since it's pre-filled, the string is quite long, and the Google Charts API for creating QR codes will not accept a string of text that long using a GET request, but I can't find any documentation of how to code the POST request into Apps Script. How do I generate a POST request in Apps Script that will return an image of the QR code which I can then insert into the document?
I already tried the GET request, and it truncates the URL before encoding it into a QR code. That gets me to the Google Form, but not the pre-filled version that the link generates (actually pretty smart on Google's part to have it truncate the string in a place that still gives a usable URL, but that's for another day...)
I have also tried the HtmlService to render the QR code using the POST method with the Charts API in an HTML form that automatically submits on the loading of that HTML. If I use showSidebar(), this will open the image in a new tab, but I haven't figured out how to return that image so that it can be inserted into the document.
I've also tried creating a blob with the HTML and then saving the blob as a PNG, but from the research I've done, the .getAs() method doesn't render images when converting the HTML.
The renderQR function:
function renderQR(inputUrl) {
var html = HtmlService.createTemplateFromFile('QREncode.html');
html.url = inputUrl;
var rendered = html.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setHeight(300)
.setWidth(300);
return rendered;
}
The QREncode.html file:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script type='application/javascript'>
// Send the POST when the page is loaded,
// which will replace this whole page with the retrieved chart.
function loadGraph() {
var frm = document.getElementById('post_form');
if (frm) {
frm.submit();
}
}
</script>
</head>
<body onload="loadGraph()">
<form action='https://chart.googleapis.com/chart' method='POST' id='post_form'>
<input type='hidden' name='cht' value='qr' />
<input type='hidden' name='chl' value='<?= url ?>' />
<input type='hidden' name='chs' value='300x300' />
<input type='submit'/>
</form>
</body>
</html>
When I treat the return from the renderQR() function as an image, Apps script gives an error saying that it is "Invalid image data", which makes sense -- but how do I convert it into an image, or is there a better or simpler way I could be doing this?
You need to get the qr code in the Apps Script, not in the browser:
var imageData = UrlFetchApp.fetch('https://chart.googleapis.com/chart', {
'method' : 'post',
'payload' : {
'cht': 'qr',
'chl': 'https://google.com',
'chs': '300x300'
}}).getContent();
For those looking for a formula solution (without Apps Script)
Reference: https://www.benlcollins.com/spreadsheets/qr-codes-in-google-sheets/
Solution:
=IMAGE("https://chart.googleapis.com/chart?chs=250x250&cht=qr&chl="&ENCODEURL(A1))

How to fix parsing errors in form POST request in Rocket?

I am making a very simple web app using the rust Rocket framework. I have a very simple HTML file that has a form, as follows:
<form action="/search" method="post" accept-charset="utf-8">
Search Term:<input type="text" name="searchterm">
<input type="submit" value="search">
</form>
Next, here are my rocket functions to deal with the requests. I have a get function that spits out index.html when accessing "/", then for my form, I have the following functions:
#[derive(FromForm)]
pub struct Request<'r> {
payload: &'r RawStr,
// we can add more if we want later on, for other form options...
}
#[post("/search", data = "<data>")]
pub fn process(data: Form<Request>) -> Result<Redirect, String> {
if data.payload == "Hello!" {
Ok(Redirect::to("/search/Hello"))
} else {
Err(format!("Unknown search term, '{}'.", data.payload))
}
}
Then, this is to response to the GET requests:
#[get("/search/<term>")]
pub fn response(term: &RawStr) -> String {
format!("You typed in {}.", term)
}
Like I said, very simple, very barebones, just trying to tiptoe into both Rust and Web Apps at the same time. I do not have much experience in either. My issue is, when using the field presented to the user in my html file, the server returns an error:
POST /search application/x-www-form-urlencoded:
=> Matched: POST /search (process)
=> Error: The incoming form failed to parse.
=> Outcome: Failure
=> Warning: Responding with 422 Unprocessable Entity catcher.
=> Response succeeded.
If I go directly, to localhost:8000/search/Hello! I can see that my GET response works. But if I use my form it refuses to parse. What am I doing wrong? I am simply attempting to make a web app that takes an input, and based on that input, returns something. Website redirection, web scraping, I am not sure on the specifics of functionality yet, but I need to be able to type something into the form and obtain it for use in my rust code later. Any help would be appreciated!
I think the problem is that your form parameter name (<input type="text" name="searchterm">) doesn't match with your struct field name (payload). If you rename one or the other so they would match, your form should work.

Integrating a Fable/Elmish app with Stripe checkout

Edit: Someone on gitter has suggested this:
https://stripe.com/docs/recipes/elements-react
...so I'm trying that and will report back here.
I'm creating a Fable/Elmish app which will take payments via the Stripe 'Checkout' api (https://stripe.com/docs/checkout/aspnet). Stripe mandates that you get the checkout.js script on demand (i.e. not via Node). When placed within a form element and provided with a few values via data- attributes, the script adds a payment button to your page. For example here is a working .cshtml view from a an ASP dotnet app:
#using Microsoft.Extensions.Options
#inject IOptions<StripeSettings> Stripe
<form action="/Home/Charge" method="POST">
<article>
<label>Amount: $5.00</label>
</article>
<script src="//checkout.stripe.com/v2/checkout.js"
class="stripe-button"
data-key="#Stripe.Value.PublishableKey"
data-locale="auto"
data-description="Sample Charge"
data-amount="500"
data-billing-address=true>
</script>
</form>
I am trying to do the equivalent in my Elmish app, which I think boils down to this:
let view (model : Model) (dispatch : Msg -> unit) =
let payScript =
script
[
Src "//checkout.stripe.com/v2/checkout.js"
Class "stripe-button"
Data ("key","pk_test_REDACTED") // Should come from config via the model
Data ("locale", "auto")
Data ("description", "Sample charge")
Data ("amount", "999")
Data ("billing-address", true)
]
[]
div []
[
Text "This is the payment area"
form [
Action "/Home/Charge"
Method "POST"
]
[
article []
[
label [] [ Text "Amount £9.99" ]
]
payScript
]
]
When rendered this appears like this on the client:
This is the payment area
Amount £9.99
* expected button here *
...but the button hasn't been created, which suggests to me that the script hasn't run, or that it hasn't found the form to insert the button. The script element does appear within the form in the rendered page:
<div>This is the payment area>
<form action="/Home/Charge" method="POST">
<article><label>Amount £9.99</label></article>
<script src="//checkout.stripe.com/v2/checkout.js" class="stripe-button" data-key="pk_test_REDACTED" data-locale="auto" data-description="Sample charge" data-amount="999" data-billing-address="true">
</script>
</form>
</div>
I don't see any browser errors on the Chrome console, other than the socket errors one usually gets. (I've back-to-backed with and without the checkout script and there are two identical errors in each case.)
If I place the script and form within my Index.htmlwith hardwired values, the button does appear, though obviously not in the right place.
What am I missing? Is there something about being within an Elmish app that stops external scripts being executed?
Many thanks!

How can I show a confirmation page before saving an image with carrierwave in rails?

I have a form in which a user may upload an image, and I'm using carrierwave to process it. Currently, the user makes a post by filling out a form and clicking submit. This takes the user to a confirmation page where all the information is displayed once more after going through rails validations, including a preview of the image, before actually creating the post. I need to display the image on this page before actually saving and sending it into to S3.
#topic_picture_uploader = TopicPictureUploader.new
#topic_picture_uploader.cache!(params[:topic_picture])
I tried to cache it like this, but trying to access anything only returns nil. How can I simply display the image before saving it to a model?
No need to submit the page, Just have the preview button on the form and on clicking of it show whatever the data you want to show and along with it show the submit button as well to complete the post.Please take a look at the sample JS code below:
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#blah').attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
$("#imgInp").change(function(){
readURL(this);
});
and the associated HTML:
<form id="form1" runat="server">
<input type='file' id="imgInp" />
<img id="blah" src="#" alt="your image" />
</form>

Uploading files using Vapor

I've seen in documentation in Body section that there's a support for file uploading right now - or at least I understand it this way 😅
I have no strong foundation in backend development - especially if it comes to frameworks which are still eveloving so fast as Vapor do.
I wonder if someone can provide some real life example of file uploading? I was hoping for a simple web page with possibility to upload a file to the backend and then store it for future usage.
Vapor allows for file upload using the Multipart encoding. You can read more about HTTP upload here:
How does HTTP file upload work?
And also here:
What does enctype='multipart/form-data' mean?
So the HTML code to upload a file to Vapor would look something like:
<form action="upload" method="POST" enctype="multipart/form-data">
<input type="text" name="name">
<input type="file" name="image">
<input type="submit" value="Submit">
</form>
And then the code in Vapor
drop.get("form") { req in
return try drop.view("form.html")
}
drop.post("upload") { req in
let name = req.data["name"]
let image = req.data["image"] // or req.multipart["image"]
...
}
In terms of how to store the image, that is up to you. You can store in a database or create a folder on the system to which you have write access.

Resources