I am trying to understand Struts2 Interceptors. So please excuse if my questions are dumb.
I guess that interceptors are specific to an action class (that means before calling a specific action class, they get invoked).
For example:
<action name="test" class="com.jranch.Test">
<interceptor-ref name="GuiStackā/>
<result name="success">/secure.jsp</result>
</action>
My question is: Assume a scenario where pictures in a website must be protected from unauthorized access (Means if the user directly enters an URL in the browser, they should not be allowed to see the pictures until they are logged in).
My view is that if its related to Servlet Filters, I can write a simple filter by putting url-pattern tag to /* to check all requests. Can Struts2 interceptors handle this as I guess they are specific to action class?
Please correct me if i am wrong.
Well As Steven told Interceptors are not specific to any Action, they in fact are the core of Struts2 framework
Interceptors are a set of reusable components.In all cases they are Applied to a request processing cycle which includes from Exception Handling to Role handling.
Its very trivial use case when one will write a Interceptor for a particular Action.
Use case you are talking about can be handled by Interceptor where for each request for a particular resources can be first intercepted by the Interceptor and based on out custom criteria whom to allow access we either forward the request down the calling stack or can reject the request.
public String intercept (ActionInvocation invocation) throws Exception {
final ActionContext context = invocation.getInvocationContext ();
Map<String, Object> session = ActionContext.getContext().getSession();
Object user = session.getAttribute (USER_HANDLE);
if (user == null) {
//Any processing
return "login"; //User is not logged in so ask him/her to login
} else {
return invocation.invoke (); //user is trusted one let her go ahead
}
}
Interceptors aren't necessarily specific to an action -- in fact, in most cases, they're applied to many actions or globally to all actions (very similar a servlet filter).
This answer talks about how to use an interceptor for authentication in a Struts2 application.
Related
I am developing an application in asp.net MVC3 and I have the following questions:
When should I write an HTTP module and when should I write an action filter?
Filter are more MVC approach of doing thing whereas Http Module are more of ASP.NET way of doing thing. Both serve similar purpose by providing hook in the processing pipline.
HttpModule is more generic and when you want some thing to be processed on every request. Filters are useful for adding action specific behaviour.
If you want some thing to be executed only once per Http Request, you should use an HttpModule. ActionFilter may get executed several times during a request until and unless you check IsChildActionOn.
HttpModule are called before and after the request handler executes. They are intended to enable a developer to intercept, participate, or modify each request. There are 22 available events that can be subscribed to that enables the module to work on the request in various stages of the process. The events are useful for page developers who want to run code when key request pipeline events are raised. They are also useful if you are developing a custom module and you want the module to be invoked for all requests to the pipeline.
Filters are designed to inject logic in between MVC request life cycle. Specifically before and after de action is invoked, as well as, before and after the result is processed. Filters provide users with powerful ways to inspect, analyze, capture and instruments several things going around within MVC projects. As of MVC5, there are 5 types of filters :
Authentication
Authorization
Action
Result
Exception
So if you want to intercept, participate, or modify in a specific of the 22 events in the http request pipeline choose the modules. If your logic is is strictly related to the action method you better server overriding one of the following ActionFilterAttribute methods:
OnActionExecuting
OnActionExecutted
OnResultExecuting
OnResultExecuted
HttpModule is how IIS allows an Web application to override the default behavior or add custom logic by letting you attach event handlers to HttpApplication events.
Different IIS modes (Integrated or Classic) even use has different Web.config settings.Reference:
http://msdn.microsoft.com/en-us/library/ms227673(v=vs.100).aspx
Example: redirect non-www to www URLs
public void Init(HttpApplication application)
{
application.PreRequestHandlerExecute += this.Application_PreRequestHandlerExecute;
}
private void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
Uri requestUrl = HttpContext.Current.Request.Url;
string host = requestUrl.Authority.ToLower();
if (!host.StartsWith("www"))
{
HttpContext.Current.Response.Redirect(requestUrl.Scheme + "://www." + host + requestUrl.PathAndQuery);
HttpContext.Current.Response.End();
}
}
An Action Filter is an attribute decorating controllers or action methods. It is an abstraction layer between MVC routing and action methods. With action filters, we can apply same logic to multiple controllers or action methods. for example, custom logging.
I'm working on a system that needs to know a user's choice before they enter a site. Up till now the choice has been stored in a cookie and checked by JavaScript on the page load - if the cookie doesn't exist then a dialog is shown and the user makes the choice.
Although we'd normally expect the user to arrive at the homepage of the application, they can legally follow a URL to any page within the application, so the JavaScript to check the choice exists on every page.
This has caused problems (almost always fixed by clearing cookies) so we're switching to store the choice in the database. What we need is a neat way of making sure that all pages (MVC and Web Forms) check that the choice has been made and, if it hasn't, either display a dialog or redirect to a page where the choice can be made.
The main thing bothering me is that to cause a redirect using MVC, I need to return a RedirectResult, and this can only be done from an Action. I don't want every action to have code regarding this check - it seems like the kind of thing that should be possible from a base controller (in the same way a base page could cause a Response.Redirect.
Can anyone suggest a good way for all pages to perform a check on the database and then either cause a redirect or show a dialog?
The main thing bothering me is that to cause a redirect using MVC, I
need to return a RedirectResult, and this can only be done from an
Action.
Oh not at all. You could also redirect from a custom action filters.
For example you could write a custom IAuthorizationFilter that will check whether the user made the necessary choice and if not redirect to some given page. The check could be done against a cookie, database or wherever you decide to persist this information:
public class EnsureChoiceHasBeenMadeAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
// get the current user
var user = filterContext.HttpContext.User;
if (user.Identity.IsAuthenticated && !UserMadeAChoice(user.Identity.Name))
{
// if the current user is authenticated and he didn't made a choice
// redirect him to some page without even attempting to execute
// the controller action that he requested
var values = new RouteValueDictionary(new
{
controller = "home",
action = "index"
});
filterContext.Result = new RedirectToRouteResult(values);
}
}
private bool UserMadeAChoice(string username)
{
throw new NotImplementedException();
}
}
Now you have different possibilities:
You decorate the controllers/actions that you want to perform this check with the [EnsureChoiceHasBeenMade] attribute
You register the action filter as a global action filter so that it applies to absolutely all actions
You write a custom filter provider in order to dynamically apply the action filter to some actions based on some dynamic values (you have access to the HttpContext).
I need to avoid double POST on refresh. So I'm using POST/Redirect/GET pattern (faces-redirect=true) and navigation handler (by #BalusC) like in this post. How to use that pattern in JSF 2.0 when action method return null (stay on the same page) ? Is it even possible or i need to use something other that POST/Redirect/GET ? I need to keep alive a view-scoped bean, so returning in action method the same view (PRG works) is not a solution (causes lost view scope).
view test.xhtml:
<h:commandButton action="#{bean.send}"/>
bean:
#ManagedBean
#ViewScoped
class Bean {
String send() {
// do something...
return null;
}
String send2() {
// do something...
return "test"; // view scope lost
}
}
Marioosh.
Post/Redirect/Get causes two lifecycle scenarios:
The redirect causes a Non-Faces Response (Section 2.1 of the JSF-2 spec).
Get causes a Non-faces request with a Faces Response (Section 2.1.1). A new view is created in this scenario.
some frameworks do implement this strategy by default: for example Spring WebFlow. Each Submit (POST) is followed by a redirect GET with a parameter "execution" enabling Spring to find the SAME view.
for example :
http://myhost:8080/booking-spring-faces/spring/mypage?execution=e1s2
I would love to find this feature in JSF toolkits like Omnifaces from BalusC !
I have a Grails WebFlow that is similar to the following example:
def myFlow = {
init {
action {
def domain = MyDomain.get(params.id)
flow.domain = domain ? domain : new MyDomain()
}
on('success').to 'first'
}
first {
on('continue') {
flow.domain.properties = params
if(!flow.domain.validate()) {
return error()
}
}.to 'second'
}
...
}
Given this example, if a validation error occurs in the transition on('continue') in first:
What's the preferred way to set the model with the invalid domain object so I can use a <g:hasErrors>... in my view (like I would in a normal controller action)?
When I call error(), does it send the flow back to init or to first?
Does error() take any arguments (i.e. a model) that can be used for what I'm trying to accomplish (I can't find much documentation on the error() method).
I'd also take suggestions on how I could improve my flow states to better-facilitate handling these validation errors.
Summary: What's the preferred way to render validation errors within a Grails Web Flow?
-1
What's the preferred way to set the
model with the invalid domain object
so I can use a ... in my
view (like I would in a normal
controller action)?
You just need to return your domain object that has the errors. you can do that in an
action state
action {
user.validate()
return [user:user]
}
You can also set your errors in flash scope. On each transition Grails will copy the content of the flash scope into the ModelView and thus available in your gsp page
action {
flash.error = "your message"
}
-2
When I call error(), does it send the flow back to init or to
first? When you call error it call the
transition you defined for
You should define a handler for such as
on("error").to("handlerError")
Does error() take any arguments (i.e. a model) that can be used for
what I'm trying to accomplish (I can't
find much documentation on the error()
method).
I don't think so but you can do the following to set any variable when transitioning from one state to another
on("error") {
// do Something
}.to("handlerError")
3-
I'd also take suggestions on how I could improve my flow states to
better-facilitate handling these
validation errors.
I use flash for global and form errors but i needed ONE way to deal it. Currently with Grails the flash scope is managed differently in a flow than it's managed in a normal action. So I decided to write a little plugin to change the way the flash scope is handled in a flow and make it consistent with the way is managed in a normal action.
I a gsp page i can use my tags in the follwing way regardless of the action type (normal or flow)
<message:global />
or
<message:inline />
As for Form fields errors, i didn't like to deal with errors in domain objects. I wanted something more unified. So I decided to make them part of the http protocol and I have a javascript component the inject them into the form if i opt to. I found this solution much cleaner than dealing with g:errors each time.
-ken
I've found that one way to do it is to specifically invoke render() in the transition state, providing the model. Here's an example where only one field is validated:
first {
render(view: 'info', model: [flow.domain])
on('continue') {
if(!flow.domain.validate(['myField'])) {
return error()
}
}.to 'second'
}
One can then use <g:hasErrors> as follows:
<g:hasErrors bean="${domain}" field="myField">
<g:renderErrors bean="${domain}" as="list" field="myField"/>
</g:hasErrors>
Here's the situation...
Site 1) ASP.NET MVC application for customers to login, view and pay bills, etc. This site is using cookieless ASP sessions to allow multiple sessions to run in tabbed browsers.
Site 2) Corporate web site with standard static content. This will have a small form at the top of each page with a username/password form that will post to the MVC application.
When posting to the MVC app, a new session is being generated and the site is returning a 302 redirect with the session ID in the URL (as expected). The controller has two Login methods, one for handling a GET, one for handling a POST. Because of the redirect, it's hitting the GET method and it loses the form values.
By changing the method on the corporate site to do a form GET rather than a POST, the username and password are preserved in the query string after the redirect and I could handle the requests that way, but I would rather do a POST and not pass that data around in the URL.
My gut says that implementing some sort of custom HttpHandler would allow me to do this, but I'm not sure where I'd be able to tie into the session creation. A breakpoint at Session_Start in global.asax shows that the session ID has already been crated and the redirect already happened at that point.
I believe I found where the redirect was happening as well as a possible solution to it. The ISessionIDManager interface has a SaveSessionID method with an "out bool redirected" parameter on it. It appears that when a new session is created, the default session manager rewrites the URL inside this method and does a redirect. I played around with implementing my own ISessionIDManager and I was able to suppress the redirect, but there's no way (that I could tell) of inserting the session ID into the URL for the cookieless session without doing the redirect. Server.Transfer is not allowed at that point in the request lifecycle. I tried that but received an "Error Executing Child Request" message.
Later on, I found this article on Enabling POST in cookieless ASP.NET Applications. Basically by tying in to the Application_EndRequest event, you can detect the redirect, clear the response and fake a form post to the new URL that contains the session ID.
void MvcApplication_EndRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
if (application.Request.Form["CorpLogin"] == "1"
&& application.Response.RedirectLocation != null
&& application.Response.IsRequestBeingRedirected)
{
StringBuilder build = new StringBuilder();
build.Append("<html>\n<body>\n<form name='Redirect' method='post' action='");
build.Append(application.Response.RedirectLocation);
build.Append("' id='Redirect' >");
foreach (string key in application.Request.Form)
{
if (key != "CorpLogin")
build.Append(string.Format("\n<input type='hidden' name='{0}' value = '{1}'>", (string)key, application.Request.Form[(string)key]));
}
build.Append("\n<noscript><h2>Object moved <input type='submit' value='here'></h2></noscript>");
build.Append(#"</form>
<script language='javascript'>
<!--
document.Redirect.submit();
// -->
</script>
");
build.Append("</body></html>");
application.Response.Clear();
application.Response.ClearHeaders();
application.Response.Output.Flush();
application.Response.Write(build.ToString());
}
}
This isn't an ideal solution IMHO but it might be workable enough to meet the requirements.
When the MVC site issues the redirect are you handling that yourself with a controller or is it being routed automatically by your membership provider?
If you can intercept the request before the redirect you could place the username and password in the TempData variable and process it after the redirect.
You're looking for a Server.Transfer equivalent in MVC (see Simon's answer.)
It's like Request.Redirect but simply transfers the .NET processing to another page. It also allows for maintaining the form variables even on POST between transfers (in this case a RouteValueDictionary).
EDIT: (response to your comment #1)
Here is an example of Forms Authentication with MVC. Without knowing the internals of your authorization mechanism, I'm not entirely sure. At any point of the "DoLogin" process, you would be able to do a transfer instead of a redirect.