ModelState.AddModelError - api

Am I missing something? I am trying to return model validation errors from my web api update method like this
public void Update public void UpdateModel(Models.Model entity) {
ModelState.AddModelError("name","error");
return;
However the JSON returned has no validation errors
For example in this article enter link description here
When I try to use a validation filter as in the previous article on this link I get the error Action xxxxx has one or more filters applied that do not derive from AuthorizationFilterAttribute. Only authorization filters are supported on DataController Insert/Update/Delete actions.

ModelState isnt accessible from your JSON, unless you specifically serialize it as such. It's more used from the HtmlHelper extensions behind the scenes. If you're attempting to update the UI as if the model state was marked as such, you're going about it all wrong. You can either:
Do a full post to the server and let it render the appropriate response
Take the json you get back and then style the fields as appropriate for that response.

Related

Implementing a net-core Web API That Returns Previous Response

I'm currently building a Web API in net-core that has the following requirements:
All web transactions must have a unique Guid identifier for each endpoint
If an endpoint is hit with a previously used Guid, then the response that was given for this Guid is returned again
I was attempting to implement this by JsonSerializing the IActionResult inside the WebApi controller, but I ran into an issue where I can't deserialize all IActionResult responses since some don't have a constructor.
For example:
JsonSerializationException: Unable to find a constructor to use for type Microsoft.AspNetCore.Mvc.CreatedResult. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.
Does anybody know if it's possible to work around this?
Personally, I'd use the response caching middleware. You can make it vary on your GUID, so as long as that is included in the request, it will only render the action if the GUID is different.
Short of that, if you want to handle this manually, cache the JSON you're intending to return, not the full response object. Then, you do not need to re-query the database, etc., and you simply return the response, which is not all that much overhead.

Converting API from REST-like routing to named parameters

I have created an API that looks for parameters as such ...
http://server/api/contollerName/param1/param2/param3
for this pattern I used the following method attribute:
[Route("api/myController/{param1}/{param2}/{param3}")]
[HttpGet]
public IHttpActionResult myMethod(int param1, int param2, int param3)
{
// Method implementation goes here
}
However the person who will be utilizing the API (the UI developer) would prefer to have the API setup as this ...
http://server/api/controllerName?param1=value&param2=value&param3=value
Am I correct in thinking that all I need to do is remove the attribute to my method and the Web Api routing engine will choose the correct method based on the parameter names? Is there a way I could modify the route to explicitly define the method and parameters like I have originally done? I like having the route specified so there is no ambiguity as to which method will be executed.
You are correct in thinking that all you need to do is to remove the attribute to your method. You would not be able to define a route template of the format
http://server/api/controllerName?param1=value&param2=value&param3=value
because ASP.Net Web API2 routing template does not allow to contain a "?" character in the route template. The closest you can get to is
http://server/api/controllerName/MethodName/param1={param1},param2={param2}
Also notice that if you have & in the routing template, ASP.NET will throw an error that dangerous Request.Path value was detected, when you make the request to hit that API. This can be resolved though by referring to this SO Question

Searching with WebAPI

I have made a web API class for my Customer model. I have the standard methods (GET, POST, PUT, DELETE). The problem is, I want to implement another GET method which is a search. Something like this:
[HttpGet]
public IEnumerable<Customer> Search(string id)
{
var customers = customerRepository.Search(id);
return customers;
}
The search method performs a search based on the account number of my customers, using the .Contains() method.
The problem is, when I navigate to: mySite.com/api/Customers/Search/123 I get a 404. What am I doing wrong here?
While Darin's answers are always of top quality this question would actually benefit from an answer that explains how searching, paging and filtering should actually be done in any API and how it should be done using the most current version of Web API (v2).
This is a post which I consider a good resource on the matter (technology indenpendent):
http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
The answer should also reflect what's new in ASP.NET Web API v2 because Darin's answer is quite old.
Since this question comes up at the top when doing Google search for "asp.net web api searching" I will try to explain few things here.
To get as close as possible to REST principles with the latest version of ASP.NET Web API (v2) one should take a serious look at attribute routing that was introduced in the latest version. It is very hard to achieve RESTful routing with the old, classic, convention based routing (in global.asax.cs or RouteConfig.cs).
You should read more about that here
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
Now, to go into details how to implement the specifics you ask about.
The most common practice is to expose these types of functionality through query string parameters.
Per REST principles, you should have one endpoint for your Customers resource, for instance
/api/customers
To achieve this you would decorate your GetCustomers() action in your Web API controller like this
[HttpGet]
[Route("/api/customers")]
public HttpResponseMessage GetCustomers(string q="", string sortBy="", string sortDirection="", bool active=true, ...)
{
// q = being optional search query
// sortBy = optional sort by column/property
// sortDirection = optional sort direction
// active = filter on 'active' column/property
// ... other filters may be applicable
}
You would implement this action closely to what you did in classic MVC if you wanted to provide filtered Views.
I would only introduce new controllers and custom actions if really needed, for some custom edge cases.
with regards to a comment about SearchFilter strongly typed object, let's explain that this won't work out of the box because the default model binder will not bind to this class when using GET requests.
So I'd either take those properties out of SearchFilter class and put them on the action itself so they'd bind via query string binder or use the [FromBody] binder if you wanted to bind from the request body. As per http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
HTH
As per the default route setup only the standard controller action names are allowed (the RESTful ones and the dispatching is done based on the HTTP verb). If you want to violate the RESTful conventions and use some custom action names then you will have to modify your route setup in order to include the action name in the url: api/{controller}/{action}/{id}. Now you can send a request to /api/Customers/Search/123 which will invoke the Search action on the Customers API controller.

How to use FluentValidation to display UI error for either or fields

I have an MVC 3 app which uses FluentValidation to express validation logic on some ViewModel objects.
One of the objects has two properties as follows:
[DisplayNameAttribute(UiConstants.Telephone)]
public string Telephone { get; set; }
[DisplayNameAttribute(UiConstants.Email)]
public string Email { get; set; }
The rule is that EITHER of these properties must be entered at the UI and I want the UI to display a validation message for at least one of the fields (Email) when the user hits Submit but without doing a PostBack.
I can get the validation to work with the following code in the validator
RuleFor(contact => contact.Email)
.Must((contact, email) => string.IsNullOrWhiteSpace(email) != string.IsNullOrWhiteSpace(contact.Telephone))
.WithMessage(ValidationConstants.EmailOrTelephone);
and this will display my validation error message at the UI, but only after a PostBack.
I have also used a Custom Validator as follows
Custom(contactUs =>
{
return string.IsNullOrWhiteSpace(contactUs.Telephone) && string.IsNullOrWhiteSpace(contactUs.Email)
? new ValidationFailure("Email", ValidationConstants.EmailOrTelephone)
: null;
});
but this behaves in the same way.
Will this not work the way I am hoping?
Is there another way to do the validator to get the error message to display in the UI without doing a PostBack?
I know that I could also use DataAnnotations but I specifically want to do this with FluentValidation.
Many thanks
Brian
You're looking for client-side validation - this isn't specific to FluentValidation or DataAnnotations. Both mechanisms will work server-side automatically (you have to wire FluentValidation up to do this automatically after model binding, or run it manually).
If you want client-side validation with ASP.NET MVC, you'll also have to wire that bit up. This blog entry may help.
One note though - your Custom validator won't work by default (you'd have to replicate that validation in jQuery on the client). Check out this article on FluentValidation; here's an excerpt that shows what validators should "just work" client-side without rewriting your own:
Note that FluentValidation will also work with ASP.NET MVC's
client-side validation, but not all rules are supported. For example,
any rules defined using a condition (with When/Unless), custom
validators, or calls to Must will not run on the client side. The
following validators are supported on the client:
*NotNull/NotEmpty
*Matches (regex)
*InclusiveBetween (range)
*CreditCard
*Email
*EqualTo (cross-property equality comparison)
*Length

Passing Validation exceptions via WCF REST

I am using WCF and REST, and I have complex types, which are working fine. Now I need to check for validation, I am thinking of using DataAnnotations e.g.
public class Customer
{
[Required]
public string FirstName {get;set;}
}
Now where the issue is how do I pass this validation down to the REST service?
ALso I need to validate the object when it comes back, and throw an exception, if I am to throw an exception then what is the best way of doing this using REST?
I would use the Validation Application Block included in the Microsoft Enterprise Library to validate the data transfer objects being used in the service interface. You can use attributes to decorate the objects' properties with validation rules, much in the same way as with the ASP.NET Data Annotations.
In case validation fails you should return an appropriate HTTP Error Code and include the details of what went wrong in the HTTP response.
Here is an example:
public void PostCustomer(Customer instance)
{
ValidationResults results = Validation.Validate(instance);
if (!results.IsValid)
{
string[] errors = results
.Select(r => r.Message)
.ToArray();
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.BadRequest;
WebOperationContext.Current.OutgoingResponse.StatusDescription = String.Concat(errors);
}
// Proceed with custom logic
}
If you are using the WCF REST Starter Kit, you should instead throw a WebProtocolException, as described in this article.
I would look into writing a custom IDispatchMessageInspector implementation where, in the AfterReceiveRequest method, you manually invoke the validation architecture.
I won't go into the details of how to call the Data Annotations validation architecture as I'm sure you can find that somewhere online if you don't already know how to do it. That said, once you have your validation results you can enumerate them and then, if there are any failed validations, you can throw a generic validation fault filled with the details from the AfterReceiveRequest implementation.