我正在使用Jhipster Spring boot + angular6。但是由于URL中的hash(#),我遇到了麻烦。它正在影响SEO。
我尝试在useHash: false
中设置app-routing-module.ts
。
但是然后当我通过npm start
运行项目时,API无法正常工作。
我认为我必须更改Java文件中的某个位置,以从URL中删除#
。
这是我的WebConfigurer代码,
@Configuration
public class WebConfigurer implements ServletContextInitializer, WebServerFactoryCustomizer<WebServerFactory> {
private final Logger log = LoggerFactory.getLogger(WebConfigurer.class);
private final Environment env;
private final JHipsterProperties jHipsterProperties;
private MetricRegistry metricRegistry;
public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties) {
this.env = env;
this.jHipsterProperties = jHipsterProperties;
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
if (env.getActiveProfiles().length != 0) {
log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles());
}
EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
initMetrics(servletContext, disps);
log.info("Web application fully configured");
}
/**
* Customize the Servlet engine: Mime types, the document root, the cache.
*/
@Override
public void customize(WebServerFactory server) {
setMimeMappings(server);
/*
* Enable HTTP/2 for Undertow - https://twitter.com/ankinson/status/829256167700492288
* HTTP/2 requires HTTPS, so HTTP requests will fallback to HTTP/1.1.
* See the JHipsterProperties class and your application-*.yml configuration files
* for more information.
*/
if (jHipsterProperties.getHttp().getVersion().equals(JHipsterProperties.Http.Version.V_2_0) &&
server instanceof UndertowServletWebServerFactory) {
((UndertowServletWebServerFactory) server)
.addBuilderCustomizers(builder ->
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true));
}
}
private void setMimeMappings(WebServerFactory server) {
if (server instanceof ConfigurableServletWebServerFactory) {
MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT);
// IE issue, see https://github.com/jhipster/generator-jhipster/pull/711
mappings.add("html", MediaType.TEXT_HTML_VALUE + ";charset=" + StandardCharsets.UTF_8.name().toLowerCase());
// CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64
mappings.add("json", MediaType.TEXT_HTML_VALUE + ";charset=" + StandardCharsets.UTF_8.name().toLowerCase());
ConfigurableServletWebServerFactory servletWebServer = (ConfigurableServletWebServerFactory) server;
servletWebServer.setMimeMappings(mappings);
}
}
/**
* Initializes Metrics.
*/
private void initMetrics(ServletContext servletContext, EnumSet<DispatcherType> disps) {
log.debug("Initializing Metrics registries");
servletContext.setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE,
metricRegistry);
servletContext.setAttribute(MetricsServlet.METRICS_REGISTRY,
metricRegistry);
log.debug("Registering Metrics Filter");
FilterRegistration.Dynamic metricsFilter = servletContext.addFilter("webappMetricsFilter",
new InstrumentedFilter());
metricsFilter.addMappingForUrlPatterns(disps, true, "/*");
metricsFilter.setAsyncSupported(true);
log.debug("Registering Metrics Servlet");
ServletRegistration.Dynamic metricsAdminServlet =
servletContext.addServlet("metricsServlet", new MetricsServlet());
metricsAdminServlet.addMapping("/management/metrics/*");
metricsAdminServlet.setAsyncSupported(true);
metricsAdminServlet.setLoadOnStartup(2);
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = jHipsterProperties.getCors();
if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
log.debug("Registering CORS filter");
source.registerCorsConfiguration("/api/**", config);
source.registerCorsConfiguration("/management/**", config);
source.registerCorsConfiguration("/v2/api-docs", config);
}
return new CorsFilter(source);
}
@Autowired(required = false)
public void setMetricRegistry(MetricRegistry metricRegistry) {
this.metricRegistry = metricRegistry;
}
}
这是我的AngularRouteFilter servlet代码,
public class AngularRouteFilter extends OncePerRequestFilter {
// add the values you want to redirect for
private static final Pattern PATTERN = Pattern.compile("^/((api|swagger-ui|management|swagger-resources)/|favicon\\.ico|v2/api-docs).*");
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
if (isServerRoute(request)) {
filterChain.doFilter(request, response);
} else {
RequestDispatcher rd = request.getRequestDispatcher("/");
rd.forward(request, response);
}
}
protected static boolean isServerRoute(HttpServletRequest request) {
if (request.getMethod().equals("GET")) {
String uri = request.getRequestURI();
if (uri.startsWith("/app")){
return true;
}
return PATTERN.matcher(uri).matches();
}
return true;
}
}
这是我的Swagger index.html(swagger-ui / index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="16x16" />
<link href='./dist/css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='./dist/css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='./dist/css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='./dist/css/reset.css' media='print' rel='stylesheet' type='text/css'/>
<link href='./dist/css/print.css' media='print' rel='stylesheet' type='text/css'/>
<script src='./dist/lib/object-assign-pollyfill.js' type='text/javascript'></script>
<script src='./dist/lib/jquery-1.8.0.min.js' type='text/javascript'></script>
<script src='./dist/lib/jquery.slideto.min.js' type='text/javascript'></script>
<script src='./dist/lib/jquery.wiggle.min.js' type='text/javascript'></script>
<script src='./dist/lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
<script src='./dist/lib/handlebars-4.0.5.js' type='text/javascript'></script>
<script src='./dist/lib/lodash.min.js' type='text/javascript'></script>
<script src='./dist/lib/backbone-min.js' type='text/javascript'></script>
<script src='./dist/swagger-ui.min.js' type='text/javascript'></script>
<script src='./dist/lib/highlight.9.1.0.pack.js' type='text/javascript'></script>
<script src='./dist/lib/highlight.9.1.0.pack.js' type='text/javascript'></script>
<script src='./dist/lib/jsoneditor.min.js' type='text/javascript'></script>
<script src='./dist/lib/marked.js' type='text/javascript'></script>
<script src='./dist/lib/swagger-oauth.js' type='text/javascript'></script>
<!-- Some basic translations -->
<!-- <script src='lang/translator.js' type='text/javascript'></script> -->
<!-- <script src='lang/ru.js' type='text/javascript'></script> -->
<!-- <script src='lang/en.js' type='text/javascript'></script> -->
<script type="text/javascript">
$(function() {
var springfox = {
"baseUrl": function() {
var urlMatches = /(.*)\/swagger-ui\/index.html.*/.exec(window.location.href);
return urlMatches[1];
},
"securityConfig": function(cb) {
$.getJSON(this.baseUrl() + "/swagger-resources/configuration/security", function(data) {
cb(data);
});
},
"uiConfig": function(cb) {
alert(cb);
$.getJSON(this.baseUrl() + "/swagger-resources/configuration/ui", function(data) {
cb(data);
});
}
};
window.springfox = springfox;
window.oAuthRedirectUrl = springfox.baseUrl() + './dist/o2c.html'
window.springfox.uiConfig(function(data) {
window.swaggerUi = new SwaggerUi({
dom_id: "swagger-ui-container",
validatorUrl: data.validatorUrl,
supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
onComplete: function(swaggerApi, swaggerUi) {
initializeSpringfox();
if (window.SwaggerTranslator) {
window.SwaggerTranslator.translate();
}
$('pre code').each(function(i, e) {
hljs.highlightBlock(e)
});
},
onFailure: function(data) {
log("Unable to Load SwaggerUI");
},
docExpansion: "none",
apisSorter: "alpha",
showRequestHeaders: false
});
initializeBaseUrl();
$('#select_baseUrl').change(function() {
window.swaggerUi.headerView.trigger('update-swagger-ui', {
url: $('#select_baseUrl').val()
});
addApiKeyAuthorization();
});
function addApiKeyAuthorization() {
var authToken = JSON.parse(localStorage.getItem("jhi-authenticationtoken") || sessionStorage.getItem("jhi-authenticationtoken"));
var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization("Authorization", "Bearer " + authToken, "header");
window.swaggerUi.api.clientAuthorizations.add("bearer", apiKeyAuth);
}
function getCSRF() {
var name = "XSRF-TOKEN=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1);
if (c.indexOf(name) !== -1) return c.substring(name.length,c.length);
}
return "";
}
function log() {
if ('console' in window) {
console.log.apply(console, arguments);
}
}
function oAuthIsDefined(security) {
return security.clientId
&& security.clientSecret
&& security.appName
&& security.realm;
}
function initializeSpringfox() {
var security = {};
window.springfox.securityConfig(function(data) {
security = data;
if (typeof initOAuth === "function" && oAuthIsDefined(security)) {
initOAuth(security);
}
});
}
});
function maybePrefix(location, withRelativePath) {
var pat = /^https?:\/\//i;
if (pat.test(location)) {
return location;
}
return withRelativePath + location;
}
function initializeBaseUrl() {
var relativeLocation = springfox.baseUrl();
$('#input_baseUrl').hide();
$.getJSON(relativeLocation + "/swagger-resources", function(data) {
var $urlDropdown = $('#select_baseUrl');
$urlDropdown.empty();
$.each(data, function(i, resource) {
var option = $('<option></option>')
.attr("value", maybePrefix(resource.location, relativeLocation))
.text(resource.name + " (" + resource.location + ")");
$urlDropdown.append(option);
});
$urlDropdown.change();
});
}
});
</script>
</head>
<body class="swagger-section">
<div id='header'>
<div class="swagger-ui-wrap">
<a id="logo" href="http://swagger.io">swagger</a>
<form id='api_selector'>
<div class='input'>
<select id="select_baseUrl" name="select_baseUrl"></select>
</div>
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/>
</div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap" data-sw-translate> </div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body>
</html>
这是docs.component.html
<iframe src="swagger-ui/index.html" width="100%" height="900" seamless
target="_top" title="Swagger UI" class="border-0"></iframe>
在这里,我的服务器代码在localhost:6060上运行良好。 Butm localhost:6060 / api / docs打开空白页。
这是屏幕截图,
请告诉我我做错了什么地方。
答案 0 :(得分:3)
使用servlet过滤器的解决方案
第一步是配置客户端,在useHash: false
中设置app-routing-module.ts
在index.html
中,将<base href="./" />
更改为<base href="/" />
但这还不够,因为它不支持深层链接,这意味着要从外部链接(例如从邮件或另一个网站)链接到客户端路由,或者在刷新浏览器中的页面时转向ve。
当进行深层链接时,服务器首先接收到请求,并且如果必须由客户端来处理URL,则将找不到该URL,因此我们的服务器应用必须检测URL是否必须由服务器按原样提供(例如,所有API调用)或转发到index.html,以便javascript应用可以对其进行解释。
一种解决方案是在我们在WebConfigurer.java中注册的servlet过滤器(Html5RouteFilter)中使用模式匹配。
WebConfigurer.java
@Bean public Html5RouteFilter html5RouteFilter() { return new Html5RouteFilter(); }
Html5RouteFilter.java
package com.mycompany.myapp.web; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.regex.Pattern; /** * Filter that distinguishes between client routes and server routes when you don't use '#' in client routes. */ public class Html5RouteFilter extends OncePerRequestFilter { private Logger log = LoggerFactory.getLogger(getClass()); // These are the URIs that should be processed server-side private static final Pattern PATTERN = Pattern.compile("^/((api|content|i18n|management|swagger-ui|swagger-resources)/|error|h2-console|swagger-resources|favicon\\.ico|v2/api-docs).*"); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (isServerRoute(request)) { filterChain.doFilter(request, response); } else { RequestDispatcher rd = request.getRequestDispatcher("/"); rd.forward(request, response); } } protected static boolean isServerRoute(HttpServletRequest request) { if (request.getMethod().equals("GET")) { String uri = request.getRequestURI(); if (uri.startsWith("/app")) { return true; } return PATTERN.matcher(uri).matches(); } return true; } }
答案 1 :(得分:3)
使用REST控制器的解决方案
第一步是配置客户端,在useHash: false
中设置app-routing-module.ts
在index.html,
中将<base href="./" />
更改为<base href="/" />
但这还不够,因为它不支持深层链接,这意味着要从外部链接(例如从邮件或另一个网站)链接到客户端路由,或者在刷新浏览器中的页面时转向ve。
当进行深层链接时,服务器首先接收到请求,并且如果必须由客户端来处理URL,则将找不到该URL,因此我们的服务器应用必须检测URL是否必须由服务器按原样提供(例如,所有API调用)或转发到index.html,以便javascript应用可以对其进行解释。
ClientRouteForwarder .java
package com.mycompany.myapp.web.rest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; /** * A REST controller that forwards all GET requests that did not match a RequestMapping to /index.html so that * client routes can be handled by client code in browser. * * This works because Spring resolves exact matches first. */ @Controller public class ClientRouteForwarder { @GetMapping(value = "/**/{[path:[^\\.]*}") public String forward() { return "forward:/"; } }
答案 2 :(得分:0)
根据其官方文档Configuring html5,AngularJS uses a “#” in it’s urls. HTML5Mode of AngularJS removes these “#” from URL
。
激活HTML 5模式
在html5.mode.config.js
目录中创建webapp/app/blocks/config/
文件:
(function() {
'use strict';
angular
.module('<YourAppName>')
.config(html5ModeConfig);
html5ModeConfig.$inject = ['$locationProvider'];
function html5ModeConfig($locationProvider) {
$locationProvider.html5Mode({ enabled: true, requireBase: true });
}
})();
然后打开index.html
并将此行添加到head标签中:
<base href="/">