I have to modify auto validation response that [ApiController] generates but only for some endpoints, so I can't change ApiBehaviorOptions. I tried using IActionFilter, but the auto validation is done before action filter and I can't modify it this way.
How can I modify auto validation response for some actions only?
Related
I have a working API with a bunch of controllers, with a single database specified in config file.
Now I want to make the the API multi database and make the target database a part of the url.
I use attributes on controllers now and default routing.
Startup.cs:
app.UseMVC();
FolderController.cs:
[Route("api/[controller]")]
[ApiController]
public class FoldersController : ControllerBase { ...
and action on controller:
[HttpGet("{Parent:Guid}", Name = "Get")]
public IActionResult Get(Guid Parent) {...
So what that gives me is the standard overall template that looks like this:
https://api.example.com/api/{controller}/{action}
What I'd want is to make the database a part of the url, the intuitive place being in front of the controller. I can also skip the second api bit as I'm not running anything else on that base address.
https://api.example.com/{database}/{controller}/{action}
I've been able to extract the database name by changing the controller attribute to:
[Route("{database}/[controller]")]
But then I'd have to insert code in every action method to check for route etc, with the risk of not implementing it consitently (beside the extra typing).
Ideally I'd like to add this to the default route in startup.cs, and add a service to the middleware that would check the privileges for the authenticated user on the requested database and continue as appropriate. That way I'd have my security in one place and no way to forget it in a controller.
I havent been able to figure out how to mix that with the attributes, they seem to conflict with each other.
Can this be done? Does anyone have some pointers for me get out of this?
By understand I know we can do it. You need to implement IHttpHandler.
You can refer to the following example https://www.c-sharpcorner.com/article/dynamic-and-friendly-url-using-mvc/
I have a user search request object which looks like this:
UserSearchRequest
{
FirstName:"John",
LastName:"Smith",
Expand:["groups","devices"]
}
Expand is an optional parameter. I have validation which checks that the provided Expand parameters are within thin expected set of parameters. The problem is that if a client submits a request like this:
{
FirstName:"John",
LastName:"Smith",
Expand:1
}
By default Web API 2 will pass this request to the controller method with an Expand value of null. So the user won't know that he submitted a bad request and the controller won't be able to identify it as a bad request b/c null is a valid value for this property. Is there a way to override this default behavior?
Action filters are triggered before controller executes its logic. I will give a general idea of what you need to do to get you on the right track.
First requirement for ActionFilter is to create your own filter class by extending ActionFilterAttribute class.
The following is a sample for this
public class ValidateCustomModel : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
//Write your logic to check that all attributes are not null
}
}
Now onto the second step. This step will register your filter in WebApiConfig class so that the application will know that it has to pass requests to this filter wherever the attribute is used
config.Filters.Add(new ValidateModelAttribute());
Now the third step is to call the custom class as an attribute on the controller method that is being executed when user makes a request.
[ValidateModel]
Hope this helps you to customise it for your own logic. Happy coding.
I have an OData controller with standard verbs for CRUD. Everything is working fine. Now I need to add a custom action to perform file upload. I try to add a method to my existing controller like this:
[HttpPost]
[Route("UploadFile")]
public async Task<HttpResponseMessage> UploadFile()
{
//handle uploaded content logic here...
}
But when I try to invoke it by doing a POST:
http://localhost/UploadFile
I get this error:
System.InvalidOperationException: No non-OData HTTP route registered.
What should I do for this custom action to allow file upload?
You need to declare the Action as part of the EdmModel, in the following example I am assuming that your Entity Type is Attachment, and your controller class name is AttachmentsController. By convention, your EntitySet name must then be Attachments
var attachments = builder.EntitySet<Attachment>("Attachments");
attachments.Action(nameof(AttachmentsController.UploadFile))
.Returns<System.Net.Http.HttpResponseMessage>();
The important part of the above statement is the return type, if you do not declare the return type correctly in your EdmModel then you will find your endpoints returning 406 errors - Unacceptable, even though your method executes correctly, which is really confusing the first time you run into it. This is because OData will still try to parse your response to match the Accept header from the request before completing the response.
Try to use 'nameof' when mapping functions and actions instead of 'magic strings' or constants so that the compiler can pickup basic issues like wrongly defined route.
With this approach you do not need the Route attribute on the method header and the action will be included in the metadata document and therefore swagger outputs.
I am creating an action/view in ASP.net MVC that I would like to POST using AJAX / jQuery.
I am using Fluent Validation for the validation in my view models.
Is it possible to have client side validation when I do this? What would the script look like in order to trigger this client side validation using fluent validation?
Do I create a regular form and create a submit event using jquery and call something or would I just Ajax.BeginForm() instead?
I use the jQuery unobtrusive validation with data annotations but it looks like you need the same settings as me (the first two options below) plus another step:
Enable client validation in your view or web.config
Enable unobtrusive validation in your view or in web.config
Add the FluentValidationModelValidatorProvider to the ModelValidatorProviders collection
For the first two, see Enabling Client-Side Validation. For the last one see Fluent Validation: Integration with ASP.NET MVC.
If you want to submit the form via AJAX, you can trigger the validation on the whole form with $('#form_selector').valid() or on an individual input with $('#input_selector').valid(). The calls to valid() return true if the validation is successful (and false if not).
Fluent Validation is a server-side validation library. Because of this, Fluent Validation supports some basic client-validations (like required, maxlength etc.) You can't use all server-side rules on client-side.
If you want to add fully client-side support to Fluent Validation, you need to add one more library to your project. Form Helper can be a solution for your problem.
https://github.com/sinanbozkus/FormHelper
You need to create your forms like this:
var formConfig = new FormConfig(ViewContext)
{
FormId = "ProductForm",
FormTitle = "New Product",
BeforeSubmit = "ProductFormBeforeSubmit", // optional
Callback = "ProductFormCallback" // optional,
};
// <form id="#formConfig.FormId" asp-controller="Home" asp-action="Save"
// ...
#await Html.RenderFormScript(formConfig)
After that you need to add [FormValidator] attribute to your action.
[HttpPost, FormValidator]
public IActionResult Save(FormViewModel viewModel)
Now all Fluent Validation rules work on client-side.
I have decorated my base controller with a couple of action filters. They work fine.
One of those filters sets up the request - does things like set the culture based on the domain, etc.
I also have a handful of actions that require authorization using the Authorize attribute.
My problem is that when an user attempts to request a page they are not authorized to access, the authorization filter kicks in and redirects them to a page telling them that they cannot vie the page.
The issue is that the action filters never run so the culture and other request data is never set. This effectively causes language to be wrong in the view and other data to be missing.
I know that authorization filters run first but my question is this: How can I design this such that I can ensure that certain methods are always run before the view is returned, regardless of the authorization.
Hope that makes sense.
According to this documentation (under the Filter Order header), Authorization filters always run before Action filters. This means that messing with Order properties won't help.
I think the best way to handle this is to write your own Authorization attribute (by subclassing AuthorizeAttribute and overriding AuthorizeCore) and running your action filters manually when authorization fails.
See Order of Execution for Action Filters on MSDN Article on Action Filter
Basically, you can supply an Order property on those culture filters so it runs before the Authorization filter, something like this:
[CultureRedirect(Order = 1)]
public class MyBaseController : Controller { }
[Authorize(Order = 2)]
public class RequiresAuth : MyBaseController { }
...
If that fails, you can still Execute code bfore an action executes and before any ActionFilter will executes.