How to detect in a [HttpPost] handler method the view you came from? - asp.net-mvc-4

Considering this example:
public ViewResult View1()
{
return View();
}
public ViewResult View2()
{
return View();
}
[HttpPost]
public ActionResult Processor(SomeModel model)
{
if (comeFromView1)
{
}
//implementation
return RedirectToAction("View3");
}
Both View1 and View2 have inside a form that post to Processor.
How to detect inside it where did i come from?

One option would be to check Request.UrlReferrer. However, a user can easily spoof the referrer.
A better way would be an action filter which sets the previous action. Like this:
public class SavePreviousActionAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Session["PreviousAction"] = filterContext.RouteData["action"]
}
}
Add this to all actions by registering it as a global filter (in Global.asax):
GlobalFilters.Filters.Add(new SavePreviousActionAttribute());
And then access it in your action:
if (Session["PreviousAction"].ToString() == "View1")
{
// Came from view1
}

Related

FluentValidation with IActionFilter in Asp.net Core 2.1 [duplicate]

I have a logic to apply in case the request received is a BadRequest, to do this I have created a filter:
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
// Apply logic
}
}
}
In Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => { options.Filters.Add<ValidateModelAttribute>(); });
}
Controller:
[Route("api/[controller]")]
[ApiController]
public class VerifyController : ControllerBase
{
[Route("test")]
[HttpPost]
[ValidateModel]
public ActionResult<Guid> validationTest(PersonalInfo personalInfo)
{
return null;
}
}
Model:
public class PersonalInfo
{
public string FirstName { get; set; }
[RegularExpression("\\d{4}-?\\d{2}-?\\d{2}", ErrorMessage = "Date must be properly formatted according to ISO 8601")]
public string BirthDate { get; set; }
}
The thing is when I put a break point on the line:
if (!context.ModelState.IsValid)
execution reaches this line only if the request I send is valid. Why it is not passing the filter if I send a bad request?
The [ApiController] attribute that you've applied to your controller adds Automatic HTTP 400 Responses to the MVC pipeline, which means that your custom filter and action aren't executed if ModelState is invalid.
I see a few options for affecting how this works:
Remove the [ApiController] attribute
Although you can just remove the [ApiController] attribute, this would also cause the loss of some of the other features it provides, such as Binding source parameter inference.
Disable only the Automatic HTTP 400 Responses
Here's an example from the docs that shows how to disable just this feature:
services.AddControllers()
.ConfigureApiBehaviorOptions(options =>
{
// ...
options.SuppressModelStateInvalidFilter = true;
// ...
}
This code goes inside of your Startup's ConfigureServices method.
Customise the automatic response that gets generated
If you just want to provide a custom response to the caller, you can customise what gets returned. I've already described how this works in another answer, here.
An example of intersection for logging is describe in Log automatic 400 responses
Add configuration in Startup.ConfigureServices.
services.AddControllers()
.ConfigureApiBehaviorOptions(options =>
{
// To preserve the default behavior, capture the original delegate to call later.
var builtInFactory = options.InvalidModelStateResponseFactory;
options.InvalidModelStateResponseFactory = context =>
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<Startup>>();
// Perform logging here.
//E.g. logger.LogError($”{context.ModelState}”);
logger.LogWarning(context.ModelState.ModelStateErrorsToString());
// Invoke the default behavior, which produces a ValidationProblemDetails response.
// To produce a custom response, return a different implementation of IActionResult instead.
return builtInFactory(context);
};
});
public static String ModelStateErrorsToString(this ModelStateDictionary modelState)
{
IEnumerable<ModelError> allErrors = modelState.Values.SelectMany(v => v.Errors);
StringBuilder sb = new StringBuilder();
foreach (ModelError error in allErrors)
{
sb.AppendLine($"error {error.ErrorMessage} {error.Exception}");
}
return sb.ToString();
}
As the attribute filter in the life cycle of the .Net Core you can’t handle it. The filter layer with ModelState will run after the model binding.
You can handle it with .Net Core middleware as the following https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.1&tabs=aspnetcore2x
If you want to SuppressModelStateInvalidFilter on individual action, consider to use custom attribute suggested on https://learn.microsoft.com/en-us/answers/questions/297568/how-to-suppress-suppressmodelstateinvalidfilter-at.html. (And similar answer https://github.com/aspnet/Mvc/issues/8575)
public class SuppressModelStateInvalidFilterAttribute : Attribute, IActionModelConvention
{
private const string FilterTypeName = "ModelStateInvalidFilterFactory";
public void Apply(ActionModel action)
{
for (var i = 0; i < action.Filters.Count; i++)
{
//if (action.Filters[i] is ModelStateInvalidFilter)
if (action.Filters[i].GetType().Name == FilterTypeName)
{
action.Filters.RemoveAt(i);
break;
}
}
}
}
Example of use
[ApiController]
public class PersonController
{
[SuppressModelStateInvalidFilter]
public ActionResult<Person> Get() => new Person();
}

shared ViewBag data with different action in MVC4

public ActionResult Completed()
{
ViewBag.PersonalInfo="";
ViewBag.Experience="";
ViewBag.Work="";
}
[HttpPost]
public ActionResult Publication(MainPageModel obj)
{
ViewBag.PersonalInfo="";
ViewBag.Experience="";
ViewBag.Work="";
}
[HttpGet]
public ActionResult Publication(MainPageModel obj)
{
ViewBag.PersonalInfo="";
ViewBag.Experience="";
ViewBag.Work="";
}
I used this ViewBag with this three action but I want to used like method
like
public void methodname()
{
ViewBag.PersonalInfo="";
ViewBag.Experience="";
ViewBag.Work="";
}
and call in different action methodname()
how to do it MVC4?
Is it possible to write like method ?

Cookie values updated but returns old values

I have got a task to make Custom BreadCrumbs which will keep historical info of where the user got to the current point in an application. I have made a class for this purpose which Inherits from ActionFilterAttribute class and decorated the actions of the controller with that class, then in my OnActionExecuting override i save the historical view in a cookie and get the information out of the cookie in my customBreadCrumb partial view.
Moreover, my customBreadCrumb partial view is inside of another partail view which in turn is in the Layout page.
The problem is that every time the code executes it returns old cookie value. To be more specific it returns previous value in current view.
Here is my sample code.
public class CustomBreadCrumb : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
WriteCookie(string cValue);
base.OnActionExecuting(filterContext);
}
private void WriteCookie(string CookieValue)
{
HttpCookie breadCrumbCookie = new HttpCookie("MyCookie");
breadCrumbCookie.Value = CookieValue;
breadCrumbCookie.Expires = DateTime.Now.AddYears(1);
HttpContext.Current.Response.Cookies.Add(breadCrumbCookie);
}
public static string GetBreadCrumb()
{
if (HttpContext.Current.Request.Cookies["MyCookie"] != null)
{
return HttpContext.Current.Request.Cookies["MyCookie"].Value;
}
return string.Empty;
}
}
Here is my View
#{
#CustomBreadCrumb.GetBreadCrumbs()
}
Here is my sample controller
public class HomeController : BaseController
{
[CustomBreadCrumb]
public ActionResult Index(int ID = 1)
{
//My logic
}
}

How does one determine the route of an Web API 2.2 Action implemented in a base class?

Assume for a moment that I have an abstract controller
public abstract class ResourceController<TResource> : ApiController where TResource: Resource,new()
{
[Route("{id}")]
[HttpGet]
public async Task<IHttpActionResult> FindById([FromUri] string id)
{
TResource resource = null;
// go fetch the resource from a database or something
return Ok(resource)
}
[Route("")]
[HttpPost]
public async Task<IHttpActionResult> Create(TResource resource)
{
TResource resource = null;
// go create the resource or something
return CreatedAtRoute("XXXXX", new { id = resource.Id }, resource);
}
// more methods
}
[RoutePrefix("foo")]
public class FooResourceController : ResourceController<Foo>
{
}
[RoutePrefix("baa")]
public class BaaResourceController : ResourceController<Baa>
{
}
public class Resource
{
public string Id { get; set; }
// some other properties all resources shared
}
At this stage all the actions work, except for creating a new resource. Without overriding the Create method in every subclass, how do I find the correct route of the FindById of the respective controllers from the ResourceController Create method?
For example, if I create a foo resource with id 123 then it would return foo/123. If I created a resource baa with id 456, then it woulds return baa/456.
I'm unable to name the route using attributes, since only one can exist for the application.
Had the same problem. I fixed it by using the Created method in combination with the calling url.
This will only work if yout post doesn't have a dedicated template
My get:
[HttpGet("{id:int}")]
public async Task<IActionResult> GetAsync(int id)
{
try
{
var codetabel = await _reader.GetAsync(id);
var model = Mapper.Map<TCodeTabelModel>(codetabel);
return OkResult(model);
}
catch ....
}
And post:
[HttpPost()]
public async Task<IActionResult> InsertAsync(TCodeTabelModel model)
{
if (!ModelState.IsValid)
return BadRequestResult(ModelState);
try
{
var entity = Mapper.Map<TCodeTabelEntity>(model);
var insertedEntity = await _writer.InsertAsync(entity);
return Created($"{Request.Path.Value}/{insertedEntity.Id}" , Mapper.Map<TCodeTabelModel>(insertedEntity));
}
catch ....
}

MVC4 Custom OnActionExecuting Global.asx Filter is not being Triggered

I am trying to create a Global filter that will run for every action I have if the user is logged in. From what I have read there are two steps necessary. First, add the new filter within the Global.asx file.
public class MvcApplication : System.Web.HttpApplication
{
//I added this
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new NotificationFilter());
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
}
Then I have to create the filter itself in the filters folder.
public class NotificationFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{ //Breakpoint here is never triggered
//This code doesn't work, but it's what I want to do
if (WebSecurity.CurrentUserId > 0)
{
var notificationCount = db.Notifications.GroupBy(i => i.UserID).Count();
if (notificationCount > 99)
{
ViewBag.Notifications = "99+";
}
else
{
ViewBag.Notifications = notificationCount;
}
}
base.OnActionExecuting(filterContext);
}
}
How can I make this work? Is there a better way? I can add this to all the controllers and it works, that's just less than ideal.
I had the same experience. you can build a BaseController class and put the filter definition in it. Then all of your controllers must be inherited from BaseController class. So you don't have to use filter class in all controllers.
something like this:
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
...
}
}
In controllers:
public class SampleController : BaseController
{
...
}
you can use this code for your problem :
public class ParentController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
/* take current action*/
action = (string)filterContext.RouteData.Values["action"];
/* check if your action have your filter */
if (!string.IsNullOrEmpty(action) && Methods.
Where(
method => method.Name == action
&& method.GetCustomAttributes(typeof(/* your filter name */), true).Length > 0
).Count() > 0)
{
// do your work
}
}
}
and
public class YourController : ParentController
{
// implementation of your controller
}
Jed, from what I understand you want all your pages to behave the same way and render notifications for authenticated users.
I suggest, within your _layout, you place a top left Div and jquery Ajax call (you can push the jquery into an alert.js file later).
<div id='divNotifications'></div>
<script type="text/javascript">
$(document).ready(function() {
$('#divNotifications').load('Notifications/UserNotifications');
}
</script>
within your Notifications Controller have UserNotifications method returning :
public JsonResult AjaxUserNotifications()
{
Notifications Notifications = new GetNotifications();
return Json(Notifications, JsonRequestBehavior.AllowGet);
}
at first I recommend you make this render once as proof of functionality, then I suggest you make it a recurring call.
How to constantly check for new message with setInterval without constantly notifying user?
this stack thread goes into how to make Ajax calls repeat at time intervals.
One possible approach is to use little view and SignalR for push notifications.