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.
Related
I saw some code like this:
[HttpPost]
[Consumes("application/json")]
public string SaveProductJson(ProductBindingTarget product) {
return $"JSON: {product.Name}";
}
[HttpPost]
[Consumes("application/xml")]
public string SaveProductXml(ProductBindingTarget product) {
return $"XML: {product.Name}";
}
I get the idea of how Consumes filter work, but a little bit confused about how it work internally. Below is the picture from MSDN:
From my understanding, the routing middleware will select the matching action method. Let's say I post a json document to the application, so both SaveProductJson and SaveProductXml match the request (because they have the same routing template [HttpPost]) and Consumes filter hasn't kicked in yet (filters run in endpoint middleware), since Consumes filter runs after routing middleware, how does Consumes filter tell the routing middleware to select SaveProductJson action method?
The Consumes attribute works together with Content-Type header. In your case application/json or application/xml, but it also supports other content types such as application/x-www-form-urlencoded if you want to submit a form.
The Consumes attribute allows an action to limit the supported request content types. Apply the Consumes attribute to an action or controller, specifying one or more content type.
Read more here
The ConsumeAttribute class inherits from IResourceFilter, which is an extensible hook in the MVC pipeline that is called from Controller/Page implementations of ResourceInvoker.
When the ResourceInvoker is executing during the middleware pipeline, it calls the OnResourceExecuting method of the ConsumesAttribute if it has been previously discovered on target actions.
This method then checks the incoming Content-Type header of the request and compares it
public void OnResourceExecuting(ResourceExecutingContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
// Only execute if the current filter is the one which is closest to the action.
// Ignore all other filters. This is to ensure we have a overriding behavior.
if (IsApplicable(context.ActionDescriptor))
{
var requestContentType = context.HttpContext.Request.ContentType;
// Confirm the request's content type is more specific than a media type this action supports e.g. OK
// if client sent "text/plain" data and this action supports "text/*".
//
// Requests without a content type do not return a 415. It is a common pattern to place [Consumes] on
// a controller and have GET actions
if (!string.IsNullOrEmpty(requestContentType) && !IsSubsetOfAnyContentType(requestContentType))
{
context.Result = new UnsupportedMediaTypeResult();
}
}
}
In addition, the attribute also inherits from IActionConstraint which will call the Accept method of targetable constraints in the ActionConstraintMatcherPolicy. This component determines the appropriate target action, based on the matching policies against the content-type which is provided by the ConsumeAttribute.Accept method.
I'm looking for a good place in the ASP.NET Web API lifecycle To update a property in my User entity that is purposed to store the date and time the User last made a request. Obviously, I could just add the code to each of my Controller methods but I would prefer doing this in one place outside of my controllers.
Ideally I would have access to the User principal and could use its Identity property to get the user's ID so that I could retrieve and update my User entity using Entity Framework.
I am currently looking at using a DelegatingHandler implementation.
Can anyone suggest the place in the lifecycle where I should carry this out? A code example would be appreciated.
Create an ActionFilter:
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
// Do your work
}
}
Yes, but wouldn't I have to add the ActionFilter to each and every controller method?
No, you can apply it to the controller or to actions.
Alternatively, you can do the following and you will not have to apply it to every controller (sort of like a global filter):
[LogActionFilter ]
public class LogableApiController : ApiController
{
...
}
Then inherit that wherever you want.
And lastly, another option is to add to global filters by finding the App_Start/FilterConfig.cs and add:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new LogActionFilter());
}
So I have shown you how to apply it to action level, controller level, one or more controllers but not all controllers and then how to apply it to all controllers (global).
I would create an Attribute for your Controller to execute the update on your User Entity with an ActionFilter.
This example explain how to create an attribute for a controller method, it is the same way to do it: Custom Attribute above a controller function
b.e, your controller would be like this:
[SaveUserRequest]
public class HomeController : ApiController
I am working on a MVC application and I have a requirement of dealing with errors and session timeouts by redirecting the user to different error pages based on few parameters in the query string.
The issue I am facing is that i tried to implement this by saving the required parameters from querystring into a session and then redirecting to error pages. But before every HttpGet and Post action in my controllers I am checking if session is active.
So in case of a situation where session values are lost and not able to read them.
How can I implement this thing in any other way?
You need to check whether the session exists, has the fields you expect and is active. If the session does not exist or does not have a fields you expect, then handle the case when the session does not exist yet/expired. If it is not active, then handle the case when the session is no longer active. If everything is ok, then handle the request normally. If the session expired, then handle it as expired.
to check about session, you can use an ActionFilter like this:
public class SessionActiveFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var activeSession = Session["user"];
if (activeSession == null)
//Here, do a redirect
base.OnActionExecuting(filterContext);
}
}
Also, you can use a third option to save the session, like Redis Cache http://blogs.msdn.com/b/webdev/archive/2014/05/12/announcing-asp-net-session-state-provider-for-redis-preview-release.aspx
I know this is a dead story now. But I post this answer for the new comers. Please see the nice tutorial in codeproject about how to check session values in Action Filters.
In a dynamic web application, the session is crucial to hold the information of current logged in user identity/data. So someone without authentication cannot have access to some Page or any ActionResult, to implement this kind of functionality, we need to check session exists (is not null) in every action which required authentication.So, the general method is as follows:
[HttpGet]
public ActionResult Home()
{
if(Session["ID"] == null)
return RedirectToAction("Login","Home");
}
We have to check the above 2 statements each time and in each ActionResult, but it may cause 2 problems.
Repeat Things: As per the good programming stranded, we don't have to repeat the things. Create a module of common code and access it multiple times/repeatedly
Code missing: We have to write code multiple times so it might happen some time we forget to write code in some method or we missed it.
How To Avoid?
The ASP.NET MVC provides a very great mechanism i.e., Action Filters. An action filter is an attribute. You can apply most action filters to either an individual controller action or an entire controller.
If you want to know more about action filter, please click here.
So we will create a custom Action Filter that handles session expiration and if session is null, redirect to Login Action.
Create a new class in your project and copy the following code:
namespace YourNameSpace
{
public class SessionTimeoutAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
if (HttpContext.Current.Session["ID"] == null)
{
filterContext.Result = new RedirectResult("~/Home/Login");
return;
}
base.OnActionExecuting(filterContext);
}
}
}
Now our Action Filter is created and we are ready to use it. The following code will show you how we can apply attribute to Action or to complete controller.
Apply to Action
[HttpGet]
[SessionTimeout]
public ActionResult MyProfile()
{
return View();
}
Apply to Controller
[SessionTimeout]
public class HomeController : Controller
{
}
Now all actions of Home Controller will check for session when hit with the help of Action Filter. So we have reduced the code and repetitive things. This is the benefits of Action Filters.
I have an ASP.NET MVC Application using MVC 4.5. I have been instructed to put a query string authorization to the application. The application serves as a data processor for another application at the front.
We decided to append a querystring in the requesting URLs containing a hash code for instance http://dr.appbox.us/DataReport/?passcode=HASHCODE.
The HASHCODE can be checked and if it matches the request is permitted further.
Now I have about 20 controllers in the application, is there a way that I can check if the HASHCODE is valid in global.asax and redirect the user to error page from there?
Also please tell me if there is a way to check if I can bypass this hashcode for Ajax requests in the application.
Thanks
You can use a custom action filter for this. If you create a new class that inherits from ActionFilterAttribute and override the OnActionExecuting method. Here you can validate the passcode and redirect if required.
public class HashCodeCheckFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var passcode = filterContext.HttpContext.Request.QueryString["passcode"];
// Validate passcode
var valid = false;
// If invalid then do some error processing
if (!valid)
{
// Redirect to errro page....
filterContext.Result = new RedirectToRouteResult("NameOfErrorRoute");
return;
}
base.OnActionExecuting(filterContext);
}
}
You can then either use this on specific controllers / actions as an attribute:
[HashCodeCheckFilter]
public class HomeController : Controller
{
// GET: Home
[HashCodeCheckFilter]
public ActionResult Index()
{
return View();
}
}
Or you can register it in Application_Start (Or App_Start/FilterConfig) as a global filter that applies to all requests:
GlobalFilters.Filters.Add(new HashCodeCheckFilterAttribute());
If you want to not check if the request is an ajax request you can check the HTTP_X_REQUESTED_WITH header on the request to see if it equals xmlhttprequest.
is there a way that I can check if the HASHCODE is valid in global.asax and redirect the user to error page from there
I would use a HttpHandler which is better suitable for this. (See documentation here and here and step-by-step tutorial here)
Also please tell me if there is a way to check if I can bypass this hashcode for Ajax requests in the application.
You could check if the HttpContext.Current.Request.Headers["x-requested-with"] is XMLHttpRequest. Then it is an AJAX call and you could skip the authentication step. Additionally, you could set a session variable when receiving the hashcode for the first time and check if that hashcode is still valid when the AJAX request is done. Then you will have a better security then just not checking it.
It sounds to me like you should be looking at implementing an IAuthorizationFilter rather than trying to use Global.asax. An implementation of IAuthorizationFilter will run before any request and you can choose what to do if the hashcode in invalid.
Since you're using asp.net MVC I would look at global action filters within your application. You can register an action filter as a global action filter through your global.asax.
GlobalFilters.Filters.Add(new MyActionFilterAttribute());
Within your action filter you can put the code to check for your hash, this will get executed each time any of your actions are called on the controllers.
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(!filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
{
// CHECK HASH HERE
}
base.OnActionExecuting(filterContext);
}
}
Check out these resources for more information
http://weblogs.asp.net/gunnarpeipman/asp-net-mvc-3-global-action-filters
https://msdn.microsoft.com/en-us/library/system.web.mvc.globalfiltercollection(v=vs.98).aspx
usually, when I look at a ASP.Net MVC application, the Route table gets configured at startup and is not touched ever after.
I have a couple of questions on that but they are closely related to each other:
Is it possible to change the route table at runtime?
How would/should I avoid threading issues?
Is there maybe a better way to provide a dynamic URL? I know that IDs etc. can appear in the URL but can't see how this could be applicable in what I want to achieve.
How can I avoid that, even though I have the default controller/action route defined, that default route doesn't work for a specific combination, e.g. the "Post" action on the "Comments" controller is not available through the default route?
Background: Comment Spammers usually grab the posting URL from the website and then don't bother to go through the website anymore to do their automated spamming. If I regularly modify my post URL to some random one, spammers would have to go back to the site and find the correct post URL to try spamming. If that URL changes constantly I'd think that that could make the spammers' work more tedious, which should usually mean that they give up on the affected URL.
I would consider to implement my own IRouteHandler and put some custom logic in my custom ControllerActionInvoker. How it would work ? The route table wouldn't dynamically change but you could check in your custom ControllerActionInvoker for a random parameter in the route path and invoke or not the corresponding action.
My route :
routes.Add
(
new Route
(
"blog/comment/{*data}",
new RouteValueDictionary(new {controller = "blog", action = "comment", data = ""}),
new MyRouteHandler()
)
);
My I route handler :
class MyRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new MyHttpHandler(requestContext);
}
}`
My handler :
class MyHttpHandler : MvcHandler
{
public MyHttpHandler(RequestContext requestContext) : base(requestContext)
{
}
protected override void ProcessRequest(HttpContextBase httpContext)
{
IController controller = new BlogController();
(controller as Controller).ActionInvoker = new MyActionInvoker();
controller.Execute(RequestContext);
} }`
and my action ivoker where the custom logic for handling an action or not should be coded :
class MyActionInvoker : ControllerActionInvoker
{
protected override ActionResult InvokeActionMethod(MethodInfo methodInfo, IDictionary<string, object> parameters)
{
var data = ControllerContext.RouteData.GetRequiredString("data");
// put my custom logic to check whetever I'll handle the action or not. The data could be a parameter in the database for that purpose.
return base.InvokeActionMethod(methodInfo, parameters);
}
}
I don't know it it's the best solution but for now it's the one that comes to my mind.
Considering the actual problem background, the usual approach is to include a dynamically created transaction number. It should be stored in a hidden form field as well as in the server side session dictionary and only be valid for exactly one request.
I think today a lot of frameworks provide such a security mechanism; whereas this attack type is known as Cross-Site-Request-Forgery (csrf).