Displaying Error in a View - error-handling

Is there any standard practice to display errors in a view? Currently it is being displayed from TempData.
I implemented a derived a class from Base Controller and used that derived class in every one of my controller. Then assign error or success messages from controller.
public class TestController : Controller
{
public string ErrorMessage
{
get { return (string) TempData[CommonHelper.ErrorMessageKey]; }
set
{
if (TempData.ContainsKey(CommonHelper.ErrorMessageKey))
{
TempData[CommonHelper.ErrorMessageKey] = value;
}
else
{
TempData.Add(CommonHelper.ErrorMessageKey,value);
}
TempData.Remove(CommonHelper.SuccessMessageKey);
}
}
public string SuccessMessage
{
get { return (string)TempData[CommonHelper.SuccessMessageKey]; }
set
{
if(TempData.ContainsKey(CommonHelper.SuccessMessageKey))
{
TempData[CommonHelper.SuccessMessageKey] = value;
}
else
{
TempData.Add(CommonHelper.SuccessMessageKey, value);
}
TempData.Remove(CommonHelper.ErrorMessageKey);
}
}
}
CommonHelper Class
public class CommonHelper
{
public const string SuccessMessageKey = "successMessage";
public const string ErrorMessageKey = "errorMessage";
public static string GetSuccessMessage(object data)
{
return data == null ? string.Empty : (string) data;
}
public static string GetErrorMessage(object data)
{
return data == null ? string.Empty : (string) data;
}
}
Then created a partial view having this
#using Web.Helpers
#if (!string.IsNullOrEmpty(CommonHelper.GetSuccessMessage(TempData[CommonHelper.SuccessMessageKey])))
{
<div class="alert alert-success">
#CommonHelper.GetSuccessMessage(TempData[CommonHelper.SuccessMessageKey])
</div>
}
else if (!string.IsNullOrEmpty(CommonHelper.GetErrorMessage(TempData[CommonHelper.ErrorMessageKey])))
{
<div class="alert alert-success">
#CommonHelper.GetErrorMessage(TempData[CommonHelper.ErrorMessageKey])
</div>
}
And in every view, the partial view is rendered.
<div>
#Html.Partial("_Message")
</div>

Here is a pretty common implementation of displaying errors.
Controller
public class UserController : Controller
{
[HttpPost]
public ActionResult Create(User model)
{
// Example of manual validation
if(model.Username == "Admin")
{
ModelState.AddModelError("AdminError", "Sorry, username can't be admin")
}
if(!ModelState.IsValid()
{
return View(model)
}
}
}
Model
public class User
{
[Required]
public string Username {get; set;}
public string Name {get; set; }
}
View
#Html.ValidationSummary(true)
#using(Html.BeginForm())
{
// Form Html here
}
You don't need all of the infrastructure you created. This is handled by the framework. If you need a way to add success messages you can checkout the Nuget Package MVC FLASH

I prefer to use ModelState.AddModelError()

Related

How can I transfer a model to a Razor Component in Blazor server-side?

I am using Blazor server-side to make a chat room.
For the style of receiving the message and sending the message is different, I made a model named MsgModel
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BlazorApp1
{
public class MsgModel: ComponentBase
{
public string MsgText { get; set; }
}
}
The razor components ReceiveMsg.razor and the SendMsg.razor are base on this model.
#inherits MsgModel
<h3>ReceiveMsg</h3>#MsgText
#inherits MsgModel
<h3>SendMsg</h3>#MsgText
In the index.razor, I wanna input the message text and display it immediately.
#page "/"
#foreach (MsgModel _MsgModel in MsgList)
{
if (_MsgModel.GetType() == typeof(ReceiveMsg))
{
<ReceiveMsg></ReceiveMsg>
}
else
{
<SendMsg></SendMsg>
}
}
<div id="inputDiv">
<EditForm Model="_InputMsgModel" OnValidSubmit="#SubmitText">
<InputText #bind-Value="_InputMsgModel.MsgText" />
</EditForm>
</div>
#code{
protected MsgModel _InputMsgModel { get; set; } = new MsgModel();
protected List<MsgModel> MsgList { get; set; } = new List<MsgModel>();
protected void SubmitText()
{
SendMsg _SendMsg = new SendMsg();
_SendMsg.MsgText = _InputMsgModel.MsgText;
MsgList.Add(_SendMsg);
}
}
Now the problem is: in the for block, I should transfer the _MsgModel to the component. Meanwhile, I don't know how to transfer it yet.
Would you please help me? Thank you.
Finally, I found a strange and stupid way to solve this.
I add these code into the MsgModel:
[Parameter]
public MsgModel TransferModel
{
set
{
CopyAll(value, this);
}
}
private void CopyAll<T>(T source, T target)
{
var type = typeof(T);
foreach (var sourceProperty in type.GetProperties())
{
if (sourceProperty.Name != "TransferModel")
{
var targetProperty = type.GetProperty(sourceProperty.Name);
targetProperty.SetValue(target, sourceProperty.GetValue(source, null), null);
}
}
}
And change the for block like this:
#foreach (Models.MsgModel _MsgModel in MsgList)
{
if (_MsgModel.GetType() == typeof(ReceiveMsg))
{
<ReceiveMsg ShowFullImage="#ShowFullImage" TransferModel="_MsgModel"></ReceiveMsg>
}
else
{
<SendMsg ShowFullImage="#ShowFullImage" TransferModel="_MsgModel"></SendMsg>
}
}
What a stupid way it is!

razor pages core local methods

Is it OK to call a method in the #functions{} section in razor pages directly from HTML? This seems to work fine and it's much easier than calling an API, but I was wondering if there is a downside to this (security, performance, etc)?
For example, in the code...
#functions {
public class Tickets: PageModel
{
public ApplicationDbContext _db { get; set; }
public Tickets(ApplicationDbContext db)
{
_db = db;
}
public void OnGet()
{
}
public string GetTickets(int Top) //--> THIS IS THE METHOD I AM CALLING
{
var data = _db.Tickets.OrderByDescending(x => x.CreatedAt).Take(Top);
var jdata = JsonConvert.SerializeObject(data.ToList());
return jdata;
}
}
}
And the HTML...
<div class="card alert-warning" v-for="ticket in tickets">
<div class="card-body">
<h4 class="card-title">{{ticket.TicketSubject}}</h4>
Card link
Another link
</div>
</div>
#section Scripts {
<script>
new Vue({
el: '#app',
data: {
dismissSecs: 5,
dismissCountDown: 5,
tickets: #Html.Raw(Model.GetTickets(100)), //-->THIS WORKS, BUT IT IS OK TO USE LIKE THIS?
xx: ''
}
})
</script>
}
It works because I believe the method is called when the page is being rendered and you have constant value being passed in. It would not automatically do something like update client-side without AJAX code being involved.
But to answer you question I think the 'more correct' approach is to set a binding property in the OnGet and reference that client-side
public class Tickets : PageModel
{
public ApplicationDbContext _db { get; set; }
public Tickets(ApplicationDbContext db)
{
_db = db;
}
[BindProperty]
public string TicketData { get; set; }
public void OnGet()
{
TicketData = GetTickets(100);
}
public string GetTickets(int Top)
{
var data = _db.Tickets.OrderByDescending(x => x.CreatedAt).Take(Top);
var jdata = JsonConvert.SerializeObject(data.ToList());
return jdata;
}
}

ASP.NET Core tag helper for conditionally adding a class to an element

In Asp.Net MVC we can add class conditionally as following code:
<div class="choice #(Model.Active?"active":"")">
</div>
How can do this by using tagHelper and by remove else part in condition.
Ability to add a conditional css class by following tagHelper provides.
this code like AnchorTagHelper asp-route-* for add route values acts.
[HtmlTargetElement("div", Attributes = ClassPrefix + "*")]
public class ConditionClassTagHelper : TagHelper
{
private const string ClassPrefix = "condition-class-";
[HtmlAttributeName("class")]
public string CssClass { get; set; }
private IDictionary<string, bool> _classValues;
[HtmlAttributeName("", DictionaryAttributePrefix = ClassPrefix)]
public IDictionary<string, bool> ClassValues
{
get {
return _classValues ?? (_classValues =
new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase));
}
set{ _classValues = value; }
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var items = _classValues.Where(e => e.Value).Select(e=>e.Key).ToList();
if (!string.IsNullOrEmpty(CssClass))
{
items.Insert(0, CssClass);
}
if (items.Any())
{
var classes = string.Join(" ", items.ToArray());
output.Attributes.Add("class", classes);
}
}
}
in _ViewImports.cshtml add reference to taghelper as following
#addTagHelper "*, WebApplication3"
Use tagHelper in View:
<div condition-class-active="Model.Active" condition-class-show="Model.Display">
</div>
result for Active = true and Display = true is:
<div class="active show">
</div>
There's no default way to do what you're asking. You would have to write a TagHelper that did that logic for you. Aka
[HtmlTargetElement(Attributes = "asp-active")]
public class FooTagHelper : TagHelper
{
[HtmlAttributeName("asp-active")]
public bool Active { get; set; }
public override void Process(TagHelperOutput output, TagHelperContext context)
{
if (Active)
{
// Merge your active class attribute onto "output"'s attributes.
}
}
}
And then the HTML would look like:
<div class="choice" asp-active="Model.Active"></div>

NInject kernel GetAll returns empty

I've two projects (class library projects) which implement one interface:
The first one:
public class MailPlugin : Extensibility.IProductorPlugin
{
...
}
The second one:
public class FileSystemPlugin : Extensibility.IProductorPlugin
{
...
}
Extensibility.IProductorPlugin, is a interface of a third project:
namespace Extensibility
{
public delegate void NotifyDigitalInputs(List<Domain.DigitalInput> digital_inputs);
public interface IProductorPlugin
{
String Name { get; }
String Description { get; }
String Version { get; }
List<Domain.Channel> AvailableChannels { get; }
IEnumerable<Guid> TypeGuids { get; }
event NotifyDigitalInputs OnDigitalInputs;
}
}
In my composition root, I've created this class:
namespace UI
{
public sealed class NinjectServiceLocator
{
private static readonly Lazy<NinjectServiceLocator> lazy = new Lazy<NinjectServiceLocator>(() => new NinjectServiceLocator());
public static NinjectServiceLocator Instance { get { return lazy.Value; } }
public Ninject.IKernel Kernel { get; private set; }
private NinjectServiceLocator()
{
using (var k = this.Kernel = new Ninject.StandardKernel())
{
k.Bind(b => b.FromAssembliesMatching("*")
.SelectAllClasses()
.InheritedFrom(typeof(Extensibility.IProductorPlugin))
.BindAllInterfaces()
);
}
}
}
}
So, when I want to look for all plugins, I just perform this:
protected void initialize()
{
foreach (Extensibility.IProductorPlugin productor_plugin in NinjectServiceLocator.Instance.Kernel.GetAll(typeof(Extensibility.IProductorPlugin)))
{
using (var channel_tile = new DevExpress.XtraBars.Docking2010.Views.WindowsUI.Tile() { Group = "Plugin Channels" })
{
foreach (Domain.Channel channel in productor_plugin.AvailableChannels)
{
channel_tile.Elements.Add(new DevExpress.XtraEditors.TileItemElement() { Text = channel.Name });
channel_tile.Elements.Add(new DevExpress.XtraEditors.TileItemElement() { Text = channel.Description });
this.tileContainer1.Items.Add(channel_tile);
}
}
}
}
However, GetAll returns anything.
What am I doing wrong?
I'll appreciate a lot your help.
Thanks for all.
try removing the using() from around the Kernel instantiation. a using will dispose the object at the end of the scope, which we don't want for a kernel.
using (var k = this.Kernel = new Ninject.StandardKernel())

mvc4 PartialView solution is needed

im building a site using MVC4 and i want to display a navigation bar at the top of my _ViewStart according to my Database.
How can i do so? can i use a contoroller ActionResult that fired once the index page is loaded?
or how can i triger it by a partial view
my current ActionResult returning partial view is:
public ActionResult NavigationBar()
{
var entities = new CakesDBEntities();
var articles = entities.Articles;
List<NavBarModel> navBarList = articles.Select(nb => new NavBarModel { Title = nb.title, Url = nb.url }).ToList();
return View(navBarList);
}
my model:
namespace SimplyCakes20131009.Models
{
public class NavBarModel
{
public string Title { get; set; }
public string Url { get; set; }
}
}
my partial view:
#model IEnumerable<SimplyCakes20131009.Models.NavBarModel>
#foreach (var bar in Model)
{
<li>
#Html.ActionLink(bar.Title, bar.Url)
</li>
}
How can i integrate the nav bar to my _ViewStart?
A better option would be to use the _Layout.cshtml. _ViewStart is just calls the _Layout.cshtml.
You probably don't need partial View here. You can use a Child Action that renders PartialView results.
In your
_Layout.cshtml :
You can have
#{ Html.RenderAction("Navigation", "Home"); }
This points to the HomeController and Navigation Action
Additional Note: Html.RenderAction better because it is much faster than the Html.Action.
It can handle large amount of HTML efficiently as it will directly send the result to the Response. Html.Action just returns a strings with the result.
Navigation Action has its Navigation View which is pretty much equivalent to what you had in your view.
Home/Navigation.cshtml :
#model IEnumerable<MvcApplication1.Controllers.NavViewModel>
#foreach (var nav in Model)
{
<li>#Html.ActionLink(nav.Title, nav.Url)</li>
}
HomeController.cs :
Note that you probably inject the DB access as dependency to support the testability.
public class HomeController : Controller
{
private readonly ICakesRepository _cakesRepository;
//additional constructor to support testability.
public HomeController(ICakesRepository cakesRepository) {
_cakesRepository = cakesRepository;
}
//this can be removed if you the above with IOC/DI wire-up
public HomeController() {
_cakesRepository = new CakesRepository();
}
[ChildActionOnly]
[HttpGet]
public ActionResult Navigation() {
var articles = _cakesRepository.GetArticles();
var navBarList = articles.Select(nb => new NavViewModel { Title = nb.Title, Url = nb.Url });
return PartialView(navBarList);
}
}
Additional supporting classes :
public class NavViewModel {
public string Title { get; set; }
public string Url { get; set; }
}
public interface ICakesRepository {
IEnumerable<Articles> GetArticles();
}
public class CakesRepository : ICakesRepository {
public IEnumerable<Articles> GetArticles() {
//call to a db
//fake db data
return new List<Articles>() {
new Articles(){Title = "Title1", Url = "http://urlone.com"},
new Articles(){Title = "Title2", Url = "http://urltwo.com"},
new Articles(){Title = "Title3", Url = "http://urlthree.com"}
};
}
}
public class Articles {
public string Title { get; set; }
public string Url { get; set; }
}