# Redirect in v4 struggle

**URL:** <https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513>\
**Category:** Questions\
**Created:** [October 8, 2019, 9:15am UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513 "2019-10-08T09:15:34Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![nadirian](https://avatars.discourse-cdn.com/v4/letter/n/bc79bd/32.png) [@nadirian](https://discourse.slimframework.com/u/nadirian)\
**Post date:** [October 8, 2019, 9:15am UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/1 "2019-10-08T09:15:34Z")

</div>

I have invoked a class through $app-\>post() from routes.php

And the script runs well without errors

```
public function __invoke(Request $request, Response $response): Response {
    // your code
    // to access items in the container... $this->container->get('');
        return $response->withHeader('Location', '/')
            ->withStatus(201);
}

```

And i see on Console that the Location and Status is actually correctly set, but the browser wont redirect me. Why is this?

---

<div class="post-metadata">

**Author:** ![Antnee](https://avatars.discourse-cdn.com/v4/letter/a/919ad9/32.png) [@Antnee](https://discourse.slimframework.com/u/Antnee)\
**Post date:** [October 8, 2019, 9:34am UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/2 "2019-10-08T09:34:51Z")

</div>

Because you’re using a `201 Created` status. If you’re redirecting after a POST, you probably want a `303 See Other`

---

<div class="post-metadata">

**Author:** ![nadirian](https://avatars.discourse-cdn.com/v4/letter/n/bc79bd/32.png) [@nadirian](https://discourse.slimframework.com/u/nadirian)\
**Post date:** [October 8, 2019, 10:29am UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/3 "2019-10-08T10:29:01Z")

</div>

> The **`Location`** response header indicates the URL to redirect a page to. It only provides a meaning when served with a `3xx` (redirection) or `201` (created) status response.

This is from Mozilla-dev, do i misunderstand this one? Shouldnt it redirect if i send a 201 created? ([Location - HTTP | MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location))

---

<div class="post-metadata">

**Author:** ![Antnee](https://avatars.discourse-cdn.com/v4/letter/a/919ad9/32.png) [@Antnee](https://discourse.slimframework.com/u/Antnee)\
**Post date:** [October 8, 2019, 10:54am UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/4 "2019-10-08T10:54:57Z")

</div>

If you return a location on a 201, that’s telling the client where they can find the resource. It doesn’t redirect. The only way to redirect is to use a 3xx status. They’re literally defined by the W3C as:

- 1xx: Informational
- 2xx: Successful
- 3xx: Redirection
- 4xx: Client Error
- 5xx: Server Error

[https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)

---

<div class="post-metadata">

**Author:** ![nadirian](https://avatars.discourse-cdn.com/v4/letter/n/bc79bd/32.png) [@nadirian](https://discourse.slimframework.com/u/nadirian)\
**Post date:** [October 8, 2019, 10:55am UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/5 "2019-10-08T10:55:32Z")

</div>

Ill try that, i have another issue atm.

---

<div class="post-metadata">

**Author:** ![conleec](https://avatars.discourse-cdn.com/v4/letter/c/3ec8ea/32.png) [@conleec](https://discourse.slimframework.com/u/conleec)\
**Post date:** [October 16, 2019, 11:32pm UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/6 "2019-10-16T23:32:04Z")

</div>

Exact same struggle here. Redirects work fine for me in normal classes, but I cannot seem to get them working in middleware, where a response is dynamically generated (apparently?) by the Request Handler. When I try to redirect with a Location header, it simply fails to redirect, and my route continues to the original location.

Here’s a basic version of my authentication middleware:

```auto
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;

class AuthMiddleware extends Middleware {

	public function __invoke(Request $request, RequestHandler $handler): Response {
		$response = $handler->handle($request);
		$loggedInTest = false;
		if ($loggedInTest) {
			echo "User authorized.";
			return $response;
		} else {
			echo "User NOT authorized.";
			return $response->withHeader('Location', '/users/login')->withStatus(302);
		}
	}
}

```

Thank you in advance.

---

<div class="post-metadata">

**Author:** ![FvsJson](https://avatars.discourse-cdn.com/v4/letter/f/e99b99/32.png) [@FvsJson](https://discourse.slimframework.com/u/FvsJson)\
**Post date:** [October 17, 2019, 12:58pm UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/7 "2019-10-17T12:58:22Z")

</div>

> [@conleec](#):
>
> class AuthMiddleware extends Middleware { public function \_\_invoke(Request $request, RequestHandler $handler): Response { $response = $handler-\>handle($request); $loggedInTest = false; if ($loggedInTest) { echo “User authorized.”; return $response; } else { echo “User NOT authorized.”; return $response-\>withHeader(‘Location’, ‘/users/login’)-\>withStatus(302); } } }

Hi, havent tested it but maybe try this…

```php

class AuthMiddleware extends Middleware {

	public function __invoke(Request $request, RequestHandler $handler): Response {
		$loggedInTest = false;
		if ($loggedInTest) {
			echo "User authorized.";
			return $handler->handle($request);;
		} else {
			echo "User NOT authorized.";

                      $response = new Response();
			return $response->withHeader('Location', '/users/login')->withStatus(302);
		}
	}
}

```

---

<div class="post-metadata">

**Author:** ![conleec](https://avatars.discourse-cdn.com/v4/letter/c/3ec8ea/32.png) [@conleec](https://discourse.slimframework.com/u/conleec)\
**Post date:** [October 17, 2019, 5:13pm UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/8 "2019-10-17T17:13:38Z")

</div>

Hmm, still a no-go, unfortunately. Browser simply displays the “Not Authorized” text, then hangs on a white screen without actually redirecting…

Chris

---

<div class="post-metadata">

**Author:** ![odan](https://avatars.discourse-cdn.com/v4/letter/o/9de053/32.png) [@odan](https://discourse.slimframework.com/u/odan)\
**Post date:** [October 17, 2019, 6:15pm UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/9 "2019-10-17T18:15:28Z")

</div>

A HTTP header can only be sent **before** sending a body content. I would guess that the `echo` statements are problematic here.

---

<div class="post-metadata">

**Author:** ![conleec](https://avatars.discourse-cdn.com/v4/letter/c/3ec8ea/32.png) [@conleec](https://discourse.slimframework.com/u/conleec)\
**Post date:** [October 17, 2019, 6:31pm UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/10 "2019-10-17T18:31:37Z")

</div>

Very good point. Unfortunately, it still fails to redirect. It falls thru to the originally called route, completely ignoring the redirect header. If I initiate a new Response, as suggested by [FvsJson](https://discourse.slimframework.com/u/FvsJson), it hangs on a white screen.

Stumped.

---

<div class="post-metadata">

**Author:** ![conleec](https://avatars.discourse-cdn.com/v4/letter/c/3ec8ea/32.png) [@conleec](https://discourse.slimframework.com/u/conleec)\
**Post date:** [October 18, 2019, 6:18am UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/11 "2019-10-18T06:18:36Z")

</div>

Okay, I figured it out. Basically, I’m an idiot. My code was stuck in a perpetual redirect loop. I had to use the $\_SERVER[‘REQUEST\_URI’] variable to break out of the loop, like so:

```auto
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;

class AuthMiddleware extends Middleware {

    public function __invoke(Request $request, RequestHandler $handler): Response {
        $response = $handler->handle($request);
        $loggedInTest = false;
        if (!$loggedInTest && $_SERVER['REQUEST_URI'] != '/user/login') {
            return return $response->withHeader('Location', '/users/login')->withStatus(302);
        } else {
            return $response;
        }
    }
}

```

Does anybody have another way to accomplish this, or is the $\_SERVER method the best way?

---

<div class="post-metadata">

**Author:** ![odan](https://avatars.discourse-cdn.com/v4/letter/o/9de053/32.png) [@odan](https://discourse.slimframework.com/u/odan)\
**Post date:** [October 18, 2019, 6:41am UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/12 "2019-10-18T06:41:15Z")

</div>

Better use the $request object for all request-related things.

```php
$uri = $request->getUri();

```

[http://www.slimframework.com/docs/v4/objects/request.html#the-request-uri](http://www.slimframework.com/docs/v4/objects/request.html#the-request-uri)

PS: Using a fixed string here `'/user/login'` could be problematic in case the basePath changes later.

---

<div class="post-metadata">

**Author:** ![FvsJson](https://avatars.discourse-cdn.com/v4/letter/f/e99b99/32.png) [@FvsJson](https://discourse.slimframework.com/u/FvsJson)\
**Post date:** [October 18, 2019, 3:09pm UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/13 "2019-10-18T15:09:24Z")

</div>

And so we all learn together 😛

---

<div class="post-metadata">

**Author:** ![conleec](https://avatars.discourse-cdn.com/v4/letter/c/3ec8ea/32.png) [@conleec](https://discourse.slimframework.com/u/conleec)\
**Post date:** [October 18, 2019, 4:39pm UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/14 "2019-10-18T16:39:05Z")

</div>

Ugh, I spoke too soon. Basically, I got so frustrated trying to make Slim 4’s redirect work in a middleware scenario that I went dabbling in FatFreeFramework, and found I had the same problem. That clued me in that maybe it was something I was doing that was the culprit. Long story short, in my FFF testing, I was definitely putting my test app in an infinite redirect loop, so I assumed it was the same issue here. But sadly it is not.

In my Slim 4 app, my ‘login’ method is in a completely different class, and it obviously is NOT behind authentication. And sadly, the redirect still falls thru to the original route. Anybody have an idea on this? Has anybody else made redirect work in middleware, and if so, can you post a snippet showing how?

---

<div class="post-metadata">

**Author:** ![odan](https://avatars.discourse-cdn.com/v4/letter/o/9de053/32.png) [@odan](https://discourse.slimframework.com/u/odan)\
**Post date:** [October 18, 2019, 5:33pm UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/15 "2019-10-18T17:33:48Z")

</div>

There are some things to consider in this case.

Make sure that The AuthMiddleware will be only invoked for routes that needs the AuthMiddleware. In Slim, you could create a special [Route group](http://www.slimframework.com/docs/v4/objects/routing.html#route-groups) for all “protected” routes and create another route group for all login/logout-related routes, but without the AuthMiddleware.

If you try this concept, the AuthMiddleware only have to check for the logged in user. I would also call the `handle` method only for valid users. Here is an example.

### Routes

```php
use Slim\App;
use Slim\Routing\RouteCollectorProxy;

return static function (App $app) {

    // Routes without authentication check
    $app->group('/users', function (RouteCollectorProxy $group) {
        $group->post('/login', \App\Action\UserLoginSubmitAction::class);
        $group->get('/login', \App\Action\UserLoginIndexAction::class)->setName('login');
        $group->get('/logout', \App\Action\UserLogoutAction::class);
    })->add(SessionMiddleware::class);

    // Routes with authentication
    $app->group('', static function (RouteCollectorProxy $group): void {
        // Default page
        $group->get('/', \App\Action\HomeIndexAction::class)->setName('root');

        // add more routes
        // ...
    })->add(AuthMiddleware::class)
      ->add(SessionMiddleware::class);

};

```

## The AuthMiddleware

Pseudo example:

```php
<?php

namespace App\Middleware;

use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Routing\RouteContext;

/**
 * Auth Middleware.
 */
final class AuthMiddleware implements MiddlewareInterface
{
    /**
     * @var ResponseFactoryInterface
     */
    private $responseFactory;

    /**
     * Constructor.
     *
     * @param ResponseFactoryInterface $responseFactory The response factory
     */
    public function __construct(ResponseFactoryInterface $responseFactory)
    {
        $this->responseFactory = $responseFactory;
    }

    /**
     * Invoke middleware.
     *
     * @param ServerRequestInterface $request The request
     * @param RequestHandlerInterface $handler The handler
     *
     * @return ResponseInterface The response
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $isLoggedIn = !empty($_SESSION['user_id']); // check user login / session here

        if ($isLoggedIn) {
            return $handler->handle($request);
        }

        // Redirect to login route
        $routeParser = RouteContext::fromRequest($request)->getRouteParser();
        $url = $routeParser->urlFor('login');

        return $this->responseFactory->createResponse()->withHeader('Location', $url)->withStatus(302);
    }
}

```

---

<div class="post-metadata">

**Author:** ![FvsJson](https://avatars.discourse-cdn.com/v4/letter/f/e99b99/32.png) [@FvsJson](https://discourse.slimframework.com/u/FvsJson)\
**Post date:** [October 19, 2019, 4:07am UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/16 "2019-10-19T04:07:11Z")

</div>

I think Odan nailed it. Your auth middleware is currently set to global so it hits a loop. you need to only assign the auth middleware to the route that needs to be authenticated.

```php

    // Routes with authentication
    $app->group('', static function (RouteCollectorProxy $group): void {
        // Default page
        $group->get('/', \App\Action\HomeIndexAction::class)->setName('root');

        // add more routes
        // ...
    })->add(AuthMiddleware::class)
      ->add(SessionMiddleware::class);

```

well done Oden.

---

<div class="post-metadata">

**Author:** ![conleec](https://avatars.discourse-cdn.com/v4/letter/c/3ec8ea/32.png) [@conleec](https://discourse.slimframework.com/u/conleec)\
**Post date:** [October 20, 2019, 4:02pm UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/17 "2019-10-20T16:02:09Z")

</div>

Yes, in my testing code, it was definitely a redirect loop, but in the app I’m actually considering upgrading from Slim 3, the login method is not behind authentication, and the redirect still fails.

However, examining Odan’s code, I see he’s used the ResponseFactory to create a fresh response for the redirect, which is something I did not do and will have to try. Life has gotten in the way the past few days, so I haven’t had a chance to try it yet…

---

<div class="post-metadata">

**Author:** ![ghabriel](https://avatars.discourse-cdn.com/v4/letter/g/bb73d2/32.png) [@ghabriel](https://discourse.slimframework.com/u/ghabriel)\
**Post date:** [October 11, 2023, 3:09am UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/18 "2023-10-11T03:09:45Z")

</div>

So, if I want to redirect I should create new response, but to continue just use handle method? Is that right?

---

<div class="post-metadata">

**Author:** ![odan](https://avatars.discourse-cdn.com/v4/letter/o/9de053/32.png) [@odan](https://discourse.slimframework.com/u/odan)\
**Post date:** [October 11, 2023, 3:56pm UTC](https://discourse.slimframework.com/t/redirect-in-v4-struggle/3513/19 "2023-10-11T15:56:33Z")

</div>

> So, if I want to redirect I should create new response, but to continue just use handle method? Is that right?

Yes, that’s correct.
