Vue js routing 404 not found and assets - vue.js

i've set up mode to history:
const router = new VueRouter({
mode: 'history',
routes:[
...
{
path: '*',
name: '404',
components: { default: Page404},
}
})
created htaccess
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
everything working as expected wrong urls falls to 404 page... the problem is with deep urls for example website.com/wrong/wrong/wrong images don't appear in the 404 page !

well it had nothing to do with i was missing a slash /
this works on level one url:
<img class="title" src="img/sad404.png" alt="404 not found" />
this is the correct way:
<img class="title" src="/img/sad404.png" alt="404 not found" />

Related

Vue 2 router history-mode returing 404 error

I have a vue app with history mode running in tomcat.
When visiting www.mywebapp.com/faq or mywebapp.com/other or ../post/99 etc it first seems to be working fine. Refreshing the page seems to be working fine aswell.
However when looking closer in the browser console i see that i got an error message.
"Failed to load resource: the server responded with a status of 404 ()"
The page is returning a 404 error for a split second before it redirects me and then the page loads.
On very slow internet connections the 404 error is there for a longer time before it loads.
Anyone know what i am missing here? I have added the following code to try to handle it but without success.
My router looks like this:
export const router = new Router({
mode: 'history',
base: '/',
routes: [
{
name: 'home',
path: '/',
component: Home
},
....,
{
path: '*',
component: ErrorLanding,
name: 'NotFound'
},
],
scrollBehavior(to, from, savedPosition) {
return {x:0, y: 0}
}
});
router.beforeEach((to, from, next) => {
const publicPages = ['/', '/login', '/register',
'/help', '/ticket', '/about' ];
const authRequired = !publicPages.includes(to.path);
const loggedIn = localStorage.getItem('user');
const redirect = to.path;
if (authRequired && loggedIn === null) {
if(to.meta.authRequired === false) {
next();
}
else
next({ name: 'Login', query: { redirect: redirect } });
} else {
next();
}
});
my vue.config.js looks like this:
module.exports = {
devServer: {
port: 80,
proxy: "http://localhost/*"
},
publicPath: process.env.NODE_ENV === 'production'
? '/'
: '/'
}
and finally, i have a web.xml file in dist/ROOT/WEB-INF/ with the following code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0"
metadata-complete="true">
<display-name>my app</display-name>
<description>
this is my app description
</description>
<error-page>
<error-code>404</error-code>
<location>/</location>
</error-page>
</web-app>
update
When i add this line of code to server.xml
<Valve
className="org.apache.catalina.valves.rewrite.RewriteValve"/>
and create a rewrite.config file that i add to Catalina/localhost/
with this info:
RewriteCond %{REQUEST_PATH} !-f
RewriteRule ^/(.*) /index.html
It is working, and i dont get any error message any more.
But now i have a new issue. My arrays that are being populated via Vuex are returning infinite loops. How can this be?
Update
I have tried to add this to the rewrite.config file:
RewriteRule ^/api1/ - [L,NC]
RewriteRule ^/api2/ - [L,NC]
RewriteRule ^/api3/ - [L,NC]
RewriteRule ^/api4/ - [L,NC]
RewriteRule ^/api5/ - [L,NC]
RewriteRule ^/api6/ - [L,NC]
RewriteCond %{REQUEST_PATH} !-f
RewriteRule ^/(.*) /index.html
update SOLVED
Below had to be added to the rewrite.config file.
RewriteRule ^/myapi1/.* - [L,NC]
RewriteRule ^/myapi2/.* - [L,NC]
RewriteRule ^/myapi3/.* - [L,NC]
RewriteRule ^/myapi4/.* - [L,NC]
RewriteRule ^/myapi5/.* - [L,NC]
RewriteRule ^/host-manager/.* - [L,NC]
RewriteRule ^/manager/.* - [L,NC]
RewriteCond %{REQUEST_PATH} !-f
RewriteRule ^/(.*) /index.html
Big thanks to #Michal Levy for help!!
Problem is result of using history mode of Vue Router and is very well described in the docs
When using history mode, the URL will look "normal," e.g. http://oursite.com/user/id. Here comes a problem, though: Since our app is a single page client side app, without a proper server configuration, the users will get a 404 error if they access http://oursite.com/user/id directly in their browser. To fix the issue, all you need to do is add a simple catch-all fallback route to your server. If the URL doesn't match any static assets, it should serve the same index.html page that your app lives in.
There are also lot of examples how to do it for lot of servers - not for Tomcat tho...
To configure "SPA fallback" (common name for this) in Tomcat you must:
Configure the RewriteValve in server.xml
Edit the ~/conf/server.xml to add the below Valve inside the Host section as below:
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.rewrite.RewriteValve" />
</Host>
Write the rewrite rule in rewrite.config
Create directory structure — ~/conf/Catalina/localhost/ and create the rewrite.config file inside it with the below content:
RewriteCond %{REQUEST_PATH} !-f
RewriteRule ^/(.*) /index.html
Source
Note: Beware that above rewrite rule causes your server to return index.html for any request that does not match "regular file" (see Rewrite Valve Docs). So if your server also serves some kind of API (REST for example), you need to modify rewrite rules to not to rewrite requests to API.
For example if your API is served on /api/..., your rewrite rules should look like this:
# match any URL starting with `/api/`, do not rewrite and don't process other rules...
RewriteRule ^/api/ - [L]
RewriteCond %{REQUEST_PATH} !-f
RewriteRule ^/(.*) /index.html
#Michal Levys answer is correct, just some small adjustments had to be made for my case.
To solve this issue below had to be added to rewrite.config.
RewriteRule ^/myapi1/.* - [L,NC]
RewriteRule ^/myapi2/.* - [L,NC]
RewriteRule ^/myapi3/.* - [L,NC]
RewriteRule ^/myapi4/.* - [L,NC]
RewriteRule ^/myapi5/.* - [L,NC]
RewriteRule ^/host-manager/.* - [L,NC]
RewriteRule ^/manager/.* - [L,NC]
RewriteCond %{REQUEST_PATH} !-f
RewriteRule ^/(.*) /index.html

Vue Js 2 error reload

I'm working with Vue Js 2 and the server it's Nginx.
I have the next code:
const routes = [
{
path: '/design-dashboard',
component: DesignDashboard,
meta: {
forAuth : true
}
},
{
path: '/dashboard/new/create',
component: ProductLineEdit,
meta: {
forAuth : true
}
}
];
export default new VueRouter({
mode: 'history',
routes
});
The problem is when the url has more than 1 sections o slashes '/', for example it works with "/design-dashboard", and don't works with "/dashboard/new/create" only works throught a vue router link or a router.push() method, but when I reload or try to access directly with the url in the browser doesn't work.
I also added this htaccess code:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
As I said before everything works fine with only one section or sliash '/ like for example "/design-dashboard"
Any ideas? Thanks in advance.
It seems that you are trying to configure the nginx server using htaccess which is intended for apache.
Try to configure nginx, with configs like this:
location / {
try_files $uri $uri/ /index.html;
}
I solved this adding the .htaccess and transform to a nginx code and adding an slash '/' in src attribute in script tag
<script src="/dist/app.js">

Workaround for Fragment URLs not allowed with Google Redirect URI

I use mac to develop a MEAN stack project. My web pages https://localhost:3000/#/login and https://localhost:3000/#/new work (note that all my pages need to have /#/ in the middle to work). https://localhost:3000/#/new has a button that leads to a google login by passportjs. In my routes ==> index.js I set
var express = require('express');
var router = express.Router();
... ...
router.get('/auth/google', passport.authenticate('google', { scope: ['profile', 'email'] }));
router.get('/auth/google/callback',
passport.authenticate('google', { successRedirect: '/login',
failureRedirect: '/login' }));
In Google APIs ==> Credentials, when I set https://localhost:3000/auth/google/callback as Authorized redirect URIs. Clicking on the login button on my page returns an error
Error: redirect_uri_mismatch
The redirect URI in the request, https://localhost:3000/auth/facebook/callback, does not match the ones authorized for the OAuth client. Visit https://console.developers.google.com/apis/credentials/oauthclient/367100934152-7csfhcdnkapil4obku1pr2cnrsmthk61.apps.googleusercontent.com?project=367100934152 to update the authorized redirect URIs.
I guess it is because https://localhost:3000/auth/facebook/callback is not a valid URI for my app, but if I set https://localhost:3000/#/auth/facebook/callback in the setting page, it is NOT allowed: Invalid Redirect: https://localhost:3000/#/auth/google/callback cannot contain a fragment.
So, does anyone know if I could modify my app to work without #, so that https://localhost:3000/login and https://localhost:3000/new, etc. will work? As a result, https://localhost:3000/auth/facebook/callback will be a valid URI too...
Edit 1:
Following the answer of #Sevran and the doc how to configure your server to work with html5mode, I did the following:
1) added $locationProvider.html5Mode(true) in app.config
2) added <base href="/" /> in index.ejs (note I don't have index.html in the project)
3) keeps .state('new', url: '/new', ... in app.config
4) did NOTHING in httpd-vhosts.conf, which has <VirtualHost *:80> and <VirtualHost *:433>, because I think my server is controlled by express.js rather than httpd-vhosts.conf, and the port is 3000.
5) did NOT add .htaccess in the project like this answer
6) added in app.js the following, I also tried to change index.html to index.ejs.
app.use('/js', express.static(__dirname + '/js'));
app.use('/dist', express.static(__dirname + '/../dist'));
app.use('/css', express.static(__dirname + '/css'));
app.use('/partials', express.static(__dirname + '/partials'));
app.all('/*', function(req, res, next) {
// Just send the index.html for other files to support HTML5Mode
res.sendFile('index.html', { root: __dirname });
});
The tests shows
entering https://localhost:3000/#/new in the URL bar becomes https://localhost:3000/new, and it loads the corresponding page.
entering https://localhost:3000/new in the URL bar raises a not found 404 error.
entering https://localhost:3000/auth/google in the URL bar raises the Error: redirect_uri_mismatch as above.
To conclude, I think the rewriting (of a url without #) should be managed by express, but the code I posted did not do the job. Regarding .htaccess, I tried also to put it in the DocumentRoot of httpd-vhosts.conf or the root folder of the project, it did not help.
In app->init.js enable below mode in config
$locationProvider.html5Mode({
enabled: true,
requireBase: false
}).hashPrefix('!');
You can remove the hash from your URL using the HTML5 mode:
appModule.config(['$locationProvider', function($locationProvider) {
$locationProvider.html5Mode(true);
}]);
Don't forget to set the base in your <head> tag:
<head>
<base href="/">
...
</head>
Note that if you are using Angular 1.6, you also need to change the hashPrefix:
appModule.config(['$locationProvider', function($locationProvider) {
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix(''); // remove the ! from URL
}]);
More info about the changelog here.
The changes you have to make:
1) Angular Side: as you already changed
$locationProvider.html5Mode(true) in app.config
added <base href="/" /> in index.ejs
keeps .state('new', url: '/new', ... in app.config
2) hta access
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !^(/index\.php|/img|/js|/css|/robots\.txt|/favicon\.ico)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ./index.html [L]
3) This change you need from server side,
If using apache:
<VirtualHost *:80>
ServerName my-app
DocumentRoot /path/to/app
<Directory /path/to/app>
RewriteEngine on
# Don't rewrite files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Rewrite everything else to index.html to allow html5 state links
RewriteRule ^ index.html [L]
</Directory>
</VirtualHost>
In the above my-app will be the application name. and DocumentRoot and Directory paths should be complete path to your application root
For Express:
var express = require('express');
var app = express();
app.use('/js', express.static(__dirname + '/js'));
app.use('/dist', express.static(__dirname + '/../dist'));
app.use('/css', express.static(__dirname + '/css'));
app.use('/partials', express.static(__dirname + '/partials'));
app.all('/*', function(req, res, next) {
// Just send the index.html for other files to support HTML5Mode
res.sendFile('index.html', { root: __dirname });
});
app.listen(3006); //the port you want to use
Here is the Documentation regarding it

Vue Router return 404 when revisit to the url

I just enable Vue router history mode. And it work fine when I visit to vue routing via v-href or href. But, when I try to refresh that page or go directly from browser address bar, it just return 404. Is there any option to accept refresh/revisit to that url?
The following is my Vue router configuration
var router = new VueRouter({
hashbang: false,
history: true,
mode: 'html5',
linkActiveClass: "active",
root: '/user'
});
I think you are missing that SPA is not server side rendering. At least for the majority. So, when you access /anything your web server won't redirect it to index.html. However, if you click on any vuejs router link, it will work due to the fact that the javascript got in action, but not the server side.
To solve this, use .htaccess and redirect all requests to index.html like so
<ifModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</ifModule>
Hope it helps someone!
By refreshing the page you are making a request to the server with the current url and the server returns 404. You have to handle this on your web framework or web server level.
This article contains example server configuration:
https://v3.router.vuejs.org/guide/essentials/history-mode.html
That is too simple, you just have to add this config on your server:
Apache
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
Then if you are on Nginx:
location / {
try_files $uri $uri/ /index.html;
}
And that's all
For more information, visit this Link
Even better (on Ubuntu) if you have root access would be to modify /etc/apache2/apache2.conf, since even Apache themselves discourage the use of .htaccess under such circumstances.
Note that first, you have to do
sudo a2enmod rewrite
Then, add the following block to /etc/apache2/apache2.conf:
<Directory /var/www/html/>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</Directory>
After this, do
sudo systemctl restart apache2
For me, this works flawlessly, as it not just redirects routes created by Vue back to /, but actually refreshes the website to the same route, so you're still on the same view after refreshing.
If you don't really care about the hash in the url, you can just set the mode to hash in you router configuration file: mode: 'hash'
That works fine without the need to set up the server side.
const router = new VueRouter({
routes: Routes,
mode: 'hash'
});
Hash mode comes by default in vue, so it should work even if you leave blank instead of mode: 'hash'.
If someone is dealing with this issue in .NET Core as the backend of the app, a nice approach is a simple fallback handler in the Startup.cs of our .NET Core application:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
....Your configuration
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
//handle client side routes
app.Run(async (context) =>
{
context.Response.ContentType = "text/html";
await context.Response.SendFileAsync(Path.Combine(env.WebRootPath, "index.html"));
});
}
}
For more details: http://blog.manuelmejiajr.com/2017/10/letting-net-core-handle-client-routes.html
Just put the code in the .htaccess file in the same folder and that's it. No need to reboot or Apache restart.
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
For Netlify, add the following to a public/_redirects file...
/* /index.html 200
See https://www.netlify.com/docs/redirects/#history-pushstate-and-single-page-apps
if any one of is facing the issue even after trying above solution, please find below method.
If you have vue.config.js which has
module.exports = {
publicPath: process.env.PUBLIC_URL || "", // <-- this is wrong
...
};
either remove the vue.config.js or try removing the publicPath from the exports object.
Also you can try below method if you dont want to remove the publicPath
module.exports = {
publicPath: process.env.PUBLIC_URL || "/", default)
transpileDependencies: ["vuetify"],
};
In my case it was happening in a SPA hosted in azure blob storage as static website under an azure CDN.
The solution of the following question solved the issue for me (fix applied in CDN endpoint): Hosting SPA with Static Website option on Azure Blob Storage (clean URLs and Deep links)
In my case, I had handled it through virtual host:
Apache
goto /etc/apache2/sites-available and clone 000-default.conf with you domain name i.e. app.abc.com.conf and open it by sudo nano app.abc.com.conf and then place below snippet:
Vue Doc Reference: Click here
StackOverflow Reference: Click here
<VirtualHost *:80>
ServerName app.abc.com
DocumentRoot /var/www/html/project_name/dist/
ErrorLog ${APACHE_LOG_DIR}/app.abc.com.error.log
CustomLog ${APACHE_LOG_DIR}/app.abc.com.access.log combined
<Directory /var/www/html/project_name/dist>
FallbackResource /index.html
</Directory>
</VirtualHost>
Niginx Vue Cli (SPA) virtual host:
Vue Doc Reference: Click here
server {
listen 80;
root /var/www/html/project_name/dist;
server_name app.abc.com;
index index.html index.htm index.php;
location / {
try_files $uri $uri/ /index.html;
}
}
Note: Tested on ubuntu server
For someone seeing this using an Express backend, there is the middleware connect-history-api-fallback that is implemented like this
const express = require('express');
const history = require('connect-history-api-fallback');
const app = express();
app.use(history({
index: '/' //whatever your home/index path is default is /index.html
}));
Or with Native Node
const http = require('http')
const fs = require('fs')
const httpPort = 80
http.createServer((req, res) => {
fs.readFile('index.htm', 'utf-8', (err, content) => {
if (err) {
console.log('We cannot open "index.htm" file.')
}
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8'
})
res.end(content)
})
}).listen(httpPort, () => {
console.log('Server listening on: http://localhost:%s', httpPort)
})
Both are suggestions in the documentation.
Anyone else facing the same problem, I just realized that
"book/:bookId/read" // this will not work after page reload
and this are different
"/book/:bookId/read" // this is what works even after page reload
This is of course after following what other fellas have suggested up there more importantly the catch all route in your app server side.
I actually don't know why this worked, but any one with any ideas can let us know.
I'm using Razor Pages with .net core 2.2 and I got this working:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
routes.MapRoute(
name: "spa-fallback",
template: "{*url:regex(^((?!.css|.map|.js|sockjs).)*$)}",
defaults: new { controller = "Index", action = "Index" });
});
Coming in late here, but.....
If anyone's interested in a workaround for this, I simply did this:
Redirect requests to known client-side URLs to the index (but only known client-side URLs, that way, you can keep 404s for actual page-not-found URLs). How you do this is dependent upon your server. I was using flask at the time, and just added more routes.
Add an attribute to the root vue element
<div id="content" redirect_to="{{redirect_to}}"></div>
Where my view function was replacing the parameter with the actual route requested by the URL
In the root vue component, check for this attribute and retrieve its value, as so:
if (this.$el.attributes['redirect_to'].value !== "") path =
this.$el.attributes['redirect_to'].value
Make a call to $router.push with the value of the attribute
this.$router.push(path).catch(err => {})
(The catch is needed otherwise the router will throw a NavigationDuplicated error.)
I did this because the entire point of urls in an SPA is because they point to specific resources and I wanted my users to be able to use the URLs to get back to those same resources.
Hopefully this either helps someone or points them in the right direction to make it work for their specific needs.
I am also facing the same problem. After adding this code in routes -> web.php the issue is resolved.
Route::get('/{vue_capture?}', function () {
return view('admin.dashboard');
})->where('vue_capture', '[\/\w\.-]*');
Redirect this route to your parent blade view.
Just as explained by #SERPRO, Vuejs, Angular, Reactjs, etc are all build on the concept of Single Page Application (SPA), so what I did on my Apache server was simply tell the server to load index.html for any path value after the origin. below are the changes that worked for me.
on my virtual host file, I added a simple rewrite rule to always load index.html and not to redirect.
RewriteEngine On
RewriteRule "^/auth/account/confirm" "/index.html"
And so if a user enters http://my-origin/account/confirm, the server will load the index.html with the app.vue file bound to it, and the app.vue file router will now pick the path account/confirm and determine the appropriate component to render
Then run
apachectl restart
service httpd restart
In my case the 404 error not only happend on a page reload but also when using the navigation buttons of the browser.
The problem were special characters like ä, ö, etc. in the path. First, the routing worked very well with those but when using the browsers navigation buttons or on a page reload the page can't be found anymore. After I replaced special characters with normal letters the routing worked again.
Maybe this helps somebody else.
In my case, I used Vue's own configuration to tackle this issue. The idea here is to utilize pages option. So in the file vue.config.js, we will add the following sample of code:
module.exports = {
pages: {
// This is our main page which refers to the main Javascript file
index: "src/main.js",
// Next, we list all other pages in the application,
// and let all of them refer to the main Javascript file as well.
other_page: "src/main.js",
},
};
With this implementation, if we stay at other_page and reload, the application would still work without any 404 error displayed.
My case is Apache server. There are 2 ways to configure:
I) Put a .htaccess file in the root vue source, (usually it is /var/www/html/)
The code and explanation for .htaccess are well explained by #Tiago Matos already.
The only one thing he didn't mention is to let Apache know a user can configure the Apache in .htaccess file under the root vue source (here is /var/www/html/), to do this:
Open httpd config file to edit:
sudo vi /etc/httpd/conf/httpd.conf
Change AllowOverride to All
<Directory /var/www/html>
AllowOverride All
Restart Apache to apply a new configuration
sudo systemctl restart httpd
II) Edit directly configuration of Apache: /etc/apache2/apache2.conf as #Tobias Feil mentioned. (Although not tested yet)

Yii UrlManager routing

I'm developing a video hosting website. I have a small trouble with UrlManager in Yii config. Easch video in my db has a key like 't9d23f78ld' and I want the user to go to http://example.com/watch/t9d23f78ld to view the video. I have created the 'watch' controller and my urlmanager config looks like this:
'urlManager'=>array(
'class'=>'application.components.UrlManager',
'urlFormat'=>'path',
'showScriptName' => false,
'rules'=>array(
'<language:(ru|uz)>/' => 'site/index',
'<language:(ru|uz)>/<action:(contact|login|logout)>/*' => 'site/<action>',
'<language:(ru|uz)>/<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<language:(ru|uz)>/<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
'<language:(ru|uz)>/<controller:\w+>/<action:\w+>/*'=>'<controller>/<action>',
),
),
My .htaccess looks like this:
RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to index.php
RewriteRule . index.php
AddDefaultCharset utf-8
HOW DO SET UP THE URL MANAGER TO VIEW THE VIDEOS AT
Add rule:
watch/<id:\w+>'=>'watch/showVideo',
which mean, that id from url is passed to showVideo action.
Also you should add parameter in action:
public function actionShowVideo($id) {
...
}