I am trying to make a custom file validation for my project which is based on .net core 2.
I want to validate file size and also file extension in order to prevent users from uploading large files and for example .png files.
I have searched a lot but I could not find anything that works.
Here is my file validation class :
public class FileTypeAttribute : ValidationAttribute, IClientModelValidator
{
private const int MaxSize = 1048576;
private const string _DefaultErrorMessage = "Only the following file types are allowed: {0}";
private IEnumerable<string> _ValidTypes { get; set; }
public string ValidTypes { get; set; }
public string ErrorMessageExtension { get; set; }
public string ErrorMessageSize { get; set; }
public FileTypeAttribute(string errorExtension, string errorSize, string vt)
{
ErrorMessageExtension = errorExtension;
ErrorMessageSize = errorSize;
_ValidTypes = vt.Split(',');
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
IFormFile file = value as IFormFile;
if (file != null)
{
if (!_ValidTypes.Any(e => file.FileName.EndsWith(e)))
{
return new ValidationResult(ErrorMessageExtension);
}
if (file.Length > MaxSize)
{
return new ValidationResult(ErrorMessageSize);
}
}
return ValidationResult.Success;
}
public void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
MergeAttribute(context.Attributes, "data-val-fileextensions", ErrorMessageExtension);
MergeAttribute(context.Attributes, "data-val-maxfilesize", ErrorMessageSize);
}
private bool MergeAttribute(
IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
and here is the javascript code in my view:
$.validator.addMethod("FileType",
function (value, element, param) {
for (var i = 0; i < element.files.length; i++) {
var extension = getFileExtension(element.files[0].name);
if ($.inArray(extension, param.validtypes) === -1) {
return false;
}
}
return true;
});
$.validator.unobtrusive.adapters.add('FileType', ['validtypes'], function (options) {
console.log("value:");
options.rules.cannotbered = {};
options.messages["FileType"] = options.message;
});
function getFileExtension(fileName) {
if (/[.]/.exec(fileName)) {
return /[^.]+$/.exec(fileName)[0].toLowerCase();
}
return null;
}
and here is the entity class code in my project:
public class MyEntityClass
{
public int MyEntityClassId { get; set; }
[FileType("invalid format", "invalid size", "jpg,png,gif")]
public IFormFile Photo { get; set; }
}
Can anyone help me to know where the problem is?
Thanks in advance.
Related
I want to design a response and design it by responses in my asp.net core application. The simlpe response is like following.
public class Response {
public string Status { get; set; } => "Ok";
public BaseReport BaseReport { get;set;}
}
if user sends extra parameters to my service, I want to change my response content dynamically.
public class ReportsController : ControllerBase
{
[HttpGet]
public ActionResult<Response> GetReport(bool isEmployee, bool isFinanace, bool isInformatinTech)
{
// if all parameters fals, return base report.
var report = baseReposrService.Get();
var response = new Response() { BaseReport = report };
if(isEmployee)
{
var ereport = employeeService.Get();
var response = new Response() {
BaseReport = report,
EmployeeReport = ereport
};
}
if(isFinanace)
{
var freport = financeService.Get();
var response = new Response() {
BaseReport = report,
EmployeeReport = freport
};
}
...
...
}
}
the response object is growing by query parameters.
So, is implementing the decorator pattern for this problem right way? Is there any best practice for these type problems?
I tried as below:
public class Response
{
public Response()
{
BaseReport = new BaseReport();
}
public string Status { get; set; } ="Ok";
public BaseReport BaseReport { get; set; }
}
public class BaseReport
{
public string report { get; set; }
public string reportcontent { get; set; }
}
public interface IResponseFactory
{
IResponseFactory Add(string key);
Response Create(string key);
}
public class ResponseFactory1 : IResponseFactory
{
private readonly Dictionary<string, Response> _responsedic = new Dictionary<string, Response>();
public ResponseFactory1()
{
this.Add("Employee").Add("Finanace").Add("InformatinTech");
}
public IResponseFactory Add(string key)
{
var response = new Response();
response.BaseReport.report = key;
response.BaseReport.reportcontent = key + "content";
_responsedic.Add(key, response);
return this;
}
public Response Create(string responsename)
{
if (_responsedic.ContainsKey(responsename))
{
return _responsedic[responsename];
}
else
{
return new Response() { BaseReport = new BaseReport() { report = "basereport",reportcontent= "basereportcontent" } };
}
}
in startup class:
services.AddSingleton<IResponseFactory, ResponseFactory1>();
in controller:
[HttpGet]
public ActionResult<Response> GetReport(string responsetype)
{
var response = _responseFactory.Create(responsetype);
return response;
}
I have this database structure:
public class QRDatabase
{
readonly SQLiteAsyncConnection _database;
public QRDatabase(string dbPath)
{
_database = new SQLiteAsyncConnection(dbPath);
_database.CreateTableAsync<db_QRCODE_Type>().Wait();
}
public Task<List<db_QRCODE_Type>> GetQRCode()
{
return _database.Table<db_QRCODE_Type>().ToListAsync();
}
public Task<int> SaveQRCode(db_QRCODE_Type note)
{
if (note.ID != 0)
{
return _database.UpdateAsync(note);
}
else
{
return _database.InsertAsync(note);
}
}
public Task<int> DelteQRCode(db_QRCODE_Type note)
{
return _database.DeleteAsync(note);
}
}
This uses this type:
public class db_QRCODE_Type
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; } // Identificator of column
public string firstName { get; set; } // firstname
public string lastName { get; set; } // firstname
public byte[] qrBytes { get; set; } //qr code in bytes
}
Then, in the class where I need the DB I am doing this from the tutorial here:
https://learn.microsoft.com/de-de/xamarin/get-started/quickstarts/database?pivots=windows
static QRDatabase database;
static string nameOfDB = "01db_qrs_q2go.db3";
public static QRDatabase Database
{
get
{
if (database == null)
{
database = new QRDatabase(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), nameOfDB));
}
return database;
}
}
However, I am not quite sure how this works since I am never calling "Database" even though this is where the database is supposed to be initiliased.
Now, when I try to write to the database:
db_QRCODE_Type entry = new db_QRCODE_Type();
entry.firstName = entry_firstname.Text;
entry.lastName = entry_lastname.Text;
entry.qrBytes = qrCodeBytes;
try
{
await database.SaveQRCode(entry);
}
catch
{
DependencyService.Get<IMessage>().LongAlert("Etwas hat nicht funktioniert, bitte versuche es noch einmal. Fehlercode: DB_665h");
}
It fails saying it is not set reference to an instance and goes into the catch block. I am doing everything as in the tutorial. Why is this happening?
Thank you!
I have tow models Context and Connection as following:
public class Context
{
[Key]
public long ContextId { get; set; }
[Required]
public string Role { get; set; }
public ICollection<Connection> Connections { get; set; }
public Context()
{
}
}
And
public class Connection
{
[Key]
public long ConnectionId { get; set; }
[Required]
public string Name { get; set; }
public long ContextId { get; set; }
public Context Context { get; set; }
public Connection()
{
}
}
So far, I did not create any controller or repository for Connection. ContextRepositiry looks like following:
public class ContextRepository: IContextRepository
{
private readonly WebAPIDataContext _db;
public ContextRepository(WebAPIDataContext db)
{
_db = db;
}
public Context CreateContext(Context context)
{
_db.Contexts.Add(context);
_db.SaveChanges();
return context;
}
public void DeleteContext(long id)
{
Context context = GetContext(id);
if (context != null)
{
_db.Contexts.Remove(context);
_db.SaveChanges();
}
}
public List<Context> GetAllContexts()
{
return _db.Contexts.AsNoTracking().ToList();
}
public Context GetContext(long id)
{
return _db.Contexts.FirstOrDefault(o => o.ContextId == id);
}
public void UpdateContext(long id, Context context)
{
}
}
public interface IContextRepository
{
List<Context> GetAllContexts();
Context GetContext(long id);
Context CreateContext(Context context);
void UpdateContext(long id, Context context);
void DeleteContext(long id);
}
And it's controller:
[Route("api/[controller]")]
public class ContextController : Controller
{
private readonly IContextRepository _contexts;
public ContextController(IContextRepository contexts)
{
_contexts = contexts;
}
[HttpGet("")]
public IActionResult GetAllContexts()
{
try
{
List<Context> contexts = _contexts.GetAllContexts();
return Ok(contexts);
}
catch (EntityNotFoundException<Context>)
{
return NotFound();
}
}
[HttpGet("{id}")]
public IActionResult GetContext(long id)
{
Context context= _contexts.GetContext(id);
if (context == null)
{
return NotFound();
}
return Ok(context);
}
[HttpPost]
public IActionResult CreateContext([FromBody] Context context)
{
if (ModelState.IsValid == false)
{
return BadRequest(ModelState);
}
Context createdContext= _contexts.CreateContext(context);
if (createdContext== null)
{
return NotFound();
}
return CreatedAtAction(
nameof(GetContext), new { id = createdContext.ContextId}, createdContext);
}
[HttpPut("{id}")]
public IActionResult UpdateContext(long id, [FromBody] Context context)
{
if (ModelState.IsValid == false)
{
return BadRequest(ModelState);
}
try
{
_contexts.UpdateContext(id, context);
return Ok();
}
catch (EntityNotFoundException<Context>)
{
return NotFound();
}
}
[HttpDelete("{id}")]
public IActionResult DeleteCOntext(long id)
{
_contexts.DeleteContext(id);
return Ok();
}
}
Question: While creating a context I shouldn't have to enter any connection data i.e. it should be optional (look ta the swagger request bellow). However, on updating a specific context there could be connection data, and corresponding context should be updated accordingly.
Right now, in Swagger for POST if I enter something like:
{
"contextId": 0,
"role": "Employee",
"connections": [
{
"connectionId": 0,
"name": "",
"contextId": 0,
"context": {}
}
]
}
then it says, The Name field is required and The Role field is required (I am trying to send just context data like role and leaving blank connection data- which should be possible). If I remove "connections":[] then it posts with connections set to null, but don't want to remove it from there.
I am having model as
public class GroupedIssueData
{
[Range(0, double.MaxValue, ErrorMessage = "Please enter valid number")]
public double IssueQty { get; set; }
public double ReqQty { get; set; }
public bool isSavings { get; set; }
}
This contains two properties as IssueQty and IsSaving, If the IsSaving is checked then IssueQty can be empty, If the IssueQty is not empty then IsSaving can be left blank. How can I validate this
My View is
<td>
#Html.DisplayFor(m => m.MaterialData[i].ReqQty)
#Html.HiddenFor(m => m.MaterialData[i].ReqQty)
</td>
<td>#Html.TextBoxFor(m => m.MaterialData[i].IssueQty, new { style = "width:70px" })#Html.ValidationMessageFor(m => m.MaterialData[i].IssueQty)</td>
<td class="text-center">#Html.CheckBoxFor(m => m.MaterialData[i].isSavings)</td>
And my Controller is
public async Task<ActionResult> GetWorkOrderMaterialDetails(IssueEntryModel m)
{
if (!ModelState.IsValid)
{
// redirect
}
var model = new IssueEntryModel();
}
How can I redirect to the if the Model is not valid. Do I need to redirect to same controller. I want to retain the entered data.
My View is
try this
`
[Required]
[Range(18, 100, ErrorMessage = "Please enter an age between 18 and 50")]
public int Age { get; set; }
[Required]
[StringLength(10)]
public int Mobile { get; set; }
[Range(typeof(decimal), "0.00", "15000.00")]
public decimal Total { get; set; } `
if (ModelState.IsValid)
{
//
}
return View(model);
Validation to the Model
Custom Validation Data Annotation Attribute
You can make a custom validation e.g. RequiredIfOtherFieldIsNullAttribute like described here:
How to validate one field related to another's value in ASP .NET MVC 3
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;
}
}
And use it like that:
[RequiredIfOtherFieldIsNull("IsSavings")]
public double IssueQty { get; set; }
[RequiredIfOtherFieldIsNull("IssueQty")]
public bool IsSavings { get; set; }
Use IValidatableObject
However your condition: If the IsSaving is checked then IssueQty can be empty, If the IssueQty is not empty then IsSaving can be left blank is bit confusing, but this might hint you anyways
public class GroupedIssueData : IValidatableObject
{
[Range(0, double.MaxValue, ErrorMessage = "Please enter valid number")]
public double IssueQty { get; set; }
public double ReqQty { get; set; }
public bool isSavings { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!isSavings && IssueQty == 0)
{
yield return new ValidationResult("Error Message");
}
}
}
public async Task<ActionResult> GetWorkOrderMaterialDetails(IssueEntryModel m)
{
if (!ModelState.IsValid)
{
return View(m);
// redirect
}
}
I am implementing a RequiredIf validation attribute and the value being passed to the IsValid method is always null.
RequiredIfAttribute Class
public class RequiredIfAttribute : ValidationAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
ViewModel
[Required]
[Display(Name = "Are You A Student?")]
public bool? IsStudent { get; set; }
[RequiredIf("IsStudent", true, ErrorMessage = "You must upload a photo of your student ID if you wish to register as a student.")]
[Display(Name = "Student ID")]
[FileExtensions("jpg|jpeg|png|pdf", ErrorMessage = "File is not in the correct format.")]
[MaxFileSize(2 * 1024 * 1024, ErrorMessage = "You may not upload files larger than 2 MB.")]
public HttpPostedFileBase StudentId { get; set; }
EditorTemplate
#model bool?
#using System.Web.Mvc;
#{
var options = new List<SelectListItem>
{
new SelectListItem { Text = "Yes", Value = "true", Selected = Model.HasValue && Model.Value },
new SelectListItem { Text = "No", Value = "false", Selected = Model.HasValue && Model.Value }
};
string defaultOption = null;
if (ViewData.ModelMetadata.IsNullableValueType)
{
defaultOption = "(Select)";
}
}
#Html.DropDownListFor(m => m, options, defaultOption)
Every time the form is submitted, the RequiredIf error message is thrown and I have a feeling it has to do with the null value I described initially. What am I doing wrong? Thanks!
NOTE: The HTML appears to be rendering properly, so I don't think that's the problem.
<select data-val="true" data-val-required="The Are You A Student? field is required." id="IsStudent" name="IsStudent"><option value="">(Select)</option>
<option value="true">Yes</option>
<option value="false">No</option>
</select>
Because this is your code -
public class RequiredIfAttribute : ValidationAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
You are using a RequriedAtrribute. So to simulate that it behaves life a RequiredIf you have to implement some logic that will check whether the target property value is true or false. But you are not doing that and returning just from the innerattribute. So it is just a mere Required not RequiredIf -
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
modify this function to do some checking like -
public override bool IsValid(object value)
{
//if the referred property is true then
return innerAttribute.IsValid(value);
//else
return True
}
I use the following code:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public abstract class StefAttribute : ValidationAttribute
{
public WDCIAttribute()
: base()
{
this.ErrorMessageResourceType = typeof(GlobalResources);
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class StefRequiredIfAttribute : StefAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public WDCIRequiredIfAttribute()
{
}
public WDCIRequiredIfAttribute(string dependentProperty, object targetValue)
: base()
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
public class RequiredIfValidator : DataAnnotationsModelValidator<StefRequiredIfAttribute>
{
public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, StefRequiredIfAttribute attribute)
: base(metadata, context, attribute)
{
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
// get a reference to the property this validation depends upon
var field = Metadata.ContainerType.GetProperty(Attribute.DependentProperty);
if (field != null)
{
// get the value of the dependent property
object value = field.GetValue(container, null);
// compare the value against the target value
if (IsEqual(value) || (value == null && Attribute.TargetValue == null))
{
// match => means we should try validating this field
if (!Attribute.IsValid(Metadata.Model))
{
// validation failed - return an error
yield return new ModelValidationResult { Message = ErrorMessage };
}
}
}
}
private bool IsEqual(object dependentPropertyValue)
{
bool isEqual = false;
if (Attribute.TargetValue != null && Attribute.TargetValue.GetType().IsArray)
{
foreach (object o in (Array)Attribute.TargetValue)
{
isEqual = o.Equals(dependentPropertyValue);
if (isEqual)
{
break;
}
}
}
else
{
if (Attribute.TargetValue != null)
{
isEqual = Attribute.TargetValue.Equals(dependentPropertyValue);
}
}
return isEqual;
}
}
Which can be used in the model as follows:
public class PersonnelVM : EntityVM
{
// . . .
[DisplayName("Name")]
[StefRequiredIf("IndividualOrBulk", PersonnelType.Bulk, ErrorMessageResourceName = GlobalResourceLiterals.Name_Required)]
public string Name { get; set; }
[DisplayName("PersonnelType")]
public PersonnelType IndividualOrBulk { get; set; }
// . . .
}