ModelState serialization - asp.net-web-api

I am trying to implement custom validation in Web API. The code in Galloway's video on the subject seems to have changed. I did download the code and the way the create action filter is like this:
public class ValidationActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest,
actionContext.ModelState);
}
}
}
When I post to my api with bad data, this is what is returned:
{"Message":"The request is invalid.","ModelState":{"user":["First name cannot start with A"]}}
Notice how ModelState is not showing individual field causing the error (like user.FirstName).
When I run their application, ModelState does have the field info (comment.Author):
{"Message":"The request is invalid.","ModelState":{"comment.Author":["Author is too long! This was validated on the server."]}}
I have the same action filter and very similar post web api. Why is my error not showing the field level detail?

You probably did set your IncludeErrorDetailPolicy to be 'never' or did set the customErrors in Web.config file to be something else. See this post for details: http://www.jefclaes.be/2012/08/aspnet-web-api-error-detail-policy-now.html

Apparently this was a known issue and has been fixed recently:
http://aspnetwebstack.codeplex.com/workitem/362

Related

OData API Composite key and Swagger Settings

I Encounter an issue with Swagger.
I have a .Net Core Wep API with one entity using a composite key.
The key is declared with the following syntax:
public class EntityConfig : IModelConfiguration
{
public void Apply(ODataModelBuilder builder, ApiVersion apiVersion)
{
EntityTypeConfiguration<Entity> entity = builder.EntitySet<Entity>("Entity").EntityType;
entity.HasKey(x => new { x.FirstId, x.SecondId});
}
}
My controller declare my delete API point like this :
[ODataRoutePrefix("Entity")]
public class EntityController : ODataController
{
///ctor with context injection
[HttpDelete]
public async Task<IActionResult> Delete([FromODataUri] int keyFirstId, [FromODataUri] int keySecondId)
{
///Delete Behavior
}
}
I can call my Delete method with Postman using this request :
http://localhost:8090/api/Entity(FirstId=1,SecondId=1)
Eveything works with Postman and my website, but when i try to launch Swagger i get this error :
Microsoft.OData.ODataException: The number of keys specified in the URI does not match number of key properties for the resource.
How can i set Swagger to accept my parameters as composite key while keep working with OData ?
Thanks
Ok i finally find a solution.
I have change my parameter into my controller and delete the 'key' prefix on my parameter.
Then i have change my request by this version:
http://localhost:8090/api/Entity(FirstId=0,SecondId=0)?FirstId=1&SecondId=1
The request doesn't work without the first parameter declaration (FirstId=0,SecondId=0), i supposed that syntax force the parameter to by see as valid by my controller.
Anyway, everything works and swagger doesn't complain anymore so i guess it's a good start.
Thanks

Web API 2 attribute routing returning 404

I'm having trouble getting the Web API 2 attribute routing to work.
I've been trying everything I could find this whole evening but I can't find the problem.
What I want to achieve is the following:
Make a POST request to http://localhost:xxxx/api/chat/joingroup/1234 to get to the following API call:
[Route("joingroup/{id}")]
[HttpPost]
public async Task<IHttpActionResult> JoinGroup(string id, string connectionID)
{
await hubContext.Groups.Add(connectionID, id);
return Ok(hubContext.Groups.ToString());
}
This keeps getting me a http 400 message.
{"message":"No HTTP resource was found that matches the request URI 'http://localhost:41021/api/chat/joingroup/123'.",
"messageDetail":"No action was found on the controller 'Chat' that matches the request."}
But sending a post to: http://localhost:41021/api/chat/sendmessage/pm/123123 and also to http://localhost:41021/api/chat/joingroup gives me a 200
The chatcontroller:
[RoutePrefix("api/chat")]
public class ChatController : ApiController
{
IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
[...]
[Route("joingroup/{id}")]
[HttpPost]
public async Task<IHttpActionResult> JoinGroup(string id, string connectionID)
{
await hubContext.Groups.Add(connectionID, id);
return Ok(hubContext.Groups.ToString());
}
HTTP POSTS to http://localhost:xxxx/api/chat/sendmessage are working fine.
I cannot figure out why it isn't going to the correct method when I'm calling a POST on http://localhost:xxxx/api/chat/joingroup/1234.
SOLUTION:
The solution was to reference both values that are needed in the JoinGroup method, id and connectionID. Now the request will hit this method.
Using:
http://localhost:xxxx/api/chat/joingroup/john?connectionid=123 will work.
I noticed two things on the code you sent through:
the path you POST to is: localhost:xxxx/joingroup/1234 , this
should be localhost:xxxx/api/chat/joingroup/1234
because you have 2 parameters for the joingroup, you will need to pass both of them through, may be like this localhost:xxxx/api/chat/joingroup/1234?connectionID=value or you can pass it on the request body
if the connectionID is optional you can modify the method to use option al parameters like this
public string JoinGroup(string id, string connectionID = "")
please let me know if this helps.
Thanks
Ashraf
I assume the connectionID parameter references the POSTed data. The easiest thing to make it work is to decorate it with the [FromBody] attribute and put an = in front of the value being sent like this: =MyConnection1.
Web API expects an object with properties or an array otherwise. Alternatively, you can wrap the connection ID with a custom class and pass it serialized as JSON/XML.

MVC5 custom HandleErrorAttribute needs to detect whether to return partial view to ajax call

I've created a custom HandleErrorAttribute in order to implement logging as part of the exception handling. However, unrelated to the logging, I have run into an issue where I need to render a partialview on an Ajax request instead of a JsonResult. I can detect that it is an ajax request but I can't figure out how to determine when it is appropriate to generate a JsonResult or a PartialView. In most instances, a JsonResult is appropriate but in some cases a PartialView is appropriate. Is there a way to determine what the action is expecting from within OnException()?
I was hoping to find a way to detect the required type of response via a property in filterContext or something that would allow me to dynamically determine the expected response type. In all of my research, I could not find anything that would make that possible. So, I dealt with the situation by adding a bool property (defaulting to false) to the custom HandleErrorAttribute class. I then applied this attribute to the method that is responding with a partialView instead of a JsonResult, setting the property value to true. When the property value is true, the OnException() method responds with a partialView instead of a JsonResult.
If there is a better way, please let me know.
I think the following snippets could help you detect the required type of response
protected override void OnException(ExceptionContext filterContext)
{
//Determine the return type of the action
string actionName = filterContext.RouteData.Values["action"].ToString();
Type controllerType = filterContext.Controller.GetType();
var method = controllerType.GetMethod(actionName);
var returnType = method.ReturnType;
if (returnType.Equals(typeof(JsonResult)))
{
}
}

Is it possible to RenderPartial a default or error View if the specified View is not found?

I am using MVC3.
I am wondering whether it is possible to render an error View if the specified View is absent.
ie if "MyTableX" is absent:
RenderPartial("MyTableX");
would return "Error.cshtml" as the Partial View, saying something like "Partial View not found" in the page.
MVC got an attribute called [HandleError] which you should set on your BaseController (or on each controller). There is no need to specify any of the options for the attribute.
The problem with [HandleError] is that it can’t handle 404 (not found), thus we need to create a custom error controller and tell ASP.NET to use it (by configuring web.config and creating and ErrorController):
http://blog.gauffin.org/2011/11/how-to-handle-errors-in-asp-net-mvc/#.UTknoxyfjmA
You can do something based off of this - the trick is in getting the view path.
A missing view returns an InvalidOperationException. So we really need to determine if the view is missing or if it's caused from something different. One way is to figure out how to get the IView in the filter, cast it to a RazorView and grab the path off of it - or the 'hacky' way is to do the below code, but actually look for "the view" and "was not found" in the exceptions message. Its ugly, I know, but if you want something that works tonight, thats all I got before I head to bed, otherwise try to get the view info from that filter.
This code from Phil Haack in this link may help in trying to get the path name, a quick test yielded I wasn't able to get the IView because my filterContext.ParentActionViewContext was null.
Retrieve the current view name in ASP.NET MVC?
So I wrote this basic one, but again, anything throwing an InvalidOperationException will cause this.
Also note a missing 'MissingView.cshtml" could cause an infinite loop here (untested assumption)
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class ViewCheckFilterAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
var exception = filterContext.Exception;
if (exception is System.InvalidOperationException)
{
//ideally here we check to ensure view doesn't exist as opposed
//to something else raising this exception
filterContext.Result = new ViewResult
{
ViewName = "~/Views/Shared/MissingView.cshtml"
};
filterContext.ExceptionHandled = true;
}
}
}

How do I handle a HttpRequestValidationException and return a meaningful error using AddModelError?

I would like to handle HttpRequestValidationExceptions (e.g. when html is inserted into form fields) and return the user back to the page after submission with a meaningful error to highlight the problem.
For example, on a login page, if the user types some invalid characters into the username field, then I would like to catch the HttpRequestValidationException and return the user to the login page with the username field highlighted.
I can catch the error in a class inheriting from HandleErrorAttribute using the OnException method.
Is there a way from here (or any other way) that I can get the Controller *ModelState* to add the error?
Note: I do not want to turn validation off or redirect to an error page.
e.g.:
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.Exception is HttpRequestValidationException)
{
ModelStateDictionary ModelState = <...>
ModelState.AddModelError("Error", "Invalid chars");
filterContext.HttpContext.Response
filterContext.ExceptionHandled = true;
HttpContextBase HttpContext = filterContext.HttpContext;
HttpContext.Response.Redirect(HttpContext.Request.Path);
return;
}
}
Thanks in advance for any help you can give me!
Instead of capturing and handling the HttpRequestValidationException, you could decorate your model's properties with the [AllowHtml] data annotation and your own custom data annotation, which contains the validation rules you require. See this answer for a good example.
Your model's properties may look like this:
[AllowHtml]
[DisallowHtml]
public string SomeProperty{ get; set; }
Which looks a bit silly, but so far it's the cleanest solution I've encountered.

Resources