I have sample app in Nancy and have problem with request validation.
I am using FluentValidator with BindAndValidate extension. So for example i have model :
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
And module with :
Post["/create-user"] = m => this.BindAndValidate<User>());
And there is problem, if client app call module with parameters Name:"foo,Age:"some-string",
then Nancy throw exception :
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Exception: some-string is not a valid value for Int32. ---> System.FormatException: Input string was not in a correct format.
Is here any workaround for exception by parameter ("property Age was not in correct format") ?
Thanks
The problem is that the bind fails so the validator never runs. You can tell nancy to ignore binding errors, but it doesn't do so gracefully (it basically stops binding on the first error). So then your validation step does run, but may complain about properties that were ok, but simply were not set by the binder.
You can get around this by providing your own BodyDeserializer that uses Newtonsoft's handling of errors so that the binding doesn't stop on the first error found. See Handle multiple binding errors from ModelBindingException in NancyFX when binding to JSON in Request.Body
Before binding you could try to check if Age is int, and if it is then to validation. Something like this:
int age;
bool isInt = int.TryParse(Request.Form("Age"), out age);
if (isInt)
{
this.BindAndValidate<User>();
}
Hope it helps.
Related
I'm attempting to hand crank a Saga without using an Automatonymous State Machine (which I started using but found it difficult to correctly unit test) by following a similar pattern to the way that Sagas are implemented in NServiceBus.
However I'm having an issue where a message is hitting the saga before an instance has been created by the initial message, and this causes MassTransit to silently swallow the message without throwing an exception or moving the message to the error queue.
While trying to find out to solve this issue a lot of people are suggesting the use of OnMissingInstance to fault the message and then rely on a retry framework to effectively delay it until the saga has been correctly initialized.
See Here.
I was wondering if there was a way to do this without using the Automatonymous framework, most likely by leveraging some middleware to preemptively check to ensure that a Saga exists for a message (throwing an exception and retrying later if not) before it tries to process it? And if not does this sound like something that would be useful/is possible?
Further information:
-Using Azure Service Bus
-MongoDB for Saga Persistence.
Some code simplified code snippets of what I'm going for:
ec.Saga(new MongoDbSagaRepository<CodeProviderSaga>(sagaDatabase, new MongoDbSagaConsumeContextFactory(), nameof(CodeProviderSaga)), config =>
{
config.UseApplicationInsights(telemetryClient);
config.UseRetry(retryConfig => retryConfig.Exponential(10, TimeSpan.FromMilliseconds(500), TimeSpan.FromMinutes(5), TimeSpan.FromSeconds(1)));
});
public class CodeProviderSaga :
InitiatedBy<FirstEvent>,
Orchestrates<SecondEvent>,
IVersionedSaga
{
[BsonId]
public Guid CorrelationId { get; set; }
public int Version { get; set; }
public bool SecondEventRecieved { get; set; }
public DateTimeOffset? LastUpdated { get; set; }
public Task Consume(ConsumeContext<FirstEvent> context)
{
this.LastUpdated = DateTimeOffset.UtcNow;
return Task.Completed;
}
// An exception should be thrown if this is received first so it can be retried but it is silently dropped atm
public Task Consume(ConsumeContext<SecondEvent> context)
{
this.SecondEventRecieved = true;
this.LastUpdated = DateTimeOffset.UtcNow;
return Task.Completed;
}
}
To close this out, a commit was just added to the develop branch which throws an exception for messages without a matching saga that are specified in the Orchestrates<T> interface.
I'm building a plugin for Jenkins and I'm trying to validate my form (connection test method). This worked fine when all #QueryParameter were Strings.
Now I'm trying to send my form validation method an Object like this:
public FormValidation doTestConnection(
#QueryParameter("url") final String url,
#QueryParameter("timeout") final String timeout,
#QueryParameter("bypassProxy") final boolean bypassProxy,
#QueryParameter("deployerCredentialsConfig") final CredentialsConfig deployerCredentialsConfig,
#QueryParameter("resolverCredentialsConfig") final CredentialsConfig resolverCredentialsConfig
) throws ServletException {
In my global.jelly file I have this:
<f:validateButton
title="${%Test Connection}" progress="${%Testing...}"
method="testConnection"
with="url,timeout,bypassProxy,deployerCredentialsConfig,resolverCredentialsConfig"/>
My CredentialConfig class implements Serializable but I guess that is not enough becuase I'm getting this when clicking the "Test Connection" button:
java.lang.IllegalArgumentException: Failed to invoke public hudson.util.FormValidation
org.jfrog.hudson.MyBuilder$DescriptorImpl.doTestConnection(java.lang.String,java.lang.String,boolean,org.jfrog.hudson.CredentialsConfig,org.jfrog.hudson.CredentialsConfig) throws javax.servlet.ServletException
Jenkins has no good documentation for using objects inside of FormValidation calls.
Looking at the Jenkins documentation and the code behind <f:validateButton/>, I believe it's impossible have objects bind in validation logic.
The docs say (https://wiki.jenkins-ci.org/display/JENKINS/Jelly+form+controls):
The 'with' attribute specifies the input fields sent to the server for
the validation. They are matched against the field attribute or the
name attribute of other input controls. The values of the nearest
input fields above the are sent to the server, so
this means the button has to come after the input fields. Multiple
fields can be specified by using ','.
The code simply gets fields by the names - there is no "object assembly" (I believe it's only done during actual config submission).
https://github.com/jenkinsci/jenkins/blob/96ec7a267e0efba2ec99590c871db0940e35920f/war/src/main/webapp/scripts/hudson-behavior.js#L2856
I bumped into a similar problem. Looking at the code, it seems stapler tries to convert your parameter to the type you provided in the doCheck function declaration.
class HandlerImpl extends AnnotationHandler<QueryParameter> {
public Object parse(StaplerRequest request, QueryParameter a, Class type, String parameterName) throws ServletException {
String name = a.value();
if(name.length()==0) name=parameterName;
if(name==null)
throw new IllegalArgumentException("Parameter name unavailable neither in the code nor in annotation");
String value = request.getParameter(name);
if(a.required() && value==null)
throw new ServletException("Required Query parameter "+name+" is missing");
if(a.fixEmpty() && value!=null && value.length()==0)
value = null;
return convert(type,value); // <--- HERE
}
}
As a workaround, I changed the type to boolean, like so:
public FormValidation doTestConnection(
#QueryParameter("url") final String url,
#QueryParameter("timeout") final String timeout,
#QueryParameter("bypassProxy") final boolean bypassProxy,
#QueryParameter("deployerCredentialsConfig") final boolean deployerCredentialsConfig,
#QueryParameter("resolverCredentialsConfig") final boolean resolverCredentialsConfig
) throws ServletException {
This allows me to at least check if the variable is set. It might not be enough for your use case, though.
I'm using the Kendo AutoComplete client javascript widget, which sends server requests such as the following:
https://domainName/Proto2/api/Goal/Lookup?text=ABC&goalId=8b625c56-7b04-4281-936f-b88d7ca27d76&filter%5Blogic%5D=and&filter%5Bfilters%5D%5B0%5D%5Bvalue%5D=&filter%5Bfilters%5D%5B0%5D%5Boperator%5D=contains&filter%5Bfilters%5D%5B0%5D%5Bfield%5D=Description&filter%5Bfilters%5D%5B0%5D%5BignoreCase%5D=true&_=1423833493290
The MVC server side method to receive this is:
[Route("api/Goal/Lookup")]
[HttpGet] // if the action name doesn't start with "Get", then we need to specify this attribute
public ICollection<IAllegroGoalContract> Lookup(Guid goalId, string text = "")
The problem occurs if the client sends an empty value for the text parameter (ex: text=&goalId=8b625c56-7b04-4281-936f-b88d7ca27d76). In this case .net returns the following error.
"System error - unable to process parameters
(goalId,text,text.String) - invalid data detected"
I've tried various Route attribute values:
[Route("api/Goal/Lookup/{goalId:guid},{text?}")]
[Route("api/Goal/Lookup/{text?}")]
Looks like your parameters are used as a filter, so instead of the GoalId and Text parameters to be part of the route, define a class like this:
public class LookupOptions
{
public Guid GoalId { get; set; } // change this to Guid? if the client can send a nullable goalId.
public string Text { get; set; }
}
So your method signature will be :
[Route("api/Goal/Lookup")]
[HttpGet]
public ICollection<IAllegroGoalContract> Lookup([FromUri]LookupOptions options)
{
// Note that [FromUri] will allow the mapping of the querystring into LookupOptions class.
}
Now, you can pass your options from the client as part of the Query string and it will be assigned to the LookupOptions parameter.
Hope this helps.
In my ASP.NET MVC application, I have a form and I'm using a ViewModel, so the ModelBinder can bind to my Strongly Typed Class. I'm using DataAnnotations for validation
public class FormViewModel
{
[Required]
public string SomeValue {get;set;}
[Range(0, 10, ErrorMessage="Enter a number between 0 and 10.")]
public byte? SomeOtherValue {get;set;}
}
This works great. The problem however is when the user doesn't enter a valid value for the SomeOtherValue (like abc), a standard MVC-error pops up: 'The value 'abc' is not valid for 'SomeOtherValue'. This is really annoying, as I can't customize this message. I know there are ways to Localize this message, but that just doesn't make sense (I don't want a general message, I want a value-specific value).
I tried applying a RegularExpression-attribute to the 'SomeOtherValue', which only allows byte-values, but probably the standard-validation 'overrides' this validation. Is there some way to apply a custom 'the value is not valid' message for a property, or otherwise disable the standard-message?
Here is a different (non-ideal way, IMHO) to fix it if the custom validation attribute is not working for you. In the controller:
if (!ModelState.IsValid)
{
string fieldName = "ThatFieldName";
var m = ViewData.ModelState[fieldName];
if (m != null && m.Errors.Count > 0)
{
ViewData.ModelState.Remove(fieldName);
ViewData.ModelState.AddModelError(fieldName, "You mucked that field up.");
}
}
I am new to asp.net and I have a problem. When the users insert in a editor for a decimal field something other than numbers, they get an error "Field name" is not a number. But I don't want them to receive this message I want them to receive another message. I have no problem with this with required and range validators.
Is there any way for me to do this?
I am not refering necessarily to changing the culture just displaying another message.
Thanks.
Hope I understand your, to change RangeValidator ErrorMessage just initialize ErrorMessage parameter:
[Range(0, 100, ErrorMessage = "Some another error message insert here!")]
[RegularExpression("\d", ErrorMessage = "!!!")]
public decimal DecimalField { get; set; }
This is the actual answer:
Create a class CustomClientDataTypeModelValidatorProvider. Copy the code from the MVC sources. Change the method MakeErrorString to output the appropiate message like this:
private static string MakeErrorString(string displayName)
{
return string.Format(
CultureInfo.CurrentCulture,
Core.Resources.Errors.EroareNuENr,
displayName);
}
I couldn't find a way not to copy the code just extend it as it uses this static method.
If anyone knows this please tell me.
Then, in global.asax, I wrote this:
var cdProvider = ModelValidatorProviders.Providers.SingleOrDefault(p => p.GetType().Equals(typeof(ClientDataTypeModelValidatorProvider)));
if(cdProvider != null)
{
ModelValidatorProviders.Providers.Remove(cdProvider);
ModelValidatorProviders.Providers.Add(
new CustomClientDataTypeModelValidatorProvider());
}
so that the flow would actually be routed to my class and not the class in the asp.net MVC dll
I got the idea from here:
Unfortunately this is is not a trivial task. However you can try the following hack...
Better to do this only on essential fields, as this is more code to maintain.
In the controller's action method
if(ModelState.IsValid)
{
// code
}
else
{
if (ModelState["YourField"].Errors.Count > 0)
{
ModelState["YourField"].Errors.Clear();
ModelState.AddModelError("YourField", "Your custom message here");
}
// code
}
You can set ResourceClassKey of ClientDataTypeModelValidatorProvider class to name of a global resource that contains FieldMustBeNumeric key to replace mvc validation error message of number with your custom message. Also key of date validation error message is FieldMustBeDate.
ClientDataTypeModelValidatorProvider.ResourceClassKey="MyResources"; // MyResource is my global resource
See here for more details on how to add the MyResources.resx file to your project:
The field must be a number. How to change this message to another language?
To change the error message you get after server side validation you need to change 'PropertyValueInvalid' key in your resource file and assign the resource file name to DefaultModelBinder.ResourceClassKey. See this question for details: localize default model validation in mvc 2
Look for solution at the end of this page:
http://jwwishart.wordpress.com/2010/03/22/custom-server-and-client-side-required-validator-in-mvc-2-using-jquery-validate/
I checked this in my MVC 3 RTM project and it works well.
... or use jQuery to change to message on the client.
A quick and simple hack for Customize RangeValidator ErrorMessage --"'Field name' is not a number"-- is using RegularExpression
[Range(0.5, 1000, ErrorMessage = "Amount should be in range {1} to {2}.")]
[DataType(DataType.Currency)]
[RegularExpression(#"\d", ErrorMessage = "Amount is not valid.")]
public decimal Amount{ get; set; }
You could implement your own custom validation attribute: http://haacked.com/archive/2009/11/19/aspnetmvc2-custom-validation.aspx
It seems that since Para's answer MVC evolved and now the ClientDataTypeModelValidatorProvider accepts a ResourceClassKey property. It uses the FieldMustBeNumeric and FieldMustBeNumeric messages specified in your resource class.