Validating Uploaded Files Using IValidatableObject - image

Hi All!
I'm a bit of a noob at model validation and i've been trying to validate an Articles object and an uploaded file using the IValidatableObject interface with no success.
This following class validates the Articles object just fine but I can't see how the HttpPostedFileBase is injected to allow me to validate against it. Is this even possible to achieve using this method?
The form i'm using to submit the data includes the enctype = multipart/form-data attribute so it knows its posting files.
This is the full class im trying to validate. This ones really got me stuck and any help will be very gratefully appreciated.
public class ArticlesModel : IValidatableObject
{
public Article Article { get; set; }
public IEnumerable<Category> Categories { get; set; }
public HttpPostedFileBase PostedFile { get; set; }
public ArticlesModel(){}
public ArticlesModel(Article article, IEnumerable<Category> categories)
{
this.Article = article;
this.Categories = categories;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Article.CategoryID == 0)
{
yield return new ValidationResult("Please select a category.", new[] { "Article.Category"});
}
if (Article.Title == null)
{
yield return new ValidationResult("Please enter a title.", new[] { "Article.Title" });
}
if (Article.Content == null)
{
yield return new ValidationResult("Please enter some content.", new[] { "Article.Content" });
}
if (PostedFile == null)
{
yield return new ValidationResult("Please upload a file.", new[] { "Article.ImageFile" });
}
else
{
if (PostedFile.ContentLength > 1 * 1024 * 1024)
{
yield return new ValidationResult("Please upload a file 1Mb or less.", new[] { "Article.ImageFile" });
}
//Other file checking logic here please!!
}
}
}

Related

Does the IClientValidator support input file?

Edit
I found that the problem is that View Components are unable to have an #section (see ViewComponent and #Section #2910 ) so adding custom client-side validation using the unobtrusive library seems imposible (or very complex). Moreover, the inability of including the required javascript into a View Component makes me regret of following this approach to modularize my app in the first place...
I am learning to make custom validation attributes with client-side support. I was able to implement a custom validator for a string property and it works pretty well, but when I tried to make one for input file it doesn't work (i.e. when I select a file in my computer, the application doesn't display the validation messages. The server-side validation works. Here is some code that shows my implementation.
The class of the model
public class UploadPanelModel
{
public int? ID { get; set; }
public string Title { get; set; }
public string Description { get; set; } //Raw HTML with the panel description
[FileType(type: "application/pdf")]
[FileSize(maxSize: 5000000)]
public IFormFile File { get; set; }
public byte[] FileBytes { get; set; }
public ModalModel Modal { get; set; } //Only used if the Upload panel uses a modal.
The validator
public class FileSizeAttribute : ValidationAttribute, IClientModelValidator
{
private long _MaxSize { get; set; }
public FileSizeAttribute (long maxSize)
{
_MaxSize = maxSize;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
UploadPanelModel panel = (UploadPanelModel)validationContext.ObjectInstance;
return (panel.File==null || panel.File.Length <= _MaxSize) ? ValidationResult.Success : new ValidationResult(GetFileSizeErrorMessage(_MaxSize));
}
private string GetFileSizeErrorMessage(long maxSize)
{
double megabytes = maxSize / 1000000.0;
return $"El archivo debe pesar menos de {megabytes}MB";
}
public void AddValidation(ClientModelValidationContext context)
{
if(context == null)
{
throw new ArgumentNullException(nameof(context));
}
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-filesize", GetFileSizeErrorMessage(_MaxSize));
var maxSize = _MaxSize.ToString();
MergeAttribute(context.Attributes, "data-val-filesize-maxsize", maxSize);
}
private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
The javascript in the Razor View
#section Scripts{
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
$.validator.addMethod('filesize',
function (value, element, params) {
var size = $((params[0]).val()).size(),
maxSize = params[1];
if (size < maxSize) {
return false;
}
else {
return false;
}
}
);
$.validator.unobtrusive.adapters.add('filesize',
['maxSize'],
function (options) {
var element = $(options.form).find('input#File')[0];
options.rules['filesize'] = [element, options.params['maxSize']];
options.messages['filesize'] = options.message;
}
);
</script>
I always return false in the javascript method to force the application to show the validation error regardless the chosen file, but it still doesn't work.
Your addMethod() function will be throwing an error because params[0] is not a jQuery object and has no .val() (you also have the $ in the wrong place). You would need to use
var size = params[0].files[0].size;
However I suggest you write you scripts as
$.validator.unobtrusive.adapters.add('filesize', ['maxsize'], function (options) {
options.rules['filesize'] = { maxsize: options.params.maxsize };
if (options.message) {
options.messages['filesize'] = options.message;
}
});
$.validator.addMethod("filesize", function (value, element, param) {
if (value === "") {
return true;
}
var maxsize = parseInt(param.maxsize);
if (element.files != undefined && element.files[0] != undefined && element.files[0].size != undefined) {
var filesize = parseInt(element.files[0].size);
return filesize <= maxsize ;
}
return true; // in case browser does not support HTML5 file API
});

How could I read one to many linked table using link with Entity Framework?

I am new to Entity Framework and Linq (Visual Studio 2017 - EF 5.0) . Currently, I could read tables without issue but wonder how could I read a linked table.
My current functions do it but sure there is a simple way than two step reading that I have developed.
public override List<CartItem> GetMyCartOrderItems(int UserID)
{
try
{
using (foodorderingdbEntities oMConnection = new foodorderingdbEntities())
{
var oCart = oMConnection.carts.SingleOrDefault(p => p.USER_ID == UserID);
if (oCartItems != null)
{
int CartID = oCart.CART_ID;
var oCartItems = oMConnection.cart_item.Where(p => p.CART_ITEM_CART_ID == CartID);
if (oCartItems != null)
{
List<CartItem> oRecList = new List<CartItem>();
foreach (cart_item oDBrec in oCartItems)
{
CartItem oRec = new CartItem();
oRec.CartID = oDBrec.CART_ITEM_ID;
oRec.CartItemID = oDBrec.CART_ITEM_CART_ID;
oRec.DateTime = oDBrec.CART_ITEM_ADDED_DATE_TIME;
oRec.SystemComments = oDBrec.CART_ITEM_SYSTEM_COMMENTS;
oRecList.Add(oRec);
}
return oRecList;
}
else { return null; }
}
else { return null; }
}
}
catch (Exception ex)
{
//IBLogger.Write(LOG_OPTION.ERROR, "File : MHCMySQLDataConection.cs, Method : GetPatientByID(1), Exception Occured :" + ex.Message + Environment.NewLine + "Trace :" + ex.StackTrace);
return null;
}
}
You could see that I get Cart ID from Carts table using UserID and then I use the CartID retrieve cart Items from Cart_Item table. Cart_Item_Cart_ID is a foreign key in cart_item table. (This is a one to many table)
This is what I am thinking but obviously does not work.
List<cart_item> oCartItems = oMConnection.carts.SingleOrDefault(c => c.USER_ID == UserID).cart_item.Where(p => p.CART_ITEM_CART_ID = c.CART_ID).ToList<cart_item>();
Any help ?
My entity relation
public partial class cart
{
public cart()
{
this.cart_item = new HashSet<cart_item>();
}
public int CART_ID { get; set; }
public int USER_ID { get; set; }
public decimal ORDER_TOTAL_COST { get; set; }
public virtual ICollection<cart_item> cart_item { get; set; }
public virtual user user { get; set; }
}
Because your query has multiple levels of one to many relationships, and you just want the cart_items, it's easier to go the other way like this:
var oCart = oMConnection.cart_item
.Where(c=>c.cart.user.USER_ID == UserID);
Going the way you did should have worked as well, but you needed to use SelectMany instead of select like this:
var oCartItems = oMConnection.carts
.Where(c=>c.USER_ID==UserID)
.SelectMany(c=>c.cart_item);

Validate Model in Pipeline Instead of Controller [duplicate]

I was wondering how I can achieve model validation with ASP.NET Web API. I have my model like so:
public class Enquiry
{
[Key]
public int EnquiryId { get; set; }
[Required]
public DateTime EnquiryDate { get; set; }
[Required]
public string CustomerAccountNumber { get; set; }
[Required]
public string ContactName { get; set; }
}
I then have a Post action in my API Controller:
public void Post(Enquiry enquiry)
{
enquiry.EnquiryDate = DateTime.Now;
context.DaybookEnquiries.Add(enquiry);
context.SaveChanges();
}
How do I add if(ModelState.IsValid) and then handle the error message to pass down to the user?
For separation of concern, I would suggest you use action filter for model validation, so you don't need to care much how to do validation in your api controller:
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace System.Web.Http.Filters
{
public class ValidationActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var modelState = actionContext.ModelState;
if (!modelState.IsValid)
actionContext.Response = actionContext.Request
.CreateErrorResponse(HttpStatusCode.BadRequest, modelState);
}
}
}
Maybe not what you were looking for, but perhaps nice for someone to know:
If you are using .net Web Api 2 you could just do the following:
if (!ModelState.IsValid)
return BadRequest();
Depending on the model errors, you get this result:
{
Message: "The request is invalid."
ModelState: {
model.PropertyA: [
"The PropertyA field is required."
],
model.PropertyB: [
"The PropertyB field is required."
]
}
}
Like this, for example:
public HttpResponseMessage Post(Person person)
{
if (ModelState.IsValid)
{
PersonDB.Add(person);
return Request.CreateResponse(HttpStatusCode.Created, person);
}
else
{
// the code below should probably be refactored into a GetModelErrors
// method on your BaseApiController or something like that
var errors = new List<string>();
foreach (var state in ModelState)
{
foreach (var error in state.Value.Errors)
{
errors.Add(error.ErrorMessage);
}
}
return Request.CreateResponse(HttpStatusCode.Forbidden, errors);
}
}
This will return a response like this (assuming JSON, but same basic principle for XML):
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
(some headers removed here)
["A value is required.","The field First is required.","Some custom errorm essage."]
You can of course construct your error object/list any way you like, for example adding field names, field id's etc.
Even if it's a "one way" Ajax call like a POST of a new entity, you should still return something to the caller - something that indicates whether or not the request was successful. Imagine a site where your user will add some info about themselves via an AJAX POST request. What if the information they have tried to entered isn't valid - how will they know if their Save action was successful or not?
The best way to do this is using Good Old HTTP Status Codes like 200 OK and so on. That way your JavaScript can properly handle failures using the correct callbacks (error, success etc).
Here's a nice tutorial on a more advanced version of this method, using an ActionFilter and jQuery: http://asp.net/web-api/videos/getting-started/custom-validation
Or, if you are looking for simple collection of errors for your apps.. here is my implementation of this:
public override void OnActionExecuting(HttpActionContext actionContext)
{
var modelState = actionContext.ModelState;
if (!modelState.IsValid)
{
var errors = new List<string>();
foreach (var state in modelState)
{
foreach (var error in state.Value.Errors)
{
errors.Add(error.ErrorMessage);
}
}
var response = new { errors = errors };
actionContext.Response = actionContext.Request
.CreateResponse(HttpStatusCode.BadRequest, response, JsonMediaTypeFormatter.DefaultMediaType);
}
}
Error Message Response will look like:
{
"errors": [
"Please enter a valid phone number (7+ more digits)",
"Please enter a valid e-mail address"
]
}
You can use attributes from the System.ComponentModel.DataAnnotations namespace to set validation rules. Refer Model Validation - By Mike Wasson for details.
Also refer video ASP.NET Web API, Part 5: Custom Validation - Jon Galloway
Other References
Take a Walk on the Client Side with WebAPI and WebForms
How ASP.NET Web API binds HTTP messages to domain models, and how to work with media formats in Web API.
Dominick Baier - Securing ASP.NET Web APIs
Hooking AngularJS validation to ASP.NET Web API Validation
Displaying ModelState Errors with AngularJS in ASP.NET MVC
How to render errors to client? AngularJS/WebApi ModelState
Dependency-Injected Validation in Web API
Add below code in startup.cs file
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = (context) =>
{
var errors = context.ModelState.Values.SelectMany(x => x.Errors.Select(p => new ErrorModel()
{
ErrorCode = ((int)HttpStatusCode.BadRequest).ToString(CultureInfo.CurrentCulture),
ErrorMessage = p.ErrorMessage,
ServerErrorMessage = string.Empty
})).ToList();
var result = new BaseResponse
{
Error = errors,
ResponseCode = (int)HttpStatusCode.BadRequest,
ResponseMessage = ResponseMessageConstants.VALIDATIONFAIL,
};
return new BadRequestObjectResult(result);
};
});
C#
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ModelState.IsValid == false)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
...
[ValidateModel]
public HttpResponseMessage Post([FromBody]AnyModel model)
{
Javascript
$.ajax({
type: "POST",
url: "/api/xxxxx",
async: 'false',
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
error: function (xhr, status, err) {
if (xhr.status == 400) {
DisplayModelStateErrors(xhr.responseJSON.ModelState);
}
},
....
function DisplayModelStateErrors(modelState) {
var message = "";
var propStrings = Object.keys(modelState);
$.each(propStrings, function (i, propString) {
var propErrors = modelState[propString];
$.each(propErrors, function (j, propError) {
message += propError;
});
message += "\n";
});
alert(message);
};
Here you can check to show the model state error one by one
public HttpResponseMessage CertificateUpload(employeeModel emp)
{
if (!ModelState.IsValid)
{
string errordetails = "";
var errors = new List<string>();
foreach (var state in ModelState)
{
foreach (var error in state.Value.Errors)
{
string p = error.ErrorMessage;
errordetails = errordetails + error.ErrorMessage;
}
}
Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("error", errordetails);
return Request.CreateResponse(HttpStatusCode.BadRequest, dict);
}
else
{
//do something
}
}
}
I had an issue implementing the accepted solution pattern where my ModelStateFilter would always return false (and subsequently a 400) for actionContext.ModelState.IsValid for certain model objects:
public class ModelStateFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = new HttpResponseMessage { StatusCode = HttpStatusCode.BadRequest};
}
}
}
I only accept JSON, so I implemented a custom model binder class:
public class AddressModelBinder : System.Web.Http.ModelBinding.IModelBinder
{
public bool BindModel(HttpActionContext actionContext, System.Web.Http.ModelBinding.ModelBindingContext bindingContext)
{
var posted = actionContext.Request.Content.ReadAsStringAsync().Result;
AddressDTO address = JsonConvert.DeserializeObject<AddressDTO>(posted);
if (address != null)
{
// moar val here
bindingContext.Model = address;
return true;
}
return false;
}
}
Which I register directly after my model via
config.BindParameter(typeof(AddressDTO), new AddressModelBinder());
You can also throw exceptions as documented here:
http://blogs.msdn.com/b/youssefm/archive/2012/06/28/error-handling-in-asp-net-webapi.aspx
Note, to do what that article suggests, remember to include System.Net.Http
Put this in the startup.cs file
services.AddMvc().ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = (context) =>
{
var errors = context.ModelState.Values.SelectMany(x => x.Errors.Select(p =>p.ErrorMessage)).ToList();
var result = new Response
{
Succeeded = false,
ResponseMessage = string.Join(", ",errors)
};
return new BadRequestObjectResult(result);
};
});

MVC3 Editing in the Index View

I need some help with this one....
I have this simple model:
public class Candidat
{
public string LoginEmail { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Prénom")]
public string FirstName { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Nom")]
public string LastName { get; set; }
}
I also have a controller like this:
[Authorize]
public ActionResult Index(Candidat model)
{
if (model.LoginEmail == null)
{
model = null;
using (var db = new rhDB())
{
MembershipUser user = Membership.GetUser();
if (user != null)
{
model = (from m in db.Candidates where m.LoginEmail == user.Email select m).SingleOrDefault();
}
if (model == null)
{
model = new Candidat();
model.LoginEmail = user.Email;
model.Email = user.Email;
}
}
}
return View("MyProfileCandidate", model);
}
As you can see, I check if the user as an existing record in the database. If not, I create a new instance of the model and set some default values... Then, I pass it to an EditView.
The problem is that my view show with the error validation messages... For all required fields...
Of course, this is because those fields are empty and required... It seems like the view think I am posting back an invalid model... Is there a way to hide those validation message ?
Try clearing the model state if you intend to modify some of the values on your model in the POST action:
[Authorize]
public ActionResult Index(Candidat model)
{
if (model.LoginEmail == null)
{
model = null;
using (var db = new rhDB())
{
MembershipUser user = Membership.GetUser();
if (user != null)
{
ModelState.Clear();
model = (from m in db.Candidates where m.LoginEmail == user.Email select m).SingleOrDefault();
}
if (model == null)
{
ModelState.Clear();
model = new Candidat();
model.LoginEmail = user.Email;
model.Email = user.Email;
}
}
}
return View("MyProfileCandidate", model);
}
The reason for this is that HTML helpers will use model state values that were initially posted instead of those in the model. You could also clear individual fields in the model state: ModelState.Remove("FirstName");.

How to validate one field related to another's value in ASP .NET MVC 3

I had two fields some thing like phone number and mobile number. Some thing like..
[Required]
public string Phone { get; set; }
[Required]
public string Mobile{ get; set; }
But user can enter data in either one of it. One is mandatory. How to handle them i.e how to disable the required field validator for one field when user enter data in another field and viceversa. In which event i have to handle it in javascript and what are the scripts i need to add for this. Can anyone please help to find the solution...
One possibility is to write a custom validation attribute:
public class RequiredIfOtherFieldIsNullAttribute : ValidationAttribute, IClientValidatable
{
private readonly string _otherProperty;
public RequiredIfOtherFieldIsNullAttribute(string otherProperty)
{
_otherProperty = otherProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_otherProperty);
if (property == null)
{
return new ValidationResult(string.Format(
CultureInfo.CurrentCulture,
"Unknown property {0}",
new[] { _otherProperty }
));
}
var otherPropertyValue = property.GetValue(validationContext.ObjectInstance, null);
if (otherPropertyValue == null || otherPropertyValue as string == string.Empty)
{
if (value == null || value as string == string.Empty)
{
return new ValidationResult(string.Format(
CultureInfo.CurrentCulture,
FormatErrorMessage(validationContext.DisplayName),
new[] { _otherProperty }
));
}
}
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif",
};
rule.ValidationParameters.Add("other", _otherProperty);
yield return rule;
}
}
which you would apply to one of the properties of your view model:
public class MyViewModel
{
[RequiredIfOtherFieldIsNull("Mobile")]
public string Phone { get; set; }
public string Mobile { get; set; }
}
then you could have a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
and finally a view in which you will register an adapter to wire the client side validation for this custom rule:
#model MyViewModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script type="text/javascript">
jQuery.validator.unobtrusive.adapters.add(
'requiredif', ['other'], function (options) {
var getModelPrefix = function (fieldName) {
return fieldName.substr(0, fieldName.lastIndexOf('.') + 1);
}
var appendModelPrefix = function (value, prefix) {
if (value.indexOf('*.') === 0) {
value = value.replace('*.', prefix);
}
return value;
}
var prefix = getModelPrefix(options.element.name),
other = options.params.other,
fullOtherName = appendModelPrefix(other, prefix),
element = $(options.form).find(':input[name="' + fullOtherName + '"]')[0];
options.rules['requiredif'] = element;
if (options.message) {
options.messages['requiredif'] = options.message;
}
}
);
jQuery.validator.addMethod('requiredif', function (value, element, params) {
var otherValue = $(params).val();
if (otherValue != null && otherValue != '') {
return true;
}
return value != null && value != '';
}, '');
</script>
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(x => x.Phone)
#Html.EditorFor(x => x.Phone)
#Html.ValidationMessageFor(x => x.Phone)
</div>
<div>
#Html.LabelFor(x => x.Mobile)
#Html.EditorFor(x => x.Mobile)
#Html.ValidationMessageFor(x => x.Mobile)
</div>
<button type="submit">OK</button>
}
Pretty sick stuff for something so extremely easy as validation rule that we encounter in our everyday lives. I don't know what the designers of ASP.NET MVC have been thinking when they decided to pick a declarative approach for validation instead of imperative.
Anyway, that's why I use FluentValidation.NET instead of data annotations to perform validations on my models. Implementing such simple validation scenarios is implemented in a way that it should be - simple.
I know this question is not so hot, because it was asked relatively long time ago, nevertheless I'm going to share with a slightly different idea of solving such an issue. I decided to implement mechanism which provides conditional attributes to calculate validation results based on other properties values and relations between them, which are defined in logical expressions.
Your problem can be defined and automatically solved by the usage of following annotations:
[RequiredIf("Mobile == null",
ErrorMessage = "At least email or phone should be provided.")]
public string Phone{ get; set; }
[RequiredIf("Phone == null",
ErrorMessage = "At least email or phone should be provided.")]
public string Mobile { get; set; }
If you feel it would be useful for your purposes, more information about ExpressiveAnnotations library can be found here. Client side validation is also supported out of the box.
Since nobody else suggested it, I'm going to tell you a different way to do this that we use.
If you create a notmapped field of a custom data type (in my example, a pair of gps points), you can put the validator on that and you don't even need to use reflection to get all the values.
[NotMapped]
[DCGps]
public GPS EntryPoint
{
get
{
return new GPS(EntryPointLat, EntryPointLon);
}
}
and the class, a standard getter/setter
public class GPS
{
public decimal? lat { get; set; }
public decimal? lon { get; set; }
public GPS(decimal? lat, decimal? lon)
{
this.lat = lat;
this.lon = lon;
}
}
and now the validator:
public class DCGps : DCValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (!(value is GPS)) {
return new ValidationResult("DCGps: This annotation only works with fields with the data type GPS.");
}
//value stored in the field.
//these come through as zero or emptry string. Normalize to ""
string lonValue = ((GPS)value).lonstring == "0" ? "" : ((GPS)value).lonstring;
string latValue = ((GPS)value).latstring == "0" ? "" : ((GPS)value).latstring;
//place validation code here. You have access to both values.
//If you have a ton of values to validate, you can do them all at once this way.
}
}

Resources