Invalid ModelState error message for Nullable types - asp.net-web-api2

I validate the input using ModelState.IsValid:
[HttpGet]
[Route("subjects")]
[ValidateAttribute]
public IHttpActionResult GetSubjects(bool? isActive = null)
{
//get subjects
}
If I pass in the uri ~/subjects/?isActive=abcdef, I get the error message:
The value 'abcdef' is not valid for Nullable`1.
If the input parameter is not nullable
public IHttpActionResult GetSubjects(bool isActive){
//get subjects
}
I get the error message:
The value 'abcdef' is not valid for Boolean.
I want to override the message if nullable type so I can maintain the message ("The value 'abcdef' is not valid for Boolean."). How can I do this since in the ModelState error I don't get the data type. I am implementing the validation as a custom ActionFilterAttribute (ValidationAttribute).

You can change callback that formats type conversion error messages. For example, let's define it right into Global.asax.cs:
public class WebApiApplication : HttpApplication
{
protected void Application_Start()
{
ModelBinderConfig.TypeConversionErrorMessageProvider = this.NullableAwareTypeConversionErrorMessageProvider;
// rest of your initialization code
}
private string NullableAwareTypeConversionErrorMessageProvider(HttpActionContext actionContext, ModelMetadata modelMetadata, object incomingValue)
{
var target = modelMetadata.PropertyName;
if (target == null)
{
var type = Nullable.GetUnderlyingType(modelMetadata.ModelType) ?? modelMetadata.ModelType;
target = type.Name;
}
return string.Format("The value '{0}' is not valid for {1}", incomingValue, target);
}
}
For not nullable types Nullable.GetUnderlyingType will return null, in this case we will use original type.
Unfortunately you cannot access default string resources and if you need to localize error message you must do it on your own.
Another way is to implement your own IModelBinder, but this is not a good idea for your particular problem.

Lorond's answer highlights how flexible asp.net web api is in terms of letting a programmer customize many parts of the API. When I looked at this question, my thought process was to handle it in an action filter rather than overriding something in the configuration.
public class ValidateTypeAttribute : ActionFilterAttribute
{
public ValidateTypeAttribute() { }
public override void OnActionExecuting(HttpActionContext actionContext)
{
string somebool = actionContext.Request.GetQueryNameValuePairs().Where(x => x.Key.ToString() == "somebool").Select(x => x.Value).FirstOrDefault();
bool outBool;
//do something if somebool is empty string
if (!bool.TryParse(somebool, out outBool))
{
HttpResponseMessage response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
response.ReasonPhrase = "The value " + somebool + " is not valid for Boolean.";
actionContext.Response = response;
}
else
{
base.OnActionExecuting(actionContext);
}
}
Then decorate the action method in the controller with the action filter attribute

Related

HTTP end point property string starts with "is" will get omit [duplicate]

This might be a duplicate. But I cannot find a solution to my Problem.
I have a class
public class MyResponse implements Serializable {
private boolean isSuccess;
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean isSuccess) {
this.isSuccess = isSuccess;
}
}
Getters and setters are generated by Eclipse.
In another class, I set the value to true, and write it as a JSON string.
System.out.println(new ObjectMapper().writeValueAsString(myResponse));
In JSON, the key is coming as {"success": true}.
I want the key as isSuccess itself. Is Jackson using the setter method while serializing? How do I make the key the field name itself?
This is a slightly late answer, but may be useful for anyone else coming to this page.
A simple solution to changing the name that Jackson will use for when serializing to JSON is to use the #JsonProperty annotation, so your example would become:
public class MyResponse implements Serializable {
private boolean isSuccess;
#JsonProperty(value="isSuccess")
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean isSuccess) {
this.isSuccess = isSuccess;
}
}
This would then be serialised to JSON as {"isSuccess":true}, but has the advantage of not having to modify your getter method name.
Note that in this case you could also write the annotation as #JsonProperty("isSuccess") as it only has the single value element
I recently ran into this issue and this is what I found. Jackson will inspect any class that you pass to it for getters and setters, and use those methods for serialization and deserialization. What follows "get", "is" and "set" in those methods will be used as the key for the JSON field ("isValid" for getIsValid and setIsValid).
public class JacksonExample {
private boolean isValid = false;
public boolean getIsValid() {
return isValid;
}
public void setIsValid(boolean isValid) {
this.isValid = isValid;
}
}
Similarly "isSuccess" will become "success", unless renamed to "isIsSuccess" or "getIsSuccess"
Read more here: http://www.citrine.io/blog/2015/5/20/jackson-json-processor
Using both annotations below, forces the output JSON to include is_xxx:
#get:JsonProperty("is_something")
#param:JsonProperty("is_something")
When you are using Kotlin and data classes:
data class Dto(
#get:JsonProperty("isSuccess") val isSuccess: Boolean
)
You might need to add #param:JsonProperty("isSuccess") if you are going to deserialize JSON as well.
EDIT: If you are using swagger-annotations to generate documentation, the property will be marked as readOnly when using #get:JsonProperty. In order to solve this, you can do:
#JsonAutoDetect(isGetterVisibility = JsonAutoDetect.Visibility.NONE)
data class Dto(
#field:JsonProperty(value = "isSuccess") val isSuccess: Boolean
)
You can configure your ObjectMapper as follows:
mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
#Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
if(method.hasReturnType() && (method.getRawReturnType() == Boolean.class || method.getRawReturnType() == boolean.class)
&& method.getName().startsWith("is")) {
return method.getName();
}
return super.nameForGetterMethod(config, method, defaultName);
}
});
I didn't want to mess with some custom naming strategies, nor re-creating some accessors.
The less code, the happier I am.
This did the trick for us :
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonIgnoreProperties({"success", "deleted"}) // <- Prevents serialization duplicates
public class MyResponse {
private String id;
private #JsonProperty("isSuccess") boolean isSuccess; // <- Forces field name
private #JsonProperty("isDeleted") boolean isDeleted;
}
Building upon Utkarsh's answer..
Getter names minus get/is is used as the JSON name.
public class Example{
private String radcliffe;
public getHarryPotter(){
return radcliffe;
}
}
is stored as { "harryPotter" : "whateverYouGaveHere" }
For Deserialization, Jackson checks against both the setter and the field name.
For the Json String { "word1" : "example" }, both the below are valid.
public class Example{
private String word1;
public setword2( String pqr){
this.word1 = pqr;
}
}
public class Example2{
private String word2;
public setWord1(String pqr){
this.word2 = pqr ;
}
}
A more interesting question is which order Jackson considers for deserialization. If i try to deserialize { "word1" : "myName" } with
public class Example3{
private String word1;
private String word2;
public setWord1( String parameter){
this.word2 = parameter ;
}
}
I did not test the above case, but it would be interesting to see the values of word1 & word2 ...
Note: I used drastically different names to emphasize which fields are required to be same.
You can change primitive boolean to java.lang.Boolean (+ use #JsonPropery)
#JsonProperty("isA")
private Boolean isA = false;
public Boolean getA() {
return this.isA;
}
public void setA(Boolean a) {
this.isA = a;
}
Worked excellent for me.
If you are interested in handling 3rd party classes not under your control (like #edmundpie mentioned in a comment) then you add Mixin classes to your ObjectMapper where the property/field names should match the ones from your 3rd party class:
public class MyStack32270422 {
public static void main(String[] args) {
ObjectMapper om3rdParty = new ObjectMapper();
om3rdParty .addMixIn(My3rdPartyResponse.class, MixinMyResponse.class);
// add further mixins if required
String jsonString = om3rdParty.writeValueAsString(new My3rdPartyResponse());
System.out.println(jsonString);
}
}
class MixinMyResponse {
// add all jackson annotations here you want to be used when handling My3rdPartyResponse classes
#JsonProperty("isSuccess")
private boolean isSuccess;
}
class My3rdPartyResponse{
private boolean isSuccess = true;
// getter and setter here if desired
}
Basically you add all your Jackson annotations to your Mixin classes as if you would own the class. In my opinion quite a nice solution as you don't have to mess around with checking method names starting with "is.." and so on.
there is another method for this problem.
just define a new sub-class extends PropertyNamingStrategy and pass it to ObjectMapper instance.
here is a code snippet may be help more:
mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
#Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
String input = defaultName;
if(method.getName().startsWith("is")){
input = method.getName();
}
//copy from LowerCaseWithUnderscoresStrategy
if (input == null) return input; // garbage in, garbage out
int length = input.length();
StringBuilder result = new StringBuilder(length * 2);
int resultLength = 0;
boolean wasPrevTranslated = false;
for (int i = 0; i < length; i++)
{
char c = input.charAt(i);
if (i > 0 || c != '_') // skip first starting underscore
{
if (Character.isUpperCase(c))
{
if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_')
{
result.append('_');
resultLength++;
}
c = Character.toLowerCase(c);
wasPrevTranslated = true;
}
else
{
wasPrevTranslated = false;
}
result.append(c);
resultLength++;
}
}
return resultLength > 0 ? result.toString() : input;
}
});
The accepted answer won't work for my case.
In my case, the class is not owned by me. The problematic class comes from 3rd party dependencies, so I can't just add #JsonProperty annotation in it.
To solve it, inspired by #burak answer above, I created a custom PropertyNamingStrategy as follow:
mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
#Override
public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
if (method.getParameterCount() == 1 &&
(method.getRawParameterType(0) == Boolean.class || method.getRawParameterType(0) == boolean.class) &&
method.getName().startsWith("set")) {
Class<?> containingClass = method.getDeclaringClass();
String potentialFieldName = "is" + method.getName().substring(3);
try {
containingClass.getDeclaredField(potentialFieldName);
return potentialFieldName;
} catch (NoSuchFieldException e) {
// do nothing and fall through
}
}
return super.nameForSetterMethod(config, method, defaultName);
}
#Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
if(method.hasReturnType() && (method.getRawReturnType() == Boolean.class || method.getRawReturnType() == boolean.class)
&& method.getName().startsWith("is")) {
Class<?> containingClass = method.getDeclaringClass();
String potentialFieldName = method.getName();
try {
containingClass.getDeclaredField(potentialFieldName);
return potentialFieldName;
} catch (NoSuchFieldException e) {
// do nothing and fall through
}
}
return super.nameForGetterMethod(config, method, defaultName);
}
});
Basically what this does is, before serializing and deserializing, it checks in the target/source class which property name is present in the class, whether it is isEnabled or enabled property.
Based on that, the mapper will serialize and deserialize to the property name that is exist.

How to handle unknown parameters in ASP.NET Core Actions

How to handle unknown parameters in ASP.NET Core? When I use [FromQuery] it just ignores the unknown parameters, but ideally it should return 400 if the parameter is unknown so the caller knows it needs to fix the parameters?
Example: GetRecords tries to use any StartDate or EndDate from query string, use default value if they are not specified.
But if a query like ?StartTime=2021/2/15&EndTime=2021/2/16, the code actually will return all records from DB as it treats like no parameters passed. Ideally it should throw an error to let caller know the parameter names are invalid.
class RecordQuery
{
public RecordQuery()
{
StartDate = DateTime.MinValue;
EndDateTime = DateTime.Now;
}
//...
}
class Controller
{
public async Task<ActionResult<RecordsResult>> GetRecords([FromQuery] RecordQuery query)
{
// query db where date < query.EndDateTime && date > query.StartDateTime;
}
}
When I use [FromQuery] it just ignores the unknown parameters
Actually, this is the default behavior of the querystring parameters. But you could return an Invalid Request status, so that the client knows that what it's trying to do isn't valid.
To implement it, you can use the ActionFilter, get both the action parameters and request query string queryParameters and make a judgement. Codes like below:
public class QueryActionFilter<T> : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
var model = context.ActionArguments.Values.OfType<T>().Single();
var modelProperties = model.GetType().GetProperties();
var queryParameters = context.HttpContext.Request.Query;
if (!queryParameters.Select(q => q.Key).All(queryParameter => modelProperties.Any(p => p.Name == queryParameter)))
{
context.Result = new BadRequestObjectResult("Querystring does not match");
}
}
}
Then in controller
[TypeFilter(typeof(QueryActionFilter<RecordQuery>))]
public async Task<ActionResult<RecordsResult>> GetRecords([FromQuery] RecordQuery query)
{
// query db where date < query.EndDateTime && date > query.StartDateTime;
}
You can see example https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-web-api?view=aspnetcore-5.0&tabs=visual-studio#the-puttodoitem-method
if(StartDate == null){
return BadRequest();
}
Let's do the same thing with another input parameter(s) (query conditions)
If you want validate input parameter(s), use [Required] for model of [FromQuery], see https://stackoverflow.com/a/19279419/3728901 . In your case, it is model RecordQuery .

How to get a custom ModelState error message in ASP.NET Core when a wrong enum value is passed in?

I'm passing a model to an API action with a property called eventType which is a nullable custom enum.
If I pass a random value for eventType, such as 'h', it fails to serialise which is correct.
However, the error I get from the ModelState is not something I would want a public caller to see. It includes the line number and position (see below).
I've tried a number of options including a custom data annotation with no success.
Does anyone know how I could define a nicer custom message?
"Error converting value \"h\" to type
'System.Nullable`1[Custom.EventTypes]'. Path 'eventType', line 1,
position 80."
Most times the first error is usually the most important error or rather one that describes the situation properly. You can use this way to manipulate to get the first error message from the first key or change it to whatever you want if you wish to get all the error messages.
public ActionResult GetMyMoney(MyModel myModel)
{
string nss = ModelState.First().Key;
ModelError[] ern = ModelState[nss].Errors.ToArray();
string ndd = ern.First().ErrorMessage;
}
public class CustomFilter: IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (!context.ModelState.IsValid)
{
// You can pass custom object to BadRequestObjectResult method
context.Result = new BadRequestObjectResult(customObject);
}
}
}
You can write a custom filter like above mentioned and pass a custom object with your message.
Ref: this
IF you just want the error messages you can simply create a custom class of response and then
var response = new ResponseApi{
StatusCode = HttpStatusCode.BadRequest,
Message = "Validation Error",
Response = ModelState.Values.SelectMany(x => x.Errors).Select(x =>
x.ErrorMessage)
};
then just return the response or create a validation filter to handle validations globally.
/// <summary>
/// Validatation filter to validate all the models.
/// </summary>
public class ValidationActionFilter : ActionFilterAttribute
{
/// <inheritdoc/>
public override void OnActionExecuting(HttpActionContext actionContext)
{
ModelStateDictionary modelState = actionContext.ModelState;
if (!modelState.IsValid)
{
actionContext.Response = SendResponse(new ResponseApi
{
StatusCode= 400,
Message = "Validation Error",
Response = modelState.Values.SelectMany(x =>
x.Errors).Select(x => x.ErrorMessage)
});
}
}
private HttpResponseMessage SendResponse(ResponseApiresponse)
{
var responseMessage = new HttpResponseMessage
{
StatusCode = (HttpStatusCode)response.StatusCode,
Content = new StringContent(JsonConvert.SerializeObject(response)),
};
responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return responseMessage;
}
}

Struts2 more than one action in one class

I'm using Struts2. I have two web forms that have the same code. I would like to eliminate one form. Here is the structure of my Struts project.
\Web Pages
form.jsp
\WEB-INF
\Content
error.jsp
form.jsp
success.jsp
\Source Packages
\action
MyAction.java
MyAction.java
package action;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.*;
public class MyAction extends ActionSupport {
#Action(value = "foo", results = {
#Result(name = "input", location = "form.jsp"),
#Result(name = "success", location = "success.jsp"),
#Result(name = "error", location = "error.jsp")
})
public String execute() throws Exception {
if (user.length() == 1) {
return "success";
} else {
return "error";
}
}
private String user = "";
public void validate() {
if (user.length() == 0) {
addFieldError("user", getText("user required"));
}
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
}
I tried to eliminate form.jsp under \Web Pages by adding a new action method to MyAction.java.
#Action(value="bar", results = {
#Result(name = "success", location = "form.jsp"),
})
public String another() {
return "success";
}
But I got the following error when I go to http : //localhost .../bar.action
HTTP Status 404 - No result defined for action action.MyAction and result input
Your MyAction has an implementation of validate(), which means it is validation aware.
What's happening is that you're calling another, but validate() is kicking in (as it's in the interceptor stack). Validation is failing, and therefore sending to INPUT result, which is not defined in another.
You should
Add #SkipValidation to the another method if you don't want validation there
Add the INPUT result to another() if you want a default input result
On a more general note, when you get that kind of error (No result defined for action X and result input) it usually means you're either having validation errors, parameter population errors (eg: an exception in preparable).

How do i get the invoked operation name within a WCF Message Inspector

I'm doing a message inspector in WCF:
public class LogMessageInspector :
IDispatchMessageInspector, IClientMessageInspector
which implements the method:
public object AfterReceiveRequest(ref Message request,
IClientChannel channel, InstanceContext instanceContext)
I can get the name of the invoked service with:
instanceContext.GetServiceInstance().GetType().Name
But how do I get the name of the invoked operation?
It's not pretty, but this is what I did to get the operation name:
var action = OperationContext.Current.IncomingMessageHeaders.Action;
var operationName = action.Substring(action.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1);
var operationName = OperationContext.Current.IncomingMessageProperties["HttpOperationName"] as string;
This approach is similar to others presented here, but uses Path.GetFileName:
Path.GetFileName(OperationContext.Current.IncomingMessageHeaders.Action);
The return value of this method and the format of the path string work quite harmoniously in this scenario:
The characters after the last directory character in path. If the last
character of path is a directory or volume separator character, this
method returns String.Empty. If path is null, this method returns
null.
OperationContext.Current.IncomingMessageHeaders.Action.Split('/').ToList().Last();
Little late to the party but I had to dig a little deeper than existing answers on this question because they seem to involve getting the action name and not the operation name. (Frequently they are the same so getting the action name does, in fact, get the operation name.)
Microsoft's Application Insights SDK Labs' WCF library makes this concerted effort:
private string DiscoverOperationName(OperationContext operationContext)
{
var runtime = operationContext.EndpointDispatcher.DispatchRuntime;
string action = operationContext.IncomingMessageHeaders.Action;
if (!string.IsNullOrEmpty(action))
{
foreach (var op in runtime.Operations)
{
if (op.Action == action)
{
return op.Name;
}
}
}
else
{
// WebHttpDispatchOperationSelector will stick the
// selected operation name into a message property
return this.GetWebHttpOperationName(operationContext);
}
var catchAll = runtime.UnhandledDispatchOperation;
if (catchAll != null)
{
return catchAll.Name;
}
return "*";
}
private string GetWebHttpOperationName(OperationContext operationContext)
{
var name = WebHttpDispatchOperationSelector.HttpOperationNamePropertyName;
if (this.HasIncomingMessageProperty(name))
{
return this.GetIncomingMessageProperty(name) as string;
}
return "<unknown>";
}