我本身是一個Java Web Application developer, 最近在學Angular Framework,
發覺要分開來開發會出現很多問題(如 Angular routing, CORS),為了解決問題,
所以在Google 裡尋找解決方案,尋找到的解決方案如下:
-
改動Tomcat Server setting, 詳細可以 click這裡。
-
用Servlet Filter 來做 URL redirect。
我不想改動Tomcat Server setting, 於是我用了第2個方案。
雖然有人已經做了一個UrlRewriteFilter可是做不到想要的効果,
最後經過一輪的Research 和測試之後終於做到了。
在解說之前, 我介紹一下我的資料夾結構:
E:. └─workspace └─DemoWeb ├─frontEnd <==這個資料夾是用來儲存Angular project 的檔案 └─jsp <==這個資料夾是用來儲存Dynamic Web project 的檔案
再來,以下是我的開發環境:
-
Eclipse Version 2019-06 (4.12.0)
-
Angular CLI version 8.3.3
-
Node version 10.16.3
-
Tomcat version 8.5
-
Windows 10
為方便解說, 我們先來做一個簡單Java Web Application:
-
打開Eclipse
-
Click File -> New Project -> Dynamic Web Project
-
Click “Next", Project name 輸入"DemoWeb"
-
Click “Finish"
-
右鍵點選這個 project , 點選"Java EE Tools" 來產生web.xml
-
最後在context root 加一個index.jsp 來展示這是JSP的"地盤"
-
在index.jsp裡面輸入以下內容:
<%@ page language="java" contentType="text/html; charset=UTF-8″ pageEncoding="UTF-8″%><!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8″>
<title>This is a JSP page.</title>
</head>
<body>
<%="This is a JSP page." %>
</body>
</html> -
然後按"Ctrl-S"儲存。
-
右鍵點選這個jsp, 點選"Run as" ,再點選"Run on Server", 此時Windows 的預設瀏覽器應會自動跑出來並顯示以下畫面:
-
此時回到Eclipse, 按下被紅圈圈著那個制去停止tomcat server:
再來建立Angular project:
-
打開VS code, 然後click File->Open Folder ,然後點選"E:\workspace\DemoWeb"
-
按"Ctrl-`", terminal 就會出現
-
然後在terminal 中下達以下命令來建立新的Angular project:
ng new frontEnd
-
之後按"Y" 和按一下enter, 然後等待Angular project建立完成
-
點選左手邊的"frontEnd" ,再點選"src",再點選"app"
-
最後點選"app.component.html"這個檔案,
-
將這個檔案的內容全部刪去,換上以下內容:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8″>
<title>This is Angular page.</title>
</head>
<body>
This is Angular page.<br>
<a href="admin/">Go To Admin. Page</a>
<router-outlet></router-outlet>
</body>
</html> -
再click terminal 一下,輸入以下指令進入我們剛剛新建Angular project目錄裡:
cd frontEnd
-
接著再輸入:
ng serve -o
-
此時Windows 的預設瀏覽器應會自動跑出來並顯示以下畫面:
-
此時再click terminal 一下回到terminal ,按"Ctrl-C" 來停止ng serve
制作Admin. Page
-
要制作Admin. Page 我們需要Admin. Page Component,
輸入以下指令來產生Admin. Page Componentng g component Admin
制作Routing
- 點選"frontEnd"->src->app->app-routing.module.ts
- 加入routing
import { NgModule } from ‘@angular/core’;
import { Routes, RouterModule } from ‘@angular/router’;
import { AdminComponent } from ‘./admin/admin.component’;
const routes: Routes = [
{ path: ‘admin’, component: AdminComponent}
];@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})export class AppRoutingModule { }
-
然後按"Ctrl-S"儲存。
測試Routing
-
click terminal 一下,輸入以下命令:
ng serve -o
-
此時Windows 的預設瀏覽器應會自動跑出來並顯示以下畫面:
-
瀏覽以下http://localhost:4200/admin,並顯示以下畫面:
-
此時再click terminal 一下回到terminal ,按"Ctrl-C" 來停止ng serve
設定Angular 的output folder
-
點選"frontEnd"->angular.json
-
將以下setting:
“outputPath": “dist/frontEnd"
改成
“outputPath": “E:\\workspace\\DemoWeb\\jsp\\WebContent\\frontEnd",
-
然後按"Ctrl-S"儲存,之後Angular 會自動將已compile的js 檔案抄到eclipse 的project 目錄裡的frontEnd資料內。
-
click terminal 一下輸入以下命令再啟動 ng serve:
ng build –watch –base-href /DemoWeb/frontEnd/
-
-
由於想tomcat server 執行angular code, 所以要用base href 來配合JSP application 的context path.
-
由於output path setting 最尾是frontEnd, 所以這裡都尾都是frontEnd
-
根據這裡說,如果要Angular routing working properly, 就要將所有access /frontEnd 這個 folder 的request forward 到/frontEnd/index.html.要做到這樣的forward就要加入custom servlet filter,加入custom servlet filter步驟如下:
- 回到Eclipse,右鍵點選這個 project , 點選"New" ->"Filter"
- 輸入filter class name 和package name
- 點選 “Next",然後再點選 “/AngularFilter"
- 再點選 “Edit", 輸入"/frontEnd/*",再click “OK"
- 最後click “Finish"
- 此時會彈出編輯AngularFilter.java畫面
- 請用以下code 才取代原本的code:
/* * Copyright 2004-2005 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.filter; import java.io.File; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.FilterRegistration; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author Roy Tsang * */ /** * Servlet Filter implementation class AngularFilter */ @WebFilter("/frontEnd/*") public class AngularFilter implements Filter { FilterConfig filterConfig; /** * Default constructor. */ public AngularFilter() { // TODO Auto-generated constructor stub } /** * @see Filter#destroy() */ public void destroy() { this.filterConfig = null; } /** * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain) */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String destination,angularRootPath,realPath; HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse=(HttpServletResponse)response; ServletContext context = httpRequest.getServletContext(); destination =httpRequest.getServletPath(); realPath =context.getRealPath(destination); angularRootPath=""; File f = new File(realPath); if (f.exists()) { chain.doFilter(request, response); } else { FilterRegistration fr= context.getFilterRegistration(this.getClass().getName()); for (String mapping: fr.getUrlPatternMappings()) { mapping=mapping.replace("/*", ""); if (destination.indexOf(mapping)>-1) { angularRootPath=mapping; break; } } if (angularRootPath.equals("")) { httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND); } else { filterConfig.getServletContext().getRequestDispatcher(angularRootPath+"/index.html").forward(request, response); } } f=null; } /** * @see Filter#init(FilterConfig) */ public void init(FilterConfig fConfig) throws ServletException { this.filterConfig=fConfig; } }
-
右鍵點選這個project, 點選"Run as" ,再點選"Run on Server", 瀏覽器應該出現以下畫面:
- 瀏覽http://localhost:8080/DemoWeb/frontEnd/, 瀏覽器應該出現以下畫面:
- 點選"Go To Admin. Page" 連結,瀏覽器應該出現以下畫面:
如果出現"admin works!" 字樣,即是設定成功了。
相關的source code 可以在這裡下載。