I have Internationalized my MvC4 application.Inside model there is one property
public float Fuels{ get; set; }
When i typed string value inside textbox corresponding to this property,I got a validation error 'The value 'ff' is not valid for Fuels.'.But i want to handle it using resource file.If i have selected arabic then validation error should come in arabic..
I got required message using the following code
[Required(ErrorMessageResourceType = typeof(#Application.Resources.Admin.Country),
ErrorMessageResourceName = "FuelReq")]
I have tried the following way:-
public class FLoatAttribute : DataTypeAttribute
{
private const string defaultErrorMessage = "{0} is required.";
public FLoatAttribute()
: base(defaultErrorMessage)
{
}
public override string FormatErrorMessage(string name)
{
return string.Format(ErrorMessageString, name);
}
public override bool IsValid(object value)
{
float objFloat;
return float.TryParse(Convert.ToString(value), out objFloat);
}
}
and tried using regular expression inside model.But it didn't worked.
Pls help me.
Related
I'm trying to sanitize content of MarkupString. Actually I created this (based from https://github.com/dotnet/aspnetcore/blob/574be0d22c1678ed5f6db990aec78b4db587b267/src/Components/Components/src/MarkupString.cs)
public struct MarkupStringSanitized
{
public MarkupStringSanitized(string value)
{
Value = value.Sanitize();
}
public string Value { get; }
public static explicit operator MarkupStringSanitized(string value) => new MarkupStringSanitized(value);
public override string ToString() => Value ?? string.Empty;
}
But render output isn't raw html. How should I implement MarkupStringSanitized to use
#((MarkupStringSanitized)"Sanitize this content")
Couple of suggestions (Not necessarily for OP, but for anyone else looking to solve the problem):
You didn't provide the code that does the actual sanitization, so I'm going to state the (hopefully) obvious best practice and if you're following it, great. Do not use Regular Expressions (Regex) to parse HTML
Also, the Sanitize() method should follow the pattern of immutability in this case
I would suggest the following library Gans.XSS.HtmlSanitizer which is an active library and updated regularly.
The problem
Razor View Engine can doesn't know how to render a MarkupStringSanitized. Just because you duck typed a sanitized version of the same struct doesn't mean it can render it. To get this to render, you'll need to cast it to something it does know, MarkupString
Here's what happens when I used your HtmlSanitizedMarkup directly with no modifications.
#((MarkupStringSanitized)Content)
Working Example #1
Here's an example using my Markdown -> Html playground (fully tested and working):
MarkupStringSanitized.cs
public struct MarkupStringSanitized
{
public MarkupStringSanitized(string value)
{
Value = Sanitize(value);
}
public string Value { get; }
public static explicit operator MarkupStringSanitized(string value) => new MarkupStringSanitized(value);
public static explicit operator MarkupString(MarkupStringSanitized value) => new MarkupString(value.Value);
public override string ToString() => Value ?? string.Empty;
private static string Sanitize(string value) {
var sanitizer = new HtmlSanitizer();
return sanitizer.Sanitize(value);
}
}
MarkupStringSanitizedComponent.razor
#if (Content == null)
{
<span>Loading...</span>
}
else
{
#((MarkupString)(MarkupStringSanitized)Content)
}
#code {
[Parameter] public string Content { get; set; }
}
That extra conversion though is ugly IMO. (maybe someone smarter than me can clean that up?)
Working example #2
Here I tried extending the MarkupString with an extension method. It looks a little better, but only a little.
MarkupStringExtensions.cs
public static class MarkupStringExtensions
{
public static MarkupString Sanitize(this MarkupString markupString)
{
return new MarkupString(SanitizeInput(markupString.Value));
}
private static string SanitizeInput(string value)
{
var sanitizer = new HtmlSanitizer();
return sanitizer.Sanitize(value);
}
}
MarkupStringSanitizedComponent.razor
#if (Content == null)
{
<span>Loading...</span>
}
else
{
#(((MarkupString)Content).Sanitize())
}
#code {
[Parameter] public string Content { get; set; }
}
I post some data to the remote server. When the data run succeeded, it returns:
{"access_token":"sd234sd234sdf324","expires_in":7200}
When it failed, it returns:
{"errcode":40013,"errmsg":"invalid appid"}
Here is my code:
HttpClient HC = new HttpClient();
HttpResponseMessage HRM = await HC.PostAsync("https://www.microsoft/callback/aaa/), null);
string Content = await HRM.Content.ReadAsStringAsync();
Models.SuccessModel SM=(Models.SuccessModel)JsonSerializer.Deserialize(Content, typeof(Models.SuccessModel));
The last part for converting JSON to the class, I used the SuccessModel yet but if returns the failed JSON it will throw an error which Type is wrong.
I know what the error is. But how can I deal with this?
I want to get whether it is succeeded or failed and get the value of it.
Thank you.
Make another model for error as follows:
public class ErrorModel
{
public string errcode {get;set;}
public string errmsg {get;set;}
}
Then do as follows:
string response = await HRM.Content.ReadAsStringAsync();
if (response.Contains("access_token", StringComparison.OrdinalIgnoreCase))
{
Models.SuccessModel successModel =(Models.SuccessModel)JsonSerializer.Deserialize(response, typeof(Models.SuccessModel));
} else
{
ErrorModel erroModel = (Models.ErrorModel)JsonSerializer.Deserialize(response,typeof(Models.ErrorModel));
}
However, you can work this way
public class Model
{
public string access_token { get; set;}
public long expires_in { get; set;}
public int errcode { get; set;}
public string errmsg { get; set;}
}
So just get the response and check errcode when use it, no need to check when Deserialize
I want to implement localization in a custom attribute to check if the property is a valid IP address or hostname. The validation is working fine so far, but my problem is that I only get the default english error message though my local is switched to german. I am working with resource files. I do not want to implement a client side validation for this. I know there is way to implement an Adapter but correct me if I'm wrong, this is only for client side validation.
My custom validation class:
public class IPAddressOrHostnameAttribute : ValidationAttribute
{
public IPAddressOrHostnameAttribute(string propertyName, object desiredvalue, string errorMessage)
{
PropertyName = propertyName;
DesiredValue = desiredvalue;
ErrorMessage = errorMessage;
}
private string PropertyName { get; }
private object DesiredValue { get; }
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var instance = context.ObjectInstance;
var type = instance.GetType();
var propertyValue = type.GetProperty(PropertyName).GetValue(instance, null);
if (propertyValue.ToString() == DesiredValue.ToString() && value != null)
{
if (Regex.IsMatch(value.ToString(), AckConstants.VALIDIPADDRESSREGEX)
|| Regex.IsMatch(value.ToString(), AckConstants.VALIDHOSTNAMEREGEX))
{
return ValidationResult.Success;
}
return new ValidationResult(ErrorMessage);
}
return ValidationResult.Success;
}
}
My model class:
[Required(ErrorMessage = "The field {0} is required")]
[RegularExpression(#"^\S*$", ErrorMessage = "No white spaces allowed.")]
[IPAddressOrHostname(nameof(IsFileAdapter), true, "Please enter a valid IP address or hostname")]
[IPAddress(nameof(IsFileAdapter), false, "Please enter a valid IP address")]
[Display(Name = "Destination")]
public string Destination { get; set; }
My startup class to configure the DataAnnotationLocalizerProvider:
services
.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix,
opts => { opts.ResourcesPath = "Resources"; })
.AddDataAnnotationsLocalization(options =>
{
options.DataAnnotationLocalizerProvider = (type, factory) =>
factory.Create(typeof(SharedResource)); // SharedResource is the class where the DataAnnotations (translations) will be stored.
})
The localization is working for the default attributes like e.g. Required etc. but not for my custom validation attribute. I have no idea what's wrong in my code. I have read the stackoverflow post ASP.NET Core custom validation attribute localization, but I do not understand why my server side validation with localization is not working. Hopefully someone can help me or give me an example how to get it work, because this problem is driving me crazy.
Creating an adapter could be a solution, but it is too expensive! you have to create the adapter, then create adapter provider then you need to register it in startup! it is too much work.
A shorter solution is to get localization service in the custom validation attribute via ValidationContext.GetService:
If you are using the built-in localization service it will be IStringLocalizer, if you are using a custom localization service e.g. (MyLocalizer) you can access it by parsing it to the GetService(typeof(MyLocalizer)) method. see below sample:
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var _localizationService = (IStringLocalizer)validationContext.GetService(typeof(IStringLocalizer));
var localizedError = _localizationService[ErrorMessage];
//
// do your custom validation
//
// if validation result is wrong
return new ValidationResult(localizedError);
// if validation result is correct
return ValidationResult.Success;
}
btw, ValidationAttribute already has ErrorMessage property, so you don't have to define it in your custom attribute.
public IPAddressOrHostnameAttribute(string propertyName, object desiredvalue /*, string errorMessage*/)
{
PropertyName = propertyName;
DesiredValue = desiredvalue;
// ErrorMessage = errorMessage;
}
Use it just like other attributes:
[IPAddressOrHostname(nameof(IsFileAdapter), true, ErrorMessage = "Please enter a valid IP address or hostname")]
I do not want to implement a client side validation for this. I know there is way to implement an Adapter but correct me if I'm wrong, this is only for client side validation.
Actually, that's not the truth. Adapter doesn't means you'll have to use a client side validation. Please see Ramin's answer here.
As for your question itself, you could create an Adapter and also a AdapterProvider to provide a adapter:
public class IPAddressOrHostnameAttributeAdapter : AttributeAdapterBase<IPAddressOrHostnameAttribute>
{
public IPAddressOrHostnameAttributeAdapter(IPAddressOrHostnameAttribute attribute, IStringLocalizer stringLocalizer)
: base(attribute, stringLocalizer)
{ }
public override void AddValidation(ClientModelValidationContext context) { }
public override string GetErrorMessage(ModelValidationContextBase validationContext)
{
return GetErrorMessage(validationContext.ModelMetadata, validationContext.ModelMetadata.GetDisplayName());
}
}
public class IPAddressOrHostnameAttributeAdapterProvider : IValidationAttributeAdapterProvider
{
private readonly IValidationAttributeAdapterProvider fallback = new ValidationAttributeAdapterProvider();
public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
{
var attr = attribute as IPAddressOrHostnameAttribute;
return attr == null?
this.fallback.GetAttributeAdapter(attribute, stringLocalizer):
new IPAddressOrHostnameAttributeAdapter(attr, stringLocalizer);
}
}
Also, ensure this service is registered in the DI container:
services.AddSingleton<IValidationAttributeAdapterProvider, IPAddressOrHostnameAttributeAdapterProvider>();
Finally, if you're using querystring as the culture provider, don't forget to append a culture=de in your form action:
#{ var __culture = Context.Features.Get<IRequestCultureFeature>().RequestCulture.Culture.ToString(); }
<form asp-action="Create" asp-route-culture="#__culture">
....
</form>
A Screenshot of the Demo
When creating a new class or method I used to be able to see the parameters needed. But, now they don't come up anymore. How do I view parameters when creating a class?
Running the latest windows version.
public class Main {
public static void main(String args[]) {
Case theCase = new Case("Default", "Corsair", "500W");
}
}
public class Case {
private String model;
private String manufacturer;
private String powerSupply;
public Case(String model, String manufacturer, String powerSupply,) {
this.model = model;
this.manufacturer = manufacturer;
this.powerSupply = powerSupply;
}
public void pressPowerButton() {
System.out.println("Power button pressed");
}
public String getModel() {
return model;
}
public String getManufacturer() {
return manufacturer;
}
public String getPowerSupply() {
return powerSupply;
}
}
When making theCase I can't see what my parameters are and have to move to the "Case" class back and forth
You can explicitly call Parameter Info action which is usually mapped to Ctrl/(Cmd) - p.
Nevermind in order to see the parameters as you type you must type them while in the editor without moving your cursor.
I am looking at implementing some pretty simple client side validation by implementing the IClientModelValidator interface. Specifically I am creating a NotEqualTo (and later an EqualTo) validation attribute that will compare the value of one input to another.
To provide a nice UX I want to use the display name of both inputs in the error messages: "Password cannot be the same as Email" for example.
This is has obviously been done a million times and there are plenty of example around, but they are either for previous versions of MVC or are not using the display name of the other property.
Below is what I have so far. I have managed to grab the display name via the Display attribute in the server side IsValid(...) method, but I can't work out how to do similar for the client side AddValidation(...) method.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class NotEqualToAttribute : ValidationAttribute, IClientModelValidator
{
private const string defaultErrorMessage = "{0} cannot be the same as {1}.";
public string OtherProperty { get; private set; }
public NotEqualToAttribute(string otherProperty) : base(defaultErrorMessage)
{
this.OtherProperty = otherProperty;
}
public override string FormatErrorMessage(string name)
{
string.Format(base.ErrorMessageString, name, this.OtherProperty);
}
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-notequalto", this.FormatErrorMessage(context.ModelMetadata.GetDisplayName());
context.Attributes.Add("data-val-notequalto-otherproperty", this.otherProperty);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
return ValidationResult.Success;
PropertyInfo otherProperty = validationContext.ObjectInstance.GetType().GetProperty(this.OtherProperty);
object otherValue = otherProperty.GetValue(validationContext.ObjectInstance, null);
if (!value.Equals(otherValue))
return ValidationResult.Success;
DisplayAttribute display = otherProperty.GetCustomAttribute<DisplayAttribute>();
string otherName = display?.GetName() ?? this.OtherProperty;
return new ValidationResult(string.Format(defaultErrorMessage, validationContext.DisplayName, otherName));
}
}
Typically I solved this myself after taking a break, just going to leave this here in case it helps someone else (or there is a better solution):
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
string otherName =
context.ModelMetadata.ContainerMetadata.Properties
.Single(p => p.PropertyName == this.OtherProperty)
.GetDisplayName();
context.Attributes.Add("data-val-notequalto",
string.Format(defaultErrorMessage, context.ModelMetadata.GetDisplayName(), otherName)
);
}
You can get to the meta data for the other properties via ClientModelValidationContext.ModelMetadata.ContainerMetadata.Properties