IFormFile is null, Razor - asp.net-core

I'm trying to bind an image uploading but facing the problem with sending this image to the controller.
I'm using Razor and .net core 2.2.
My Controller
[HttpPost(nameof(MealController))]
[Authorize(Roles = UserRoles.Moderator)]
public IActionResult Update(MealModel meal, IFormFile pic)
{
if (ModelState.IsValid)
{
try
{
var changedMeal = _mapper.Map<MealModel, MealDTO>(meal);
_mealService.Update(changedMeal, pic);
}
catch (Exception e)
{
return NotFound();
}
return RedirectToAction("Index");
}
return View(meal);
}
My form
<form class="form" method="post" asp-controller="Meal" asp-action="Update">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Продукт</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="product">
<div class="product-img">
<div class="selector">
<img src="#Model.ImagePath" class="img-thumbnail" id="selectedImg" alt="Product Image"/>
<div class="form-group">
<input type="file" class="custom-file-input" id="pic-input" name="pic"
onchange="readURL(this, 'selectedImg')">
#* <label class="custom-file-label" for="pic-input"></label> *#
</div>
</div>
</div>
<div class="product-info">
<div class="form-group name">
<label for="name">Назва продукту</label>
<input class="form-control" type="text" placeholder="Назва" id="name" name="name" asp-for="Name"
value="#Model.Name">
</div>
<div class="form-group">
<label for="name">Ціна</label>
<input class="form-control" placeholder="Price" id="price" type="number" asp-for="Price"
value="#Model.Price">
</div>
<div class="form-group">
<label for="name">Вага</label>
<input class="form-control" id="weight" type="number" asp-for="Weight"
value="#Model.Weight">
</div>
<div class="comments">
<textarea class="form-control" rows="3" cols="5" placeholder="Склад" asp-for="Description" value="#Model.Description"></textarea>
</div>
<input type="hidden" asp-for="MealGroupId" value="#Model.MealGroupId"/>
<input type="hidden" asp-for="Id" value="#Model.Id"/>
#* <input type="hidden" asp-for="ImagePath" value="#Model.ImagePath"/> *#
</div>
</div>
</div>
<div class="modal-footer">
<div class="action-button">
<button type="submit"
class="btn btn-success ok-button">
<span>Додати</span>
</button>
<button type="reset"
class="btn btn-danger remove-button">
<span>Відмінити</span>
</button>
</div>
</div>
</div>
</form>
When I debug, IFormFile is always null. I tried using [FromForm] but it didn't help. I googled and found out that my name in form and parameter name was different but it had effect. Could you, please give me a little hint on how I can solve it. Thank you and have a good day!

You have to specify <form>'s enctype attribute set to multipart/form-data to be able to send files to server
<form class="form" method="post" asp-controller="Meal" asp-action="Update" enctype="multipart/form-data">

Related

How to display validation summary below the input controls?

I have the following form:
<form asp-controller="Manage" asp-action="RemoveDetails" method="post" class="form-horizontal">
#if (Model.RequirePassword)
{
<div id="password-container">
<div class="col-md-6">
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" id="password" autocomplete="current-password" aria-required="true" />
<small>
<span asp-validation-for="Password" class="text-danger"></span>
</small>
</div>
</div>
</div>
}
<div class="col-6">
<hr class="mt-2 mb-3">
<button type="submit" class="btn btn-style-1 btn-danger">Confirm</button>
</div>
<div class="row">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
</div>
</form>
If I leave the password empty, a validation message (the model uses DataAnnonations) is displayed right below the control. This is fine.
If I enter the wrong password, the Post action validates it and adds an error to the ModelState. This error is displayed below the form.
Is it possible to display such model errors below the relevant controls?
Try this code in Controller: ModelState.AddModelError("Password", "validation summary error."); The error message won't display in <div asp-validation-summary="ModelOnly"></div>, but it can display error message in #Html.ValidationSummary(false, "", new { #class = "text-danger" }).
<form asp-controller="Home" asp-action="RemoveDetails" method="post" class="form-horizontal">
<div id="password-container">
<div class="col-md-6">
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" id="password" autocomplete="current-password" aria-required="true" />
<small>
#*<span asp-validation-for="Password" class="text-danger"></span>*#
#Html.ValidationSummary(false, "", new { #class = "text-danger" })
</small>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label asp-for="Description"></label>
<input asp-for="Description" class="form-control" id="description" autocomplete="current-password" aria-required="true" />
<small>
<span asp-validation-for="Description" class="text-danger"></span>
</small>
</div>
</div>
</div>
<div class="col-6">
<hr class="mt-2 mb-3">
<button type="submit" class="btn btn-style-1 btn-danger">Confirm</button>
</div>
<div class="row">
<div asp-validation-summary="ModelOnly"></div>
</div>
</form>
[HttpPost]
public IActionResult RemoveDetails(RemoveDetail rdl)
{
if(ModelState.IsValid)
{
var pwd = rdl.Password;
//do something here and find the password is wrong
//code below won't display error message in <div asp-validation-summary="ModelOnly" class="text-danger">
//but can display error message in #Html.ValidationSummary(false, "", new { #class = "text-danger" })
ModelState.AddModelError("Password", "validation summary error.");
return View(rdl);
}
return View(rdl);
}

How to pass data in Kotlin Ktor Routing without saving the data?

Is it possible to pass various data in the Routing.kt class between different routes without saving the data in a database?
I'm calling a rest api in a search ui "search.ftl" and want to show the response data in another ui "found.ftl" and they're in different fields. If the data looks good the user can click "save" and then the data really go into the database.
At the end of get("field") I need to pass the data to get("found).
That's my code so far:
Routing.kt:
route("search") {
get {
call.respond(FreeMarkerContent("search.ftl", model = null))
}
get("field") {
// API-Call and json data in response
val title = volumeInfoObject?.get("title")
val author = authors?.get(0)
val publisher = volumeInfoObject?.get("publisher")
val pageCount = volumeInfoObject?.get("pageCount")
client.close()
call.respondRedirect("/search/found")
// How to pass data to get("found")?
}
get("found") {
call.respond(FreeMarkerContent("found.ftl", model = null))
}
}
Search.ftl:
<#import "_layout.ftl" as layout />
<#layout.header>
<div>
<div class="text-center">
<h1 class="display-4">Search</h1>
</div>
<!-- // style="border:1px solid red; -->
<div class="container">
<div class="row">
<div class="form-group has-search">
<span class="fa fa-search form-control-feedback"></span>
<form action="/search/field" method="get">
<input type="text" class="form-control" name="isbn">
</form>
</div>
</div>
<br><br><br>
<div class="row">
<div class="col-sm-3">
<div class="img"><img src="/static/500x900.png" class="img-fluid" alt="Responsive image"></div>
</div>
<div class="col-sm-9">
<div class="row">
<div class="col-8 col-sm-6">
</div>
<div class="col-4 col-sm-6">
<form>
<fieldset disabled>
<legend>Book Information</legend>
<div class="mb-3">
<label for="disabledTextInput" class="form-label">Titel</label>
<input type="text" id="disabledTextInput" class="form-control" placeholder="Disabled input">
</div>
<div class="mb-3">
<label for="disabledTextInput" class="form-label">Author</label>
<input type="text" id="disabledTextInput" class="form-control" placeholder="Disabled input">
</div>
<div class="mb-3">
<label for="disabledTextInput" class="form-label">Publisher</label>
<input type="text" id="disabledTextInput" class="form-control" placeholder="Disabled input">
</div>
<div class="mb-3">
<label for="disabledTextInput" class="form-label">Pages</label>
<input type="text" id="disabledTextInput" class="form-control" placeholder="Disabled input">
</div>
<button type="submit" class="btn btn-primary">Save</button>
</fieldset>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</#layout.header>
Found.ftl:
<#-- #ftlvariable name="book" type="com.nw.models.Book" -->
<#import "_layout.ftl" as layout />
<#layout.header>
<div>
<div class="text-center">
<h1 class="display-4">Search</h1>
</div>
<!-- // style="border:1px solid red; -->
<div class="container">
<div class="row">
<div class="col-sm-3">
<div class="img"><img src="/static/500x900.png" class="img-fluid" alt="Responsive image"></div>
</div>
<div class="col-sm-9">
<div class="row">
<div class="col-8 col-sm-6">
</div>
<div class="col-4 col-sm-6">
<form action="/search/found" >
<legend>Book Information</legend>
<div class="mb-3">
<label for="disabledTextInput" class="form-label">Titel</label>
<input type="text" id="disabledTextInput" class="form-control" name="title" value="title">
</div>
<div class="mb-3">
<label for="disabledTextInput" class="form-label">Author</label>
<input type="text" id="disabledTextInput" class="form-control" placeholder="Disabled input">
</div>
<div class="mb-3">
<label for="disabledTextInput" class="form-label">Publisher</label>
<input type="text" id="disabledTextInput" class="form-control" placeholder="Disabled input">
</div>
<div class="mb-3">
<label for="disabledTextInput" class="form-label">Pages</label>
<input type="text" id="disabledTextInput" class="form-control" placeholder="Disabled input">
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</#layout.header>
The solution ist now like this:
I'm saving the found book directly to db and show it on my /found route:
call.respondRedirect("/search/found/${book?.id}")
On the /found page there is a delete button, so If you are not happy with the result you can directly delete the book.
get("found/{id}") {
val id = call.parameters.getOrFail<Int>("id").toInt()
call.respond(FreeMarkerContent("edit.ftl", mapOf("book" to bookFacade.book(id))))
}

How to hide some textfields in razor view in asp.net core

I'm implementing a project by asp.net core. I have a selectlist item which its options are loaded from a model and it has 2 items. My problem is, I want in the razor view, according to the selected option in selectlist, some TextFields be shown to the user. For example if the user choose the first option, three of the fields be shown to the user and if the user selects the second option, other fields be shown to the user meanwhile the other three field be hidden. For doing this. I have tried some code like below in my Create view:
#model CSDDashboard.Models.ApplicantViewModel
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"> </script>
<script>
$(document).ready(function () {
$('#applicantvm.ApplicantType').change(function () {
var value = $(this).val();
if (value == '1') {
$(".legal").hide();
} else {
$(".person").show();
}
});
});
</script>
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Applicant</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="applicantvm.ApplicantType" class="control-label"></label>
<select asp-for="applicantvm.ApplicantType" class="form-control" asp-items="ViewBag.ApplicantType"></select>
</div>
<div class="form-group">
<label asp-for="applicantvm.Address" class="control-label"></label>
<input asp-for="applicantvm.Address" class="form-control" />
<span asp-validation-for="applicantvm.Address" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="applicantvm.Description" class="control-label"></label>
<input asp-for="applicantvm.Description" class="form-control" />
<span asp-validation-for="applicantvm.Description" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="applicantvm.Name" class="control-label"></label>
<input asp-for="applicantvm.Name" class="form-control" />
<span asp-validation-for="applicantvm.Name" class="text-danger"></span>
</div>
<div class="form-group legal">
<label asp-for="legalapplicantvm.EconomicCode" class="control-label"></label>
<input asp-for="legalapplicantvm.EconomicCode" class="form-control" />
<span asp-validation-for="legalapplicantvm.EconomicCode" class="text-danger"></span>
</div>
<div class="form-group legal">
<label asp-for="legalapplicantvm.NationalCode" class="control-label"></label>
<input asp-for="legalapplicantvm.NationalCode" class="form-control" />
<span asp-validation-for="legalapplicantvm.NationalCode" class="text-danger"></span>
</div>
<div class="form-group legal">
<label asp-for="legalapplicantvm.RegisterNo" class="control-label"></label>
<input asp-for="legalapplicantvm.RegisterNo" class="form-control" />
<span asp-validation-for="legalapplicantvm.RegisterNo" class="text-danger"></span>
</div>
<div class="form-group person">
<label asp-for="personapplicantvm.BirthCertificateNo" class="control-label"></label>
<input asp-for="personapplicantvm.BirthCertificateNo" class="form-control" />
<span asp-validation-for="personapplicantvm.BirthCertificateNo" class="text-danger"></span>
</div>
<div class="form-group person">
<label asp-for="personapplicantvm.IssuePlace" class="control-label"></label>
<input asp-for="personapplicantvm.IssuePlace" class="form-control"/>
<span asp-validation-for="personapplicantvm.IssuePlace" class="text-danger"></span>
</div>
<div class="form-group person">
<label asp-for="personapplicantvm.NationalCode" class="control-label"></label>
<input asp-for="personapplicantvm.NationalCode" class="form-control" />
<span asp-validation-for="personapplicantvm.NationalCode" class="text-danger"></span>
</div>
<div class="form-group person">
<label asp-for="personapplicantvm.Username" class="control-label"></label>
<input asp-for="personapplicantvm.Username" class="form-control" />
<span asp-validation-for="personapplicantvm.Username" class="text-danger"></span>
</div>
}
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back To List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
After running the project, the view is the same as before and all the fields will be visible. I appreciate if anyone suggest me a solution.
asp-for="applicantvm.ApplicantType" render the id and name in the html like id="applicantvm_ApplicantType" name="applicantvm.ApplicantType" ,you could use the developer tools to check the Source code.
So you should change your javascript as shown:
<script>
$(document).ready(function () {
$(".person").hide();
$(".legal").hide();
$('#applicantvm_ApplicantType').change(function () {
var value = $(this).val();
if (value == '1') {
$(".legal").show();
if ($(".person").show()) {
$(".person").hide();
}
} else {
$(".person").show();
if ($(".legal").show()) {
$(".legal").hide();
}
}
});
});
</script>
Result:
In Simple terms,
Write ONCHANGE Event in the drop down and invoke a JavaScript function.
From JavaScript, based on the drop down values, show and hide the controls.

ValidationSummary.ModelOnly fails but ValidationSummary.All works

This is strange. For some reason, this fails:
<div asp-validation-summary="ValidationSummary.ModelOnly"></div>
But this one works just fine:
<div asp-validation-summary="All"></div>
Any one would know why? Here is entire code snippet:
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h3>Login</h3>
<form method="post" novalidate>
<div asp-validation-summary="All"></div>
#*<div asp-validation-summary="ValidationSummary.ModelOnly"></div>*#
<div class="form-group">
<label asp-for="Username"></label>
<input type="text" asp-for="Username" class="form-control" />
<span asp-validation-for="Username"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input type="password" asp-for="Password" class="form-control" />
<span asp-validation-for="Password"></span>
</div>
<div class="form-group">
<input type="submit" value="Login" class="btn btn-success" />
</div>
</form>
</div>
I'm assuming you are using asp.net core 1.0. The value for the validation-summary tag helper was changed from
<div asp-validation-summary="ValidationSummary.ModelOnly"></div>
to simply
<div asp-validation-summary="ModelOnly"></div>
You did it correclty with All ;-)
asp.net core docs

layout is not included in all pages in asp.net mvc4 application

I am developing an asp.net mvc4 application with Bootstrap 3 and i've _Layout.cshtml in "Shared" folder , in Views, i've two pages, "Index and "Register" and i've included Layout in both of these Views but It seems that Layout is included in only "Index and not in "Register" View. Following are my Index and Register Views
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2 align="center" class="bg-info">Login</h2>
<form class="form-horizontal" role="form">
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label"><strong>UserName : </strong></label>
<div class="col-sm-10">
<input type="email" class="form-control" id="inputEmail3" placeholder="UserName">
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-label"><strong>Password</strong></label>
<div class="col-sm-10">
<input type="password" class="form-control" id="inputPassword3" placeholder="Password">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label>
<input type="checkbox"> Remember me
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">Sign in</button>
</div>
</div>
<h2 align="center" class="bg-info">SignIn With Other Services</h2>
</form>
<form class="form-horizontal" role="form" method="post" action="/Home/FacebookLogin">
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">SignIn with Facebook</button>
</div>
</div>
</form>
<h2 align="center" class="bg-info">Don't Have an Account?</h2>
<form class="form-horizontal" role="form" method="post" action="/Home/Register">
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">Register</button>
</div>
</div>
</form>
![#{
ViewBag.Title = "Register";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2 align="center" class="bg-info">Register</h2>
<form class="form-horizontal" role="form">
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label"><strong>UserName : </strong></label>
<div class="col-sm-10">
<input type="email" class="form-control" id="uname" name="uname" placeholder="UserName">
<input type="button" class="btn btn-primary" id="check" value="Check Availability" >
<h4 class="bg-warning"></h4>
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-label"><strong>Password</strong></label>
<div class="col-sm-10">
<input type="password" class="form-control" id="upass" name="upass" placeholder="Password">
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-label"><strong>Retype Password</strong></label>
<div class="col-sm-10">
<input type="password" class="form-control" id="retype" placeholder="Password">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">Register</button>
</div>
</div>
</form>]
Can you please check below answer
Section has been defined but not rendered for the layout page "~/Views/Shared/_Layout.cshtml": "head"
Also check for it