I am having a lot of issues routing my pages with Spring-Boot and AngularJS. Once the index.html page renders I get a 404 error when I click on a new link. AngularJS in loading fine, and the controllers etc are also loading.
Also worth noting I have to use this <base href="/"> when I run it on my IDE and this <base href="/personalsite/"> when I package it into a war file on deploy it on tomcat.
index.html
<html lang="en" ng-app="SharpVision">
<head>
<base href="/">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>Drew</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="Drew's personal site" content="Drew" />
<meta name="author" content="AHthemes" />
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<!--<script src="http://a1alfred.com/sharpvisionbs3/v2/angular/assets/libs/html5shiv/html5shiv.min.js"></script>-->
<!--<script src="http://a1alfred.com/sharpvisionbs3/v2/angular/assets/libs/respond/respond.min.js"></script>-->
<![endif]-->
<!--[if gte IE 9]>
<style type="text/css">
.gradient {
filter: none;
}
</style>
<![endif]-->
<link href="assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="assets/css/styles.css" rel="stylesheet">
</head>
<body>
<ng-include src="'views/structure/navigation.tpl.html'"></ng-include>
<ng-view autoscroll="true"></ng-view>
<ng-include src="'views/structure/footer.tpl.html'"></ng-include >
<!--Scripts-->
<!--<script src="bower_components/masonry/dist/masonry.pkgd.min.js"></script>-->
<!--<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>-->
<script src="assets/libs/flexslider/jquery.flexslider.js"></script>
<script src="assets/libs/masonry/masonry.pkgd.min.js"></script>
<script src="assets/libs/imagesloaded/imagesloaded.pkgd.min.js"></script>
...
app.config
app.config(function($routeProvider, $locationProvider){
$routeProvider.when('/',{
templateUrl: "views/misc/home.tpl.html",
controller: "HomeController"
})
.when('/features/blog',{
templateUrl: "views/features/blog.tpl.html",
controller: "BlogController"
})
.when('/features/blogpost',{
templateUrl: "views/features/blog-post.tpl.html",
controller: "BlogPostController"
})
.when('/features/portfolio',{
templateUrl: "views/features/portfolio.tpl.html",
controller: "PortfolioController"
})
.when('/features/portfolioitem',{
templateUrl: "views/features/portfolio-item.tpl.html",
controller: "PortfolioItemController"
})
.when('/features/gallery',{
templateUrl: "views/features/gallery.tpl.html",
controller: "GalleryController"
})
.when('/features/404',{
templateUrl: "views/features/404.tpl.html",
controller: "404Controller"
})
.when('/features/500',{
templateUrl: "views/features/500.tpl.html",
controller: "500Controller"
})
.when('/contact',{
templateUrl: "views/misc/contact.tpl.html",
controller: "ContactController"
})
.otherwise({redirectTo:'/'});
$locationProvider.html5Mode(true);
});
Once the page loads I select gallery:
at: http://localhost:8080/features/portfolioitem
which give me a 404:
However all this ONLY happens when I create a war file and deploy it on Tomcat. On my intellij IDE all I have to do it change <base href="/personalsite/"> to <base href="/"> and it works fine.
It appears once the application starts loads everything fine but handles the routing through Spring's #RestController so I tried this:
//try mapping to contact and gallery and see what happens
#RequestMapping(value = "/features/portfolioitem", method = RequestMethod.GET)
public String galleryPage(){
System.out.println("HIT HIT HIT HIT HIT!!!");
return "personalsite/features/portfolioitem";
}
galleryPage() renders a page but just the String personalsite/features/portfolioitem on the page.
---------------------------UPDATE 1------------------------------------
I decided to see if I could use Spring-boot `#RestController` to route my pages but that was unsuccessful. This is how I did it:
//try mapping to contact and gallery and see what happens
#RequestMapping(value = "/features/portfolioitem", method = RequestMethod.GET)
#ResponseBody
public String galleryPage(){
System.out.println("HIT HIT HIT HIT HIT!!!");
return "views/features/portfolioitem";
}
#RequestMapping(value = "/", method = RequestMethod.GET)
#ResponseBody
public String indexPage(){
System.out.println("I be in here too");
return "index";
}
WebConfig.java
#Configuration
public class WebConfig extends WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter{
private static final Logger LOGGER = Logger.getLogger(WebConfig.class);
#Bean
InternalResourceViewResolver internalResourceViewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/webapp/");
viewResolver.setSuffix(".html");
return viewResolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
configurer.enable();
}
}
This this only renders the string of whatever the method returns and Angular doe not load.
---------------------Update 2----------------------------
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<!-- Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" />
-->
<!--APR library loader. Documentation at /docs/apr.html -->
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
--------------------Update 3------------------------------
I added server.contextPath=/personalsite/ to my application.properties
I then change <base href="/"> to this is good because now my IDE is doing the same thing as tomcat. Index.html will render athttp://localhost:8081/personalsite/#/BUT once I click a link I get a blank page, for examplehttp://localhost:8081/features/portfolioitembutpersonalsite` is missing from the path. I add that and I get the following page:
Update 1:
You should add 'server.contextPath=/' to your 'application.properties' file.
Update 2:
You need to remove the #ResponseBody annotation if you want Spring to resolve your view, rather than return a string, from your galleryPage method.
Update 3:
If your entire application is a single page application, with embedded templates, and you want your application, when refreshed on any valid path (like '/features/portfolioitem') to render index.html, you need to route every request to your index method. Try this:
#RequestMapping(value = "/**", method = RequestMethod.GET)
public String indexPage(){
System.out.println("I be in here too");
return "index";
}
This should load your index.html for all URLS. It is then up to your application to determine if the URL is valid.
Also, make sure you have $locationProvider.html5Mode(true); set in your app.
Related
I have successfully installed and run the React + Redux starter template that is provided with .Net Core 2.1 (I'm talking about the project created withdotnet new reactredux).
Now I would like to change my entry point from index.html to my own controller inside the Asp.Net.Core project.
Since the create-react-app is configured to use index.html as an entry point, I've had to:
Delete the index.html file
"Eject" the dependencies (npm run eject) in order to modify the configuration file for webpack (I removed the InterpolateHtmlPlugin and HtmlWebpackPlugin plugins from webpack config, as well as all references to index.html in build.js and start.js
Add a HomeController.cs to the project:
namespace ReactReduxSpa.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
}
Add the corresponding Index.cshtml file to my project with pretty much the same content as the former index.html file, except that I now manual insert the generated script bundle:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<base href="~/" />
<link rel="manifest" href="~/manifest.json">
<link rel="shortcut icon" href="~/favicon.ico">
</head>
<body>
<div id="root"></div>
<script src="~/static/js/bundle.js" asp-append-version="true"></script>
</body>
</html>
In Startup.cs I changed app.UseMvc(…) to:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
My home controller is now executed when accessing the site and the site itself works as intended. However, I'm getting thousands of errors in the console for these requests:
Looking at the response, I see that the server simply served the content of my Index.cshtml view instead of letting these requests through. I assume these are used for development to sync the site and the sources.
To sum it up: I don't think that I'm doing it the right way, but I don't know how to do it differently. My goal is simply to replace the static index.htm with some dynamic content (some bits need to be generated on server side).
I'm new to Angular and I'm kind of learning js as I go, but I'm trying to run Angular on the front end with Spring Boot. By default Spring renders my index.html page normally, but I'm getting a 404 not found error when I try to go to the /login page. I've tried just about every url combination I can think of for the template url, but no luck. Because I'm new at Angular, I cannot determine if this is a Spring config issue or something simpler.
Angular controller app.js:
var wishingWell = angular.module('wishingWell', [ 'ngRoute' ]);
wishingWell.config(function($routeProvider, $httpProvider) {
$routeProvider.when('/', {
templateUrl : 'static/home.html',
controller : 'home'
}).when('/login', {
templateUrl : 'static/login.html',
controller : 'navigation'
}).otherwise('/');
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
});
wishingWell.controller('home', function($scope, $http) {
$http.get('/resource/').success(function(data) {
$scope.greeting = data;
})
});
wishingWell.controller('navigation', function() {});
index.html:
<!doctype html>
<html ng-app="wishingWell">
<head>
<meta charset="UTF-8"/>
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<link rel="stylesheet" href="/css/normalize.css"/>
<link rel="stylesheet" href="/css/main.css"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-route.js"></script>
<script src="https://code.jquery.com/jquery-3.1.1.js" integrity="sha256-16cdPddA6VdVInumRGo6IbivbERE8p7CQR3HzTBuELA=" crossorigin="anonymous"></script>
<script src="/javascript/Angular/app.js"></script>
<title>Wishing Well</title>
</head>
<body ng-controller="home">
<nav class="nav">
<ul class="navback">
<li>
<a class="active" href="/">The Well</a>
</li>
<li>
<a href="/profile" >Profile</a>
</li>
<li>
Sign Up
</li>
<li>
Log In
</li>
<li>
Sign Out
</li>
</ul>
</nav>
<div ng-view></div>
</body>
</html>
folder structure:
Any ideas?
It has nothing to do with Spring as clicking on login link is getting rendered on client side and not on server side (not an ajax call or page submit).
I can see few issues here:
-> In index.html link for angular-route is incorrect
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-route.js"></script>
it should be
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-route.js"></script>
you forgot https:
-> You need to use #/login in anchor tag, else your page will be redirected to .login that doesn't exists
Log In
->index.html and login.html are in same directory, so in app.js template url should be login.html and not static/login.html
You can see it working here
https://plnkr.co/edit/YpAm5FVG2ME7s3EeAigY?p=preview
Note: I have moved index.html outside static folder to make it work with plunker and updated app.js path in index.html accordingly.
If it still doesnt work, press f12 (if using chrome) an post console output.
The root cause (#coder response found the surface level errors) of why these pages wouldn't load was that Spring security was blocking the file paths. I added the templates into the ant matchers and it works. This documentation is where I found this; something easily skipped over when going through it: https://spring.io/guides/tutorials/spring-security-and-angular-js/
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/index.html", "/sign-up.html","/login.html", "/").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable();
}
Dispatcher:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:component-scan base-package="com.nt.beans" />
<mvc:resources mapping="/resources/**" location="/" />
<mvc:annotation-driven />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/views/</value>
</property>
<property name="suffix">
<value></value>
</property>
</bean>
</beans>
Controller class:
#Controller
public class IndexController {//Serves main index.html
#RequestMapping("/**")
public String getIndexPage() {
return "/";
}
}
UI-Router:
myApp.config(function ($stateProvider, $locationProvider, $urlRouterProvider) {
// For any unmatched url, redirect to /login
$urlRouterProvider.otherwise("/Login");
// Now set up the states
$stateProvider
.state('Login', {
url: "/Login",
views: {
header: header,
content: {
templateUrl: 'views/Login.html',
controller: function () {
}
},
footer: footer
}
});
$locationProvider.html5Mode({
enabled: false,
requireBase: true
});
});
index jsp page:
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="en" ng-app="myApp">
<head>
<title>BulkSMS - HTML</title>
<base href="/BulkSMS2/">
<!-- Bootstrap core CSS -->
<link href="resources/bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
<!--font-awesome-->
<link href="resources/bower_components/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css"/>
<!--Bootstrap social-->
<link href="resources/bower_components/bootstrap-social/bootstrap-social.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div id="wrap">
<!--main content-->
<div ui-view="content"></div>
</div>
<!--Bootstrap core JavaScript-->
<script src="resources/bower_components/bootstrap/dist/js/bootstrap.min.js" type="text/javascript"></script>
<!--angular related scripts-->
<script src="resources/bower_components/angular/angular.min.js" type="text/javascript"></script>
<script src="resources/bower_components/angular-ui/build/angular-ui.min.js" type="text/javascript"></script>
<script src="resources/bower_components/angular-ui-router/release/angular-ui-router.min.js" type="text/javascript"></script>
<script src="resources/js/angular_module.js" type="text/javascript"></script>
<script src="resources/js/controllers/StateProvider.js" type="text/javascript"></script>
<script src="resources/js/controllers/LoginController.js" type="text/javascript"></script>
</body>
</html>
When I run the app, it is supposed to load login.html but it showing error in console like:
Resource interpreted as Stylesheet but transferred with MIME type text/html: "http://localhost:8084/BulkSMS2/resources/bower_components/bootstrap-social/bootstrap-social.css"
The prefix in dispatcher.xml is WEB-INF/views so the login.html page inside it should load and also in the controller I am returning the path as / so any unmatched url pattern should load login page according to state provider.
I'm tring build single page app, but i have problem with angular routing it doesnt work.
I'm using spring boot, thymeleaf and angular 1.4.8.
I'm basing on this example https://code.angularjs.org/1.4.8/docs/api/ngRoute/directive/ngView
Here is my main controller:
#Controller
public class HomeController {
#RequestMapping(value = "/")
public String index(){
return "index";
}
}
Here is my index.html
<!DOCTYPE html>
<html>
<head ng-app="ngViewExample">
<title></title>
</head>
<body>
<div ng-controller="MainCtrl as main">
test
<div ng-view=""></div>
</div>
<script type="text/javascript" th:src="#{/js/lib/angular.js}" />
<script type="text/javascript" th:src="#{/js/lib/angular-route.js}" />
<script type="text/javascript" th:src="#{/js/lib/angular-resource.js}" />
<script type="text/javascript" th:src="#{/js/lib/underscore.js}" />
<script type="text/javascript" th:src="#{/js/lib/restangular.js}" />
<script type="text/javascript" th:src="#{/js/src/index.js}" />
</body>
</html>
index.js
var home = angular.module('ngViewExample', ['ngRoute']);
home.config(['$routeProvider',
function ($routeProvider) {
$routeProvider
.when('/test', {
templateUrl: 'test.html',
controller: 'TestCtrl',
controllerAs: 'test'
})
}])
.controller('MainCtrl',
function() {
})
.controller('TestCtrl',
function() {
});
And content I wish to pass to ng-view
test.html
<div>
Hello
</div>
Everything is loading fine but routing just not working here.
you linked me this question yesterday:
here my answer so far
Let's start with How to provide static content via Spring Boot
As shown in this spring blog all resources placed in the directories
/META-INF/resources/
/resources/
/static/
/public/
inside your classpath are added as static resources. So for example just put your index.html into the /public/ directory inside your classpath and you won't need your HomeControlleranymore
Now to the angular part
First of all put the ng-app directive at the <html>or the <body> tag.
I can't see any problems with your angular code so far. Can you verify, that the partial HTML is available at the /test endpoint? If not please follow the steps i told you above. Just put the test.html inside the /public/directory inside your classpath.
Are there any errors in the console if you open the browser devtools and reload the page?
If you could provide your project on Github i will have a look at it.
First of all, Please ng-app directive placement <body> or <html> tag
<body ng-app="ngViewExample">
Template url
templateUrl: 'test'
MvcConfig.java
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/test").setViewName("test");
}
}
SpringBootApp
#SpringBootApplication
#ComponentScan(basePackages = {"com.example.spring"})
public class SpringAngularApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAngularApplication.class, args);
}
}
where do you put test.html. It should be in /resources/static folder. If in /resources/templates it will be process by thymeleaf and need to be added in ViewControllerRegistry
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/test").setViewName("test");
}
}
Whenever I manually enter a url into my browser to a site built with Angular, I get:
'HTTP 404
The resource cannot be found
Requested URL: /register'
The only navigable url is http://localhost:XXXX/index.html, from there I have to navigate around the site with anchor tags.
My config file for Angular looks like this:
app.config(function ($locationProvider, $routeProvider) {
$locationProvider.html5Mode(true);
//.hashPrefix('!')
$routeProvider
.when('/', {
templateUrl: '/Client/AngularViews/home.html'
})
.when('/register', {
templateUrl: '/Client/AngularViews/register.html',
controller: 'registerController'
})
.when('/post-register', {
templateUrl: '/Client/AngularViews/postRegister.html',
controller: 'registerController'
})
.otherwise({ templateUrl: 'Client/AngularViews/home.html' });
});
Index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" data-ng-app="app">
<head>
<script type="text/javascript" src="Scripts/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="Scripts/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.3/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular-route.js"></script>
<link href="Content/bootstrap.min.css" rel="stylesheet" />
<script type="text/javascript" src="Scripts/mainController.js"></script>
<base href="/" />
</head>
<body>
<div class="container-fluid">
<div ng-view></div>
</div>
Can someone explain to me what I'm doing wrong? I want to be able to manually enter urls into the address bar and navigate to anything other than index.html
According to documentation:
Server side
Using this mode requires URL rewriting on server side,
basically you have to rewrite all your links to entry point of your
application (e.g. index.html). Requiring a tag is also
important for this case, as it allows Angular to differentiate between
the part of the url that is the application base and the path that
should be handeled by the application.
https://docs.angularjs.org/guide/$location
So you need some additional support from server-side: configure your http server to reply with index.html for all url you have in your apps.
I use ASP.Net MVC for server side URL rewriting using controllers. Probably not its intended use but works well