How can I read an uploaded file using MVC3? - file-upload

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Excel;
using System.Data;
namespace QuimizaReportes.Controllers
{
public class UploadController : Controller
{
public ActionResult Index()
{
//stream is supposed to be the excel file object.
IExcelDataReader excelReader = ExcelReaderFactory.CreateBinaryReader(stream);
excelReader.IsFirstRowAsColumnNames = true;
DataSet result = excelReader.AsDataSet();
while (excelReader.Read())
{
}
excelReader.Close();
return View();
}
}
}
I'm supposed to let users upload the file and read from it, then display a confirmation message that it has been saved. The question is: How can I 'get' that stream? Any suggestions?

Would this do the trick?
[HttpPost]
public ActionResult Index(HttpPostedFileBase excelFile)
{
IExcelDataReader excelReader = ExcelReaderFactory.CreateBinaryReader(excelFile.InputStream);
//Blah
}
In conjunction with something like:
<form action="/MyController/Index" enctype="multipart/form-data" method="post">
<!-- blah -->
<input type="file" id="excelFile" name="excelFile" />
<!-- blah -->
</form>

Related

Problem with embed RCL in asp.net core with form

I made an RCL(Razor Class Library) for reusable UI.
Here is the code of front-end:
#model Sample.Models.VistorModel
#using Microsoft.AspNetCore.Mvc.Localization
#inject IViewLocalizer Localizer
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<form id="ContactForm" asp-action="Contact">
<div>
<h3>#Localizer["Name"]</h3>
<span asp-validation-for="Name"></span>
</div>
<input asp-for="Name" placeholder="#Localizer["NameHint"]" />
<div>
<h3>#Localizer["Phone"]</h3>
<span asp-validation-for="Phone"></span>
</div>
<input asp-for="Phone" placeholder="#Localizer["PhoneHint"]" type="tel" />
<button type="submit">#Localizer["Sumit"]</button>
</form>
Here is the model:
public class VistorModel
{
[Required(ErrorMessageResourceName = "NameError", ErrorMessageResourceType = typeof(Sample.Resources.Views.Contact.Contact))]
public string Name { get; set; }
[Required(ErrorMessageResourceName = "PhoneError", ErrorMessageResourceType = typeof(Sample.Resources.Views.Contact.Contact))]
[RegularExpression(#"((\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$)", ErrorMessageResourceName = "NotAPhoneNumber", ErrorMessageResourceType = typeof(Sample.Resources.Views.Contact.Contact))]
public string Phone { get; set; }
}
And here is the controller:
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sample.Controllers
{
[Route("{culture}/[controller]")]
public class ContactController : Controller
{
[Route("Contact.html")]
public IActionResult Contact()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
[Route("Contact.html")]
public IActionResult Contact(Models.VistorModel Vistor)
{
if (ModelState.IsValid)
{
}
return View();
}
}
}
For example,
the RCL URL is www.sample.com/contact/contact.html
the page URL is www.sample.com/home/index.html(the page is made by asp.net core also)
Now I embedded the RCL into the page.
However, when I click the submit button on the page, the page will redirect to the www.sample.com/contact/contact.html but not www.sample.com/home/index.html.
Besides, I need to embed this RCL not only on the home page but also on any other page.
In a word, what I want to do is:
1.When I input nothing(invalid), the page will redirect to www.sample.com/home/index.html and show the invalid error message on the span which has set attribute asp-validation-for before.
2.When I input name and phone(valid), the page will redirect to www.sample.com/home/index.html and show a message box alert that submits success.
Now I don't know how to make the URL redirect and display the invalid error message correctly. How can I achieve it? Thank you.
As far as I know, if you have set the asp-action, it will generate the form action with /contact/contact not home index.
To solve this issue, you should firstly move the RCL's view to the shared folder and then modify the form tag as below:
<form id="ContactForm" method="post">

Programmatically generating request verification token

Starting with an empty MVC project, here's my Startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace AntiForgeryExample
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
}
}
And here's my HomeController.cs:
using Microsoft.AspNetCore.Mvc;
using System.Net;
namespace AntiForgeryExample
{
public class XyzController : Controller
{
[HttpPost]
[ValidateAntiForgeryToken]
public string Fgh() => "fgh 1";
[HttpGet]
public ContentResult Efg()
{
return new ContentResult()
{
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
Content = #"<!DOCTYPE html>
<html>
<body>
<form method=""post"" action=""/Xyz/Fgh"">
<button type=""submit"">123</Button>
</form>
</body>
</html>"
};
}
}
}
The following line in Startup.cs adds the anti-forgery middleware:
services.AddMvc();
So, if we go to http://localhost:52838/Xyz/Efg, we get the simple page with a single button:
If we press the button, we get a 400 "Bad Request" response:
I'm assuming this is because we haven't passed a valid request verification token as part of the post. The Fgh method has the ValidateAntiForgeryToken attribute applied:
[HttpPost]
[ValidateAntiForgeryToken]
public string Fgh() => "fgh 1";
Thus a token is required when calling this method.
As described on this page, normally the code for this token will automatically be included if you use the form tag helper with ASP.NET Core MVC or a Razor Page. It will look something like this and be included as part of the form tag:
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">
In the example program I show above, we're programmatically generating the HTML with the form.
My question is, is there a way to programmatically generate the required token from C#, without having to go through using an MVC view or Razor Page. The idea would be that we'd get the token value and then include the input tag:
<input name="__RequestVerificationToken" type="hidden" value="TOKEN VALUE HERE">
I shared this question on the /r/dotnet subreddit.
/u/kenos1 provided a very helpful answer there:
You can inject Microsoft.AspNetCore.Antiforgery.IAntiforgery and call GetTokens() on it.
Here’s the documentation: link
As he mentions there, we inject IAntiforgery at the XyzController constructor:
private IAntiforgery antiforgery;
public XyzController(IAntiforgery antiforgery_)
{
antiforgery = antiforgery_;
}
We call GetAndStoreTokens on the IAntiforgery instance that we injected:
var token_set = antiforgery.GetAndStoreTokens(HttpContext);
And finally, we use the resulting token in the generated HTML:
return new ContentResult()
{
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
Content = string.Format(#"<!DOCTYPE html>
<html>
<body>
<form method=""post"" action=""/Xyz/Fgh"">
<button type=""submit"">123</Button>
<input name=""__RequestVerificationToken"" type=""hidden"" value=""{0}"">
</form>
</body>
</html>",
token_set.RequestToken)
};
Here is the controller file in its entirety:
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System.Net;
namespace AntiForgeryExample
{
public class XyzController : Controller
{
private IAntiforgery antiforgery;
public XyzController(IAntiforgery antiforgery_)
{
antiforgery = antiforgery_;
}
[HttpPost]
[ValidateAntiForgeryToken]
public string Fgh() => "fgh 1";
[HttpGet]
public ContentResult Efg()
{
var token_set = antiforgery.GetAndStoreTokens(HttpContext);
return new ContentResult()
{
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
Content = string.Format(#"<!DOCTYPE html>
<html>
<body>
<form method=""post"" action=""/Xyz/Fgh"">
<button type=""submit"">123</Button>
<input name=""__RequestVerificationToken"" type=""hidden"" value=""{0}"">
</form>
</body>
</html>",
token_set.RequestToken)
};
}
}
}
The official ASP.NET Core 3.1 documentation mentions the GetAndStoreTokens method here.

Returning Different Data To Various View Sections

I have a "Most Popular" div currently displaying the most popular auction items and an "Ending Soon" div that I want to display those auctions ending soon. I have successfully queried the database for the "Most Popluar" and returned those results to the page. And I know what the query is for those ending soon, but how do I return both sets of data to the page for the partial view "_AuctionTile" to use?
The following update reflects the suggestions made by Vinutha N --
The View
#model IEnumerable<MyAuctionApp.Models.Auction>
#{
ViewBag.Title = "Home Page";
}
<div class="row">
<div id="popular" class="col-md-4 col-lg-4">
<h2>Most Popular Items</h2>
#foreach (var item in Model.mostPopularItems)
{
#Html.Partial("_AuctionTile", item)
}
</div>
<div id="ending" class="col-md-4 col-lg-4">
<h2>Auctions Ending Soon</h2>
#foreach (var item in Model)
{
#Html.Partial("_AuctionTile", item)
}
</div>
The ViewModel
using MyAuctionApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MyAuctionApp.ViewModels
{
public class AuctionViewModel
{
public static List<Auction> mostPopularItems;
public static List<Auction> endingSoon;
public AuctionViewModel(AuctionsDataContext db)
{
mostPopularItems = db.Auctions.Where(x => x.EndTime >
DateTime.Today).OrderByDescending(x => x.viewCount).ToList();
endingSoon = db.Auctions.Where(x => x.EndTime >
DateTime.Today).OrderByDescending(x => x.EndTime).ToList();
}
}
}
The Controller
using MyAuctionApp.Models;
using MyAuctionApp.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MyAuctionApp.Controllers
{
[AllowAnonymous]
public class HomeController : Controller
{
[AllowAnonymous]
public ActionResult Index()
{
var db = new AuctionsDataContext();
var dataModuleObject = new AuctionViewModel(db);
return View(dataModuleObject);
//var auctions = db.Auctions.ToArray();
//var auctions = db.Auctions.Where(x => x.EndTime >
//DateTime.Today).OrderByDescending(x => x.viewCount);
//return View(auctions);
}
}
As you can see from the attached screenshot however, the 'mostPopularItems'
property that was initialized in the AuctionViewModel, is not being picked
up by Intellisense as existing, in the line
#foreach (var item in Model.mostPopularItems)
What do I still not have quite right?
Thanks,
CM
Use child actions. First, create actions like the following on your AuctionController:
[ChildActionOnly]
public ActionResult MostPopular()
{
// get most popular auctions
return PartialView("_AuctionTile", auctions);
}
[ChildActionOnly]
public ActionResult EndingSoon()
{
// get auctions ending soon
return PartialView("_AuctionTile", auctions);
}
Then, in your view, where you want each to display:
#Html.Action("MostPopular")
#Html.Action("EndingSoon")
Your main view's model should only focus on what it needs to do, not asides like these.
UPDATE
Sorry, I wasn't paying attention to your HTML. Basically, you would need to create a partial for each section, then. For example:
_MostPopular.cshtml
#model IEnumerable<Auction>
<div id="popular" class="col-md-4 col-lg-4">
<h2>Most Popular Items</h2>
#foreach (var item in Model)
{
#Html.Partial("_AuctionTile", item)
}
</div>
Then, change the child action to return this instead:
return PartialView("_MostPopular", auctions);
The key here is that the child action creates a separate context, where you can build a view model specifically for this partial view, without affecting what's going on in the main view.
BONUS
Display templates can help a lot here too. For example, if you could create a view like Views\Shared\DisplayTemplates\Auction.cshtml. Then, any time you call Html.DisplayFor with an instance of Auction, it would automatically use that view to render it. Additionally, Html.DisplayFor can handle enumerables as well. It will simply use the display template for the contained type to render each item in the list. So in your code, instead of iterating over the items in Model and rendering a partial view for each, you could then simply do:
#model IEnumerable<Auction>
<div id="popular" class="col-md-4 col-lg-4">
<h2>Most Popular Items</h2>
#Html.DisplayForModel()
</div>
Firstly you need to declare 2 list they are,
list Popularitem and list endingSoon in view model which you are using(if not create and use its a best practice. because u cannot do in the controller)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace DataViewModel.ViewModels
{
public class DataViewModel
{
public list<Auctions> Popularitem { get; set; }
public list<Auctions> endingSoon { get; set; }
public CustomerViewModel((datatype)db)
{
list<Auctions> Popularitem=db.Auctions.Where(x => x.EndTime > DateTime.Today).OrderByDescending(x => x.viewCount);
list<Auctions> endingSoon =db.Auctions.Where(x => x.EndTime > DateTime.Today).OrderByDescending(x => x.viewCount);
}
}
}
in controller
public ActionResult Index()
{
var db = new AuctionsDataContext();
datamoduleobject= new Dataviewmodel(db);
return View(datamoduleobject);
}
in view :
#MyProject.Web.ViewModels.DataViewModel
<div id="popular" class="col-md-4 col-lg-4">
<h2>Most Popular Items</h2>
#foreach (var item in Model.Popularitem)
{
#Html.Partial("_AuctionTile", item)
}
</div>
<div id="ending" class="col-md-4 col-lg-4">
<h2>Auctions Ending Soon</h2>
#foreach (var item in Model.endingSoon)
{
#Html.Partial("_AuctionTile", item)
}
</div>
and please go through the link i have provided which help u to understand http://tutlane.com/tutorial/aspnet-mvc/how-to-use-viewmodel-in-asp-net-mvc-with-example
In your model declare 2 list and assign the data to lists. In html page use the specific list property in foreach.
Like,
model.Popularitems
Model.Endingitems
Either create view model as suggested above or you can pass extra data in view bag as in controller you can set
ViewBag.endingSoon= yourCollection
and use it in your view
hope It'll help

MVC Web App Error HTTP 404

Im doing an MVC tutorial and Im in the part of doing a really basic web app.
This is the Controller inside my Controller folder:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace FirstMVCApplication.Controllers
{
public class HomeContoller : Controller
{
//
// GET: /HomeContoller/
public ActionResult Index()
{
int hour = DateTime.Now.Hour;
ViewBag.Greeting =
hour < 12
? "Good Morning. Time is" + DateTime.Now.ToShortTimeString()
: "Good Afternoon. Time is " + DateTime.Now.ToShortTimeString();
return View();
}
}
}
And this is my View inside View Folder:
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
#ViewBag.Greeting (<b>From Index View</b>)
</div>
</body>
</html>
Im using Razor.
And when I execute it It returns an HTTP 404 resource not found. It is an Empty Web MVC-4 Application.
EDIT
RouteConfig
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace FirstMVCApplication
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}
Delete this #{ Layout = null; } from the Layout.cshtml file you posted here
Add #RenderBody() to Layout.cshtml file you posted here
In the Index.cshtml file which you do have here. Add at the top of the page file
#{ Layout = "~/Views/Shared/Layout.cshtml"; }

Using Ajax.BeginForm with MVC 4 - adding to my model collection asynchronously isn't working

I am trying to make a small football site where the user can create a new team and then asynchronously in another div it shows all the teams the user has created. So basically a team is created then added to the list of teams. All of this is in the model.
Now, I would like to do this asynchronously because its a nice to have but it's not working in my code. I am either missing something or it's not possible with what I am doing.
Controller
public ActionResult TeamManagement()
{
modelTeamSelect modelTeamSelect = new modelTeamSelect();
return View(modelTeamSelect);
}
[HttpPost]
public ActionResult TeamManagement(string btnSubmit, modelTeamSelect modelTeamSelect)
{
switch (btnSubmit)
{
case "Add Team":
// For now - add to collection but not use DAL
modelTeamSelect.teams.Add(modelTeamSelect.team);
//modelTeamSelect.team.TeamName = string.Empty;
break;
}
return View(modelTeamSelect);
}
View
#model Website.Models.modelTeamSelect
#{
ViewBag.Title = "Football App";
}
#section featured {
}
#using (Ajax.BeginForm(new AjaxOptions
{
HttpMethod = "POST",
Url = "Home/TeamManagement",
OnComplete = "teamAdded()"
}))
{
<div id="divTeams" style="float:left">
<h3>Create a new team:</h3>
#Html.LabelFor(m => m.team.TeamName)
#Html.TextBoxFor(m => m.team.TeamName)
<input type="submit" value="Add Team" name="btnSubmit" />
</div>
<div id="divCreatedTeams" style="float:left">
<h3>Your created teams:</h3>
#if (Model.teams.Count > 0)
{
for (int i = 0; i < Model.teams.Count; i++)
{
#Html.TextBoxFor(m => m.teams[i].TeamName)
}
}
</div>
<div id="divLeagues">
</div>
}
Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Website.Models
{
public class modelTeamSelect
{
public modelTeamSelect()
{
teams = new List<modelTeam>();
team = new modelTeam();
}
public List<modelTeam> teams { get; set; }
public modelTeam team { get; set; }
}
}
I have the right javascript references being used in the project as I recently fixed this.
Why isn't my UI changing to reflect new contents of list?
I dont get the idea of passing the submit button string to the Action. But in order to pass a ViewModel to the Action I think you have to write your own model binder. If you want you can try getting the models seperately in the action and combining them in the Action
public ActionResult TeamManagement(List<modelTeam> teams, modelTeam team)
and combine them in the action in the viewModel.
Just a sugestion If you want to retrieve them async with ajax what I do is return partial view (i think better in your case) or json