I need to validate data coming from request. What I have done so far is:
Added reference to DevTrends.WCFDataAnnotations and System.ComponentModel.DataAnnotations
Placed [ValidateDataAnnotationsBehavior] in service implementation
Placed attribute on data like:
private int items=0;
[DataMember]
[Range(0, 6, ErrorMessage = "Items must be an integer between 0 and 6")]
public int Items
{
get { return items; }
set { items = value; }
}
This is all that's needed according to http://wcfdataannotations.codeplex.com/. What am I missing? When I send a SoapUI request it shows 'The formatter threw an exception while trying to deserialize the message'
Related
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;
}
}
I am new to WCF. I have a sample WCF server and a client consuming the service.
I have a OperationContract called getEmployer4 which accepts a EmployerRequestBO and returns a EmployerResponseBO, both these 2 types are decorated as MessageContract
public EmployerResponseBO getEmployer4(EmployerRequestBO rqst)
{
return new EmployerResponseBO
{ CompanyName = "Apple", CompanyAddress = "US" };
}
my EmployerRequestBO looks like:
[MessageContract(IsWrapped = true, WrapperName = "EmployerRequest", WrapperNamespace ="http://mycompany.com/services")]
public class EmployerRequestBO
{
[MessageHeader(Namespace = "http://mycompany.com/services")]
public string LicenseKey
{
get; set;
}
private int _regID;
[MessageBodyMember(Order = 1, Name = "CompanyRegistrationID", Namespace = "http://mycompany.com/services")]
public int RegistrationID
{
get
{
return _regID;
}
set
{
_regID = value;
}
}
Problem is, when i tried to call the operaiton in client with below code:
ServiceReference_EmployerService.EmployerClient client = new ServiceReference_EmployerService.EmployerClient("BasicHttpBinding_IEmployer");
ServiceReference_EmployerService.EmployerRequestBO request = new ServiceReference_EmployerService.EmployerRequestBO("ABC123", 123);
ServiceReference_EmployerService.EmployerResponseBO response= client.getEmployer4(request);
The getEmployer4 doesnot expect an EmployerRequestBO argument, Error looks like below
Click to see attachment
There is no argument given that corresponds to the required formal
parameter 'CompanyRegistrationID' of
'EmployerClient.GetEmployer4(string, ref int, out string)'.
Can anyone explain why it is asking for primitive types instead of a MessageContract type? Thanks!
It took quite a bit of time before I learned that, if your Operation communicate through MessageContract, you need to create the proxy like:
ServiceReference_EmployerService.**IEmployer** client =
new ServiceReference_EmployerService.EmployerClient("BasicHttpBinding_IEmployer");
whereas if you Operation communicate through DataContract, you need to create the proxy like:
ServiceReference_EmployerService.**EmployerClient** client2 =
new ServiceReference_EmployerService.EmployerClient("BasicHttpBinding_IEmployer");
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
I am implementing Jersey based REST API and using swagger to generate HTML based documentation for the same. I am using swagger's annotations to read and scan the resources to generate documentation. I have specified response for each resource using #ApiResponse annotation as below:
#Path("/hello")
#Api(value = "Hello World" )
public class HelloRest
{
#GET
#ApiOperation(value="Hello world", httpMethod="GET")
#ApiResponses(value={ #ApiResponse(code = 200, message = "Success", response = WebservicesErrorResponse.class, reference = "C:/Desktop/hello.json")
#ApiResponse(code = 404, message = "Not found", response = WebservicesErrorResponse.class)})
#Produces({"application/json", "application/xml"})
public Response helloWorld()
{
return Response.status(WebservicesCommonTypes.SUCCESS).entity("Hello rest API").build();
}
}
It is working fine and it is generating HTML based documentation as below:
As it shows the complete structure (Model and example value) of response if response code is 404. And in example value, it is not showing the values, only showing the type for each parameter for the model.
I want to show the sample example schema for the response so that client can understand that what would be the exact response for each response. I researched on it and I found that there is one attribute:
#ApiResponse(reference = "") - Specifies a reference to the response type. The specified reference can be either local or remote and will be used as-is, and will override any specified response() class.
I tried it and I give it a path for my sample.json file as below:
#ApiResponse(code = 200, message = "Success", response = WebServicesErrorResponse, reference = "http://localhost:9001/myinstanceofapplication/html/api-doc/hello.json")
and I also tried to give another path that is local path like below:
#ApiResponse(code = 200, message = "Success", response = WebservicesErrorResponse.class, reference = "C:/Desktop/hello.json")
but when swagger generate document for it then it gives following:
It is showing C:/Desktop/hello.json is not defined!
I have researched and tried lot many solutions but couldn't able to give proper reference to it. I found that this is an issue by https://github.com/swagger-api/swagger-ui/issues/1700 and https://github.com/swagger-api/swagger-js/issues/606.
So how can I use reference attribute of #ApiResponse to that swagger could show the sample XML/JSON swagger UI. My model class is below:
#XmlRootElement(name="response")
#XmlAccessorType(XmlAccessType.FIELD)
public class WebservicesErrorResponse
{
#XmlElement
private int code;
#XmlElement
private String message;
public WebservicesErrorResponse(){ }
public WebservicesErrorResponse(int code, String message)
{
this.code = code;
this.message = message;
}
public int getCode()
{
return code;
}
public void setCode(int code)
{
this.code = code;
}
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
}
and I want to show following sample XML in the swagger UI:
<?xml version="1.0"?>
<response>
<code>200</code>
<message>success</message>
</response>
You need to annotate your model class (not the API resource/method!) with the #ApiModel and #ApiModelProperty annotations as described here.
For what you want to achieve, it would probably be enough to annotate your model members as follows:
#ApiModelProperty(example = "200")
#XmlElement
private int code;
#ApiModelProperty(example = "success")
#XmlElement
private String message;
If that doesn't work, try putting the annotation on the getters (I'm not really familiar with the XML side of this, have only done it for JSON).
Hello, Here is a class ...
public class Authentification
{
private string userField;
private string passwordField;
public string user
{
get
{
return this.userField;
}
set
{
this.userField = value;
}
}
public string password
{
get
{
return this.passwordField;
}
set
{
this.passwordField = value;
}
}
}
here the web service :
[WebMethod]
public Vehicle[] getVehiculeList(Authentification authentification)
{
....
}
Here the client and the call of webservice :
(the same class Authentification like in the webservice has been defined)
Authentification azz = new Authentification() ;
azz.user = "toto";
azz.password = "tata";
string aa = ws.getVehiculeList(azz);
gives an error :
Error 27 The best overloaded method match for 'WSCL.localhost.Service1.getVehiculeList(WSCL.localhost.Authentification)' has some invalid arguments
and
Error 28 Argument '1': cannot convert from 'WSCL.Authentification' to 'WSCL.localhost.Authentification'
Any help ?
Thank a lot !
What might have happened is that you have referenced the assembly containing the data entities (e.g. Authentication) on your client, and now you have both the proxied entity (WSCL.localhost.Authentification) and the original server entity (WSCL.Authentification). If you change your client's use of Authentication to use the proxied class (WSCL.localhost.Authentification) it should work.
If you switch to WCF, you will be able to move the data entities like Authentication into a separate assembly, and then Share this same type between your Service and your Client. AFAIK this isn't possible 'out of the box' in ASMX.