PRG BestPractice Zf2 - zend-framework2

i have an best practice question.
Its clear what Post/Redirect/Get does, but what will be the bestpractice to handle them?
I think there are 2 methods to handle them.
1.) We call the prg plugin at first on controller action
2.) We first validate the post data, and only redirect to the prg-response if successfull?
My problem about this is, at
1.) We enlarge the response time because of the redirect, this is by default so i think not the best solution
2.) will create an overhead by the every time validation of the form
What did you mean is the better solution aber this case?
regards
UPDATE:
What i mean is, the normal(standard) case is something like this - http://framework.zend.com/manual/2.0/en/modules/zend.mvc.plugins.html#the-post-redirect-get-plugin.
$prg = $this->prg('url');
if ($prg instanceof Response) {
return $prg;
} elseif ($prg === false) {
return new ViewModel(array(...));
}
$form->setData($prg);
This means, that theres after every form submit an redirect executes.
Now, my idea was something like this:
$prg = $this->prg();
$form = $this->getFormLogin();
$data = ($prg instanceof ResponseInterface)
? $this->getRequest()->getPost()
: $prg;
if (false !== $data) {
$form->setData($data);
if (true === $form->isValid()) {
if ($prg instanceOf ResponseInterface) {
return $prg;
}
// Make something within the loginservice or something else
}
The idea behind this was, to only redirect for the PRG only if the form is valid, to save response time and other things (because of bootstrapping settings etc.)

The Zend Framework is designed based on Front-Controller pattern so its essential to redirect the page when you access different resources(controller-action).
moreover when you fire redirect(URL) function from your source code it takes minimal time when you compared the time to access the same(URL) from your browser.
You could reduce the response time to considerable amount when you use classmap_autoloading.
Updated:
for an example i take login process, in the below code i implement both HTTP get and post methods in the same action() but, you can refactor this function based on HTTP methods.
LoginController.php
public function loginAction()
{
//code
if ($request->isPost()) {
//code
if ($isValid) {
return $this->redirect()->toUrl('urUrl');
}
return $this->redirect()->toUrl('urUrl');
}
//code
return $viewModel;
}
After refactoring above code
//it used for HTTP get
public function loginAction()
{
//code
return $viewModel;
}
//it used for HTTP POST
public function loginPostAction()
{
//code
if ($notValid) {
return $this->redirect()->toUrl('urUrl');
}
$viewModel->setTemplate($viewpath);
return $viewModel;
}
You need to modify your routing configuration in such a way to handle for both HTTP get and post methods. if the request is HTTP-get the controller process the loginAction() but if its HTTP-post it process the loginPostAction()
Zend framework 2 - HTTP method Routing
Updated:
The purpose of plugin is to avoid the user to POST the data again to the browser. In your case you are trying to enable the option to POST their data when the form is not valid (you are trying to change the behaviour of PRG plugin). if you really worried about response time don't use PRG plugin. create your custom logic inside your controller-action.
--SJ

Related

ValidateInputAttribute not working in Post Request of ASP.NET MVC controller

My understanding was OOTB, MVC will validate input to prevent XSS Attack and SQL Injection.
For example, In one of my app, the "a dangerous input has been detected" error will be received when I put in HTTP Get request. However, the post actions can let these values posted successfully through html input element without error. Even after I marked the controller action as [ValidateInput(true)]. How can I make them validate those post input?
Any advice will be appreciated!
Without seeing your GET handler, or what you're sending to it, it's tough to say why it behaves that way. However, OOTB MVC guards against SQL injection through the use of Entity Framework, and against XSS through ModelState validation.
Inside the body of your POST action that handles this forms submission you'll want to use code much like the following:
if (ModelState.IsValid)
{
//do the stuff I want to do when things are valid and free of XSS
}
else
{
//something went wrong. Probably shouldn't process this one. Have the user try again
}
Update: please disregard my filthy lies. ValidateInput(true) is not necessary because it is on by default. So, the only things I can think of would be that you have the AllowHtml attribute on your class or properties, or you are not posting back a model for modelBinding, and therefore input validation, to occur. At this point, you're probably going to need to put up some code for further help. There's too many unknowns right now.
I ran into a similar issue - we had JQuery using $.ajax to post JSON to the MVC action. The default model binder does not validate posted JSON allowing unsafe XSS to be posted against our action.
To solve this, I found the RequestValidator has a static method InvokeIsValidRequestString that allowed
public class ValidateJsonXssAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.HttpContext?.Request;
if (request != null && "application/json".Equals(request.ContentType, StringComparison.OrdinalIgnoreCase))
{
if (request.ContentLength > 0 && request.Form.Count == 0) //
{
if (request.InputStream.Position > 0)
request.InputStream.Position = 0; // InputStream has already been read once from "ProcessRequest"
using (var reader = new StreamReader(request.InputStream))
{
var postedContent = reader.ReadToEnd(); // Get posted JSON content
var isValid = RequestValidator.Current.InvokeIsValidRequestString(HttpContext.Current, postedContent,
RequestValidationSource.Form, "postedJson", out var failureIndex); // Invoke XSS validation
if (!isValid) // Not valid, so throw request validation exception
throw new HttpRequestValidationException("Potentially unsafe input detected");
}
}
}
}
}
Then, you can just decorate relevant MVC actions expecting JSON-posted data that might bypass the standard XSS prevention:
[HttpPost]
[ValidateJsonXss]
public ActionResult PublishRecord(RecordViewModel vm) { ... }
You can see other options for customizing request validation with OWASP .NET recommendations by extending the RequestValidator object, which exposes the string validation done by the ValidateInput automatically utilized by MVC for other scenarios of query string, form collection, and cookie values.
For more info: https://www.owasp.org/index.php/ASP.NET_Request_Validation

Interrupting application flow from a non-MVC event

I'm working on an application which uses a REST API backend. This API has a login step which creates a token used for all subsequent API requests. I store this token in the auth storage, and I have an event hook that checks if the user is logged in, and if not, renders the login page:
$eventManager->attach(MvcEvent::EVENT_ROUTE, function($e) use ($view, $auth) {
$match = $e->getRouteMatch();
// No route match, this is a 404
if (!$match instanceof RouteMatch) {
return;
}
// Route is whitelisted
$matchedRoute = $match->getMatchedRouteName();
if (in_array($matchedRoute, array('login'))) {
return;
}
// if they're logged in, all is good
if ($auth->hasIdentity()) {
return true;
}
[render login form and return response object]
}, -100);
This works great.
The API also sometimes expires the login tokens in a way that I can't easily predict, which means all API calls will return a 'Session expired' type error. I've written an event trigger after the API calls that I can hook into. I want to check for these 'session expired' responses and somehow render the login page in the same way I do above:
$events->attach('Api', 'call', function ($e) {
$api = $e->getTarget();
$params = $e->getParams();
$result = $params['apiResult'];
if ([result is a session expired response]) {
// what can I do here?
}
}, 999);
but since this isn't an MVC event, even if I could access the response object here, returning it wouldn't do anything. What would be the best way to interrupt the application flow in a non-MVC event?
I'm not sure but I'm assuming that your API events do occur in a dedicated EventManager instance (so your API may be an implementation of EventManagerAwareInterface) and not in the MVC one (which is the one you grab from the Zend\Mvc\Application instance).
If that's the case, you could inject both the main EventManager and the MvcEvent inside your API, and then short circuit the MVC cycle from the call listener.
I.e. assume your dependencies are in $mvcEvent and $mvcEventManager properties with getters, this is how you would listen for the call event:
$events->attach('call', function($e) {
$api = $e->getTarget();
$params = $e->getParams();
$result = $params['apiResult'];
if ([result is a session expired response]) {
$mvcEvent = $api->getMvcEvent();
$mvcEvent->setError('api error');
$mvcEvent->setParam('exception', new \Exception('Session expired'));
$api->getMvcEventManager()->trigger('dispatch.error', $mvcEvent);
}
}, 999);
There are better ways to do it for sure, choosing the best will depend on the architecture of your API class.
You could use Zend\EventManager\ResponseCollection returned by your trigger, rather than using the MVC event inside the listener; that would enable your API event cycle to continue even if some error occurs. That's actually how Zend\Mvc\Application uses its own event manager in the run() method, so you can peek at that for an example.

Redirect after EndInvoke in ASP.NET MVC

Hi have the following code on my view (JQuery):
$.post('<%=Url.Content("~/Customer/StartLongRunningProcess")%>');
Wich invokes an asynchronous call (C#):
public void StartLongRunningProcess()
{
ProcessTask processTask = new ProcessTask();
processTask.BeginInvoke(new AsyncCallback(EndLongRunningProcess), processTask);
}
Finally, the result of the call:
public void EndLongRunningProcess(IAsyncResult result)
{
ProcessTask processTask = (ProcessTask)result.AsyncState;
string id = processTask.EndInvoke(result);
RedirectToAction("~/SubscriptionList/SubscribedIndex/" + id);
}
The redirect is ignored. Response.Redirect also fails, since the HTTP headers has been already sent. I've tried change the window.location from javascript, this works, but I'm unable to pass the parameter id by ViewData. Any idea to resolve this?
Are you sure the headers have already been sent? I'm not really up on asynchronous controllers, but I would doubt that it would start sending any headers right away. My first thought would be that a redirect response to an ajax call isn't handled by the browser. You will probably need to implement some logic that sends back a result with the URL and have your success delegate in jQuery look for that piece of data and then do the redirect through javascript (i.e. window.location).
HTH

In Asp.Net MVC 2 is there a better way to return 401 status codes without getting an auth redirect

I have a portion of my site that has a lightweight xml/json REST API. Most of my site is behind forms auth but only some of my API actions require authentication.
I have a custom AuthorizeAttribute for my API that I use to check for certain permissions and when it fails it results in a 401. All is good, except since I'm using forms auth, Asp.net conveniently converts that into a 302 redirect to my login page.
I've seen some previous questions that seem a bit hackish to either return a 403 instead or to put some logic in the global.asax protected void Application_EndRequest()
that will essentially convert 302 to 401 where it meets whatever criteria.
Previous Question
Previous Question 2
What I'm doing now is sort of like one of the questions, but instead of checking the Application_EndRequest() for a 302 I make my authorize attribute return 666 which indicates to me that I need to set this to a 401.
Here is my code:
protected void Application_EndRequest()
{
if (Context.Response.StatusCode == MyAuthAttribute.AUTHORIZATION_FAILED_STATUS)
{
//check for 666 - status code of hidden 401
Context.Response.StatusCode = 401;
}
}
Even though this works, my question is there something in Asp.net MVC 2 that would prevent me from having to do this? Or, in general is there a better way? I would think this would come up a lot for anyone doing REST api's or just people that do ajax requests in their controllers. The last thing you want is to do a request and get the content of a login page instead of json.
How about decorating your controller/actions with a custom filter:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class RequiresAuthenticationAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
var user = filterContext.HttpContext.User;
if (!user.Identity.IsAuthenticated)
{
filterContext.HttpContext.Response.StatusCode = 401;
filterContext.HttpContext.Response.End();
}
}
}
and in your controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[RequiresAuthentication]
public ActionResult AuthenticatedIndex()
{
return View();
}
}
Another way of doing this is to implement a custom ActionResult. In my case, I wanted one anyway, since I wanted a simple way of sending data with custom headers and response codes (for a REST API.) I found the idea of doing a DelegatingActionResult and simply added to it a call to Response.End(). Here's the result:
public class DelegatingActionResult : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
Command(context);
// prevent ASP.Net from hijacking our headers
context.HttpContext.Response.End();
}
private readonly Action<ControllerContext> Command;
public DelegatingActionResult(Action<ControllerContext> command)
{
if (command == null)
throw new ArgumentNullException("command");
Command = command;
}
}
The simplest and cleanest solution I've found for this is to register a callback with the jQuery.ajaxSuccess() event and check for the "X-AspNetMvc-Version" response header.
Every jQuery Ajax request in my app is handled by Mvc so if the header is missing I know my request has been redirected to the login page, and I simply reload the page for a top-level redirect:
$(document).ajaxSuccess(function(event, XMLHttpRequest, ajaxOptions) {
// if request returns non MVC page reload because this means the user
// session has expired
var mvcHeaderName = "X-AspNetMvc-Version";
var mvcHeaderValue = XMLHttpRequest.getResponseHeader(mvcHeaderName);
if (!mvcHeaderValue) {
location.reload();
}
});
The page reload may cause some Javascript errors (depending on what you're doing with the Ajax response) but in most cases where debugging is off the user will never see these.
If you don't want to use the built-in header I'm sure you could easily add a custom one and follow the same pattern.
TurnOffTheRedirectionAtIIS
From MSDN, This article explains how to avoid the redirection of 401 responses : ).
Citing:
Using the IIS Manager, right-click the
WinLogin.aspx file, click Properties,
and then go to the Custom Errors tab
to Edit the various 401 errors and
assign a custom redirection.
Unfortunately, this redirection must
be a static fileā€”it will not process
an ASP.NET page. My solution is to
redirect to a static Redirect401.htm
file, with the full physical path,
which contains javascript, or a
meta-tag, to redirect to the real
ASP.NET logon form, named
WebLogin.aspx. Note that you lose the
original ReturnUrl in these
redirections, since the IIS error
redirection required a static html
file with nothing dynamic, so you will
have to handle this later.
Hope it helps you.
I'm still using the end request technique, so I thought I would make that the answer, but really
either of the options listed here are generally what I would say are the best answers so far.
protected void Application_EndRequest()
{
if (Context.Response.StatusCode == MyAuthAttribute.AUTHORIZATION_FAILED_STATUS)
{
//check for 666 - status code of hidden 401
Context.Response.StatusCode = 401;
}
}

Is it possible to implement X-HTTP-Method-Override in ASP.NET MVC?

I'm implementing a prototype of a RESTful API using ASP.NET MVC and apart from the odd bug here and there I've achieve all the requirements I set out at the start, apart from callers being able to use the X-HTTP-Method-Override custom header to override the HTTP method.
What I'd like is that the following request...
GET /someresource/123 HTTP/1.1
X-HTTP-Method-Override: DELETE
...would be dispatched to my controller method that implements the DELETE functionality rather than the GET functionality for that action (assuming that there are multiple methods implementing the action, and that they are marked with different [AcceptVerbs] attributes). So, given the following two methods, I would like the above request to be dispatched to the second one:
[ActionName("someresource")]
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult GetSomeResource(int id) { /* ... */ }
[ActionName("someresource")]
[AcceptVerbs(HttpVerbs.Delete)]
public ActionResult DeleteSomeResource(int id) { /* ... */ }
Does anybody know if this is possible? And how much work would it be to do so...?
You won't be able to use the [AcceptVerbs] attribute as-is since it's tied to the request's actual HTTP verb. Fortunately the [AcceptVerbs] attribute is very simple; you can see the source for yourself at http://www.codeplex.com/aspnet/SourceControl/changeset/view/21528#266431.
In short, subclass AcceptsVerbsAttribute and override the IsValidForRequest() method. The implementation would be something like the following:
string incomingVerb = controllerContext.HttpContext.Request.Headers["X-HTTP-Method-Override"] ?? controllerContext.HttpContext.Request.Method;
return Verbs.Contains(incomingVerb, StringComparer.OrdinalIgnoreCase);
Levi's answer is great. Additionally, I added a check in the custom AcceptsVerbsAttribute that also examines the FORM collection, so you can simply put a hidden input to trigger the DELETE (similar to MVC 2's Html.HttpMethodOverride(HttpVerbs.Delete)).
<input name="X-HTTP-Method-Override" type="hidden" value="DELETE" />
Change the incomingVerb assignment to:
string incomingVerb = controllerContext.HttpContext.Request.Headers["X-HTTP-Method-Override"] ?? controllerContext.HttpContext.Request.Form["X-HTTP-Method-Override"] ??controllerContext.HttpContext.Request.HttpMethod;
Be careful with this approach! See a related post by Stephen Walther.
Hopefully this helps someone.
Insert to Form:
<%= Html.HttpMethodOverride(HttpVerbs.Delete) %>
This conversation is a bit old, but I wanted to share what I have found using mvc 2:
Browsers support two HTTP verbs: GET and POST, but ASP.NET MVC 2 allows you to simulate Put, Get, and Delete using Html.HttpMethodOverride helper method. Internally, this works by sending the verb in an X-HTTP-Method-Override form field. The behavior of HttpMethodOverride is used by the [AcceptVerbs] attribute as well as the new shorter verb attributes:
For example, the action declaration:
[ActionName("someresource")]
[HttpDelete]
public ActionResult DeleteSomeResource()
should take responsibility for your get request that has the X-HTTP-Method-Override set to Delete.
I'm surprised that this hasn't been mentioned yet, but ASP.NET MVC natively supports X-HTTP-Method-Override and has been doing so from at least version 2. There's no need to write custom code to handle this.
It work in the following way:
Inside AcceptVerbsAttribute (also proxied by [HttpPut], [HttpPost], etc), there's an IsValidForRequest method. Inside that method, it checks with Request.GetHttpMethodOverride(), which returns the proper overriden HTTP method with the following conditions:
Overriding is only supported in POST requests. All others are ignored.
If the X-HTTP-Method-Override value is GET or POST, it's ignored. This makes sense, as you'd never need to override with these values.
It looks for X-HTTP-Method-Override in the following places in this priority:
1) HTTP Header
2) Form Body
3) Query String
If you're really curious, here's how GetHttpMethodOverride() looks (from MVC 3's source code):
public static class HttpRequestExtensions {
internal const string XHttpMethodOverrideKey = "X-HTTP-Method-Override";
public static string GetHttpMethodOverride(this HttpRequestBase request) {
if (request == null) {
throw new ArgumentNullException("request");
}
string incomingVerb = request.HttpMethod;
if (!String.Equals(incomingVerb, "POST", StringComparison.OrdinalIgnoreCase)) {
return incomingVerb;
}
string verbOverride = null;
string headerOverrideValue = request.Headers[XHttpMethodOverrideKey];
if (!String.IsNullOrEmpty(headerOverrideValue)) {
verbOverride = headerOverrideValue;
}
else {
string formOverrideValue = request.Form[XHttpMethodOverrideKey];
if (!String.IsNullOrEmpty(formOverrideValue)) {
verbOverride = formOverrideValue;
}
else {
string queryStringOverrideValue = request.QueryString[XHttpMethodOverrideKey];
if (!String.IsNullOrEmpty(queryStringOverrideValue)) {
verbOverride = queryStringOverrideValue;
}
}
}
if (verbOverride != null) {
if (!String.Equals(verbOverride, "GET", StringComparison.OrdinalIgnoreCase) &&
!String.Equals(verbOverride, "POST", StringComparison.OrdinalIgnoreCase)) {
incomingVerb = verbOverride;
}
}
return incomingVerb;
}
}
Have you looked at Simply Restful Routing? It already does this.
Edited Feb 2010 to add: Method overrides are built into MVC 2.
The X-HTTP-Method-Override is a custom header and most likely isn't supported by your web container.
Are you calling this from a web page? If so, you should probably use XmlHttpRequest with DELETE (or whatever verb you want). Better yet, use a JS framework to do the heavy lifting for you.
You could create an ActionFilter that implements OnActionExecuting, which fires before the controller action is invoked. You could then interrogate the request headers, and redirect based on the value of the X-HTTP-Method-Override header, when present.

Resources