Please note: Although I'm using the Grails Webflow plugin here, I am pretty sure this is just a generic question that any battle-weary Grails veteran could answer.
Grails 2.4.4 here. I have a need for a Grails Filter that inspects all traffic coming into a particular controller. If the app is in a particular state, I need to redirect traffic to another controller/action. Else, I need the filter to act as a no-op/pass-through filter (meaning it does nothing; just allows the request to pass through it and on to its intended destination):
package filters
class MyAppFilters {
def filters = {
fizzFilter(controller: 'fizz', action: '*') {
before = {
if(AppStateHolder.checkState() == AppState.Blue) {
redirect(controller: 'auth', action: 'unauthorized')
return false
} else {
// Allow request to continue on to its intended controller/action target ('no-op'/pass-through)
???
}
}
}
}
}
I have everything working perfectly for when the app state is AppState.Blue (so, the if-condition). The problem is that when app state isn't "blue", and that else executes, I keep getting infinite redirect errors. I believe Grails Webflow is complicating things, but I can't really fix anything with how it has been implemented.
I've also tried returning true/false from inside the else like so:
} else {
// Allow request to continue on to its intended controller/action target ('no-op'/pass-through)
return false // Also tried 'true'
}
But this produces the same infinite redirect errors. It looks like I need to do some kind of redirect/render/etc. inside this else or webflow will cause problems.
So I'm looking for a way to tell Grails to redirect to whatever was the controller/action off the request. Something like:
} else {
// Allow request to continue on to its intended controller/action target ('no-op'/pass-through)
redirect(controller: req.controller, action: req.action)
return false
}
Is this possible, if so, how (specifically)?
Related
I'm wondering if there's a way to do an internal redirect, re-route, or response forwarding inside Ktor.
call.respondRedirect("relative/url")
sends a HTTP 302 or 301 depending on the permantent: Boolean flag. I'm looking for something that would do the same without using HTTP, just internally in Ktor. Here's some pseudo-routing of what I want to achieve:
get("/foo") {
if (call.parameters["something"] != null) {
call.respondText("Yay, something!")
} else {
call.respondRedirect("/bar") // except without relying on client to handle HTTP redirects.
}
}
get("/bar") {
call.respondText("No thing :(")
}
The goal is that the client shouldn't make 2 requests, and shouldn't be aware of the redirection happening.
NB: I'm aware I can extract a function for /bar's body and invoke it, instead of responsdRedirect. However, I want to make Ktor handle it so that it goes through all the necessary lifecycle and pipeline with all the interceptors. This is to make sure it is handled as if it was an external request, except the network roundtrip.
I'm looking for something like Express.js' req.app.handle(req, res) as shown in the first half of this answer: https://stackoverflow.com/a/48790319/253468. A potential solution I couldn't understand yet is something like TestApplicationEngine.handleRequest (in io.ktor:ktor-server-test-host) is doing with pipeline.execute. I guess I could invoke call.application.execute(), the question is how to construct the ApplicationCall object then. Note this is for production use, so no TestApplicationCall.
You can do similar thing in Ktor by using call.application.execute function with cloned call object. For convenience let's define extension function for doing internal redirects:
suspend fun ApplicationCall.redirectInternally(path: String) {
val cp = object: RequestConnectionPoint by this.request.local {
override val uri: String = path
}
val req = object: ApplicationRequest by this.request {
override val local: RequestConnectionPoint = cp
}
val call = object: ApplicationCall by this {
override val request: ApplicationRequest = req
}
this.application.execute(call)
}
Here it creates a copy of an ApplicationCall object with the replaced path for a request. I use delegates to avoid boilerplate code. You can use redirectInternally function like this:
get("/foo") {
call.redirectInternally("/bar")
}
public function actionDone($id)
{
if ($model = $this->findModel($id)) {
$model["status"] = 3;
if ($model->save()) {
return $this->redirect(['test/index']);
}
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
It works only for the first time for each link. After that its just redirects to the 'test/index' without doing anything. Seems like browser (or smth else) remember, that if we open, for example, page site.com/?r=test/done&id=2 it should redirect to 'test/index' anyway.
Why is that? How can I fix it?
I even tried put die(); in the beginning of the method - anyway it redirects to 'test/index' until I use different link with another ID.
Thanks!
So i have asked about this before and have changed a lot of code around.
Spray Routing Doesn't match anything
Now I am executing my functions that return HTTPresponses insided detach() blocks so that i dont block. These then are completed and return to the client, but I still can't seem to get my routing to work.
In my tests, a request to a single slash works fine, but anything else, such as this create user path shown below fails. I can't seem to figure out why, and spray routing uses so many constructs I'm having a hard time figuring out how the system works well enough to find out whats happening.
I tried inserting logRequest blocks around certain paths thinking that might show me whats happening, but none of them seem to get hit. Any help would be greatly appreciated.
val route: Route = {
host("fakebook.com", "www.fakebook.com") {
pathSingleSlash {
complete("pong")
} ~
pathPrefix("users") { req =>
path("newuser") {
put {
detach() {
complete(genericPut(CreateUser(req.request)))
}
}
} ~
... rest of routing
And here is what my scalatests look like, the simple Put passes, but the put with newuser doesn't
val createUserSuccessRequest = Put(Uri("http://www.fakebook.com/users/newuser") withQuery(F_User.lastNameString -> "Doe", F_User.firstNameString -> "John", F_User.bioString -> "i like foobar",
F_User.ageString -> "42", F_User.dobString -> dateFormatter.format(new Date(1970 - 1900, 5, 7))))
"The FakeBook route" when {
"When sending a single slash request" should {
"respond with a simple pong" in {
Get() ~> logRequestResponse("plain get final request and response")(sealRoute(route)) ~> check {
assert(responseAs[String] == "pong")
}
}
}
"Running route to create a user with PUT" should {
"forward a message to the backbone to create a new user" in {
createUserSuccessRequest ~> logRequest("create user final request and response"){sealRoute(route)} ~> check {
expectMsg(CreateUser(createUserSuccessRequest))
}
}
}
}
For anyone else trying to solve this issue:
a lot of these directives actually DONT extract anything, so having the lambda inputs i have like req => and req2 => will not work.
It turns out, spray routing is designed so that you never have to touch the RequestContext as I have done with my functions (which is why I try to access it). They extract only the useful data. Rather than do things as I should and change my function signatures, i am going to (for now) do a hotfix that has worked.
if you absolutely must have the requestcontext, so long as you don't break it somehow, you can extract it by making your own extraction directive like so
val extractRequestContext = extract(x => x) and wrap your code in that
I did this
path("somepath") {
detach() {
extractRequestContext { request => complete(someFunc(request)) }
}
}
In the future I should learn to use the DSL more correctly and extract what I need from the request context using directives and pass THOSE to the functions
I am implementing the 'form_alter' hook in Drupal 7. I want to redirect the web to a specific node after deleting any node of type 'article'.
It seems that the proper way of doing so is:
function mymodule_form_alter(&$form, &$form_state, $form_id){
switch ($form_id){
case 'node_delete_confirm':
if($form['#node']->type == 'article'){
$form['actions']['submit']['#submit'][] = '_mymodule_redirect';
}
break;
}
}
function _mymodule_redirect($form, &$form_state){
$form_state['redirect'] = 'node/60';
}
When I put this code in my module it does redirect after confirming the node delete but the node is not actually deleted, if I go to the home page it is still alive!
If I remove the code the node is deleted as expected and the webpage is redirected to the frontpage as usual.
What am I doing wrong?
UPDATE: I forced the 'node_delete_confirm_submit' before the redirect action writing the following line before adding my redirect handler:
$form['actions']['submit']['#submit'][] = 'node_delete_confirm_submit';
This solves the problem.
The easiest way to accomplish this task (and not have to use a hook at all) would be to use the Rules module. It's a nice clean way of performing any number of actions on your site, and I know there's a rule for redirecting the user after content of a certain type is deleted.
Just for clarification I repeat the entire correct answer:
/**
* Implements hook_form_alter()
*/
function MYMODULE_form_alter(&$form, &$form_state, $form_id){
switch ($form_id) {
case 'node_delete_confirm':
// replace 'article' in next line with your node type machine name
if($form['#node']->type == 'article') {
$form['actions']['submit']['#submit'][] = 'node_delete_confirm_submit';
$form['actions']['submit']['#submit'][] = '_MYMODULE_redirect';
}
break;
}
}
function _MYODULE_redirect($form, &$form_state){
// replace 'node/123' in next line with node you like redirect to
$form_state['redirect'] = 'node/123';
}
Doing only $form['actions']['submit']['#submit'][] = '_MYMODULE_redirect'; was not enough. Still $form['actions']['submit']['#submit'][] = 'node_delete_confirm_submit'; needed to be triggered. Now the delete and the redirect are both triggered.
I want to redirect people to a certain page if the form validations fails. However, I can’t quite figure out how.
If i redirect people at the REDIRECT HERE comment below, it also redirects when it loads up the form and causes a endless loop.
public function create() {
$this->form_validation->set_rules('email_adress', 'E-mail', 'required|valid_email|is_unique[users.email_adress]');
if ($this->form_validation->run() !== FALSE) {
// PASSED
}
else {
// REDIRECT HERE
}
$this->load->view('user_register_view');
}
How can I achieve this?
You would redirect like so:
redirect('insert_URI_here');
Or if you are passing it to another method in the same controller you could just do :
$this->method_name();
However, if you just drop that in your else statement, user_register_view will not load because $this->form_validation->run() will return false either on validation error or non submission of the form.
What you will have to do is add another check to look for validation errors. If validation has failed and there are no validation errors, then your form hasn't been submitted.
So you could do something like this:
public function create() {
$this->form_validation->set_rules('email_adress', 'E-mail', 'required|valid_email|is_unique[users.email_adress]');
if(($this->form_validation->run() == FALSE) && ($this->form_validation->error_string() == ''));
//form not submitted yet
$this->load->view('user_register_view');
else if ($this->form_validation->run()) {
// PASSED
} else {
//Validation errors
redirect('insert_URI_here');
}
}
You will have to play around with it. I think $this->form_validation->error_string() should return an empty string if not submitted, but it might be a null value or a false (sorry can't remember off the top of my head).