Symfony: email address as request parameter

2019-02-16 14:58发布

问题:

I'm having some issues with passing an email address in a url to a symfony app.

The url looks like

example.com/unsubscribe/email/me@example.com

It will always result in a sfError404Exception, except when the period is removed. After doing some googling around, the only solution I've yet seen is that htaccess is bypassing the url because of the period present. However, when I add the suggested fix to htaccess, like so:

# we skip all files with .something
RewriteCond %{REQUEST_URI} \..+$
RewriteCond %{REQUEST_URI} !@.+    #skip email address
RewriteCond %{REQUEST_URI} \.epl$
RewriteCond %{REQUEST_URI} !\.html$ 
RewriteCond %{REQUEST_URI} !\.rhtml$
RewriteRule .* - [L]

I get the same 404. It also returns 404 when I use the front controller directly in the url (example.com/index.php/unsubscribe/email/me@example.com). I've tried putting the escaped version directly into the address bar, eg example.com/unsubscribe/me%40example%2Ecom and this works, but only in firefox, nowhere else.

I've spent about 2 hours in forum answer googling hell at this point and I'm running out of ideas.

Any thoughts?

Thanks.

Update: Here is the relevant section of the routing.yml:

unsubscribeform:
  url:  /unsubscribe/email/:email
  param: { module: subscribe, action: index }

Update: Stack trace ... looks like its not getting any route information to go on to me

404 | Not Found | sfError404Exception
Empty module and/or action after parsing the URL "/unsubscribe/email/me@example.com" (/).
stack trace

1. at ()
  in SF_SYMFONY_LIB_DIR/controller/sfFrontWebController.class.php line 44 ...
          41.
          42.       if (empty($moduleName) || empty($actionName))
          43.       {
          44.         throw new sfError404Exception(sprintf('Empty module and/or action after parsing the URL "%s" (%s/%s).', $request->getPathInfo(), $moduleName, $actionName));
          45.       }
          46.
          47.       // make the first request
2. at sfFrontWebController->dispatch()
  in SF_SYMFONY_LIB_DIR/util/sfContext.class.php line 159 ...
         156.    */
         157.   public function dispatch()
         158.   {
         159.     $this->getController()->dispatch();
         160.   }
         161.
         162.   /**
3. at sfContext->dispatch()
  in /home/web/htdocs/index.php line 10 ...
           7. require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'ProjectConfiguration.class.php');
           8.
           9. $configuration = ProjectConfiguration::getApplicationConfiguration(SF_APP, SF_ENVIRONMENT, SF_DEBUG);
          10. sfContext::createInstance($configuration)->dispatch();

11.

回答1:

By default, Symfony treats . and / as parameter separators.
That makes it very easy to match a url like so:

/some/path/:param.:ext

But doesn't help with email addresses.

Fortunately, you can override the . separator by specifying your own pattern.
Just add the requirements line below to your routing:

unsubscribeform:
  url:  /unsubscribe/email/:email
  param: { module: subscribe, action: index }
  requirements: { email: .+ }

The .+ in the requirement is a regular expression matching anything. The .matches any character, and the + means match one-or-more.

(Tested in Symfony 1.4)



回答2:

I don't know what you are doing in Symfony but it may help to be clear that the following is not a valid URL:

example.com/unsubscribe/email/me@example.com

What you almost certainly want (and this is true for all browsers!) is:

http://example.com/unsubscribe/email/me%40example.com

Note: The @ symbol is not safe and must be encoded, the . symbol is safe however (RFC1738). If you don't escape the @ symbol it will almost certainly cause big trouble so (escaping the . almost certainly won't, but you don't need to so I wouldn't).

Problems will occur with not escaping it because @ is reserved as a separator when passing authentication parameters (e.g. http://username:password@hostname.domain/url/). Some URL parsers will work out that you really meant to type %40 if @ occurs after the domain in the URL, but others won't.

Rather than just encoding the @ symbol statically you should use one of PHP's URL encoding functions on the email address( e.g. "$emailAddress = urlencode($emailAddress);") to ensure other characters in the address are also escaped properly. Don't be tempted to leave this till later or 'after you get it working' do it from the start and save yourself and the end users a headache! :-)

NB: There are several ways to encode URL's in PHP, so you will need to read through the documentation page for urlencode() and compare it with other approaches like rawurlencode() to be sure it's what you really want in your case.



回答3:

Your issue isn't with Rewrite Rules. Considering that Symfony is throwing the exception then the request is making it to Symfony.

Can you post the traceback for the exception? If you have sf_logging_enabled it should log some pretty useful info for debugging the routing.