I am not sure how to test thymeleaf controller views in a spring boot webflux application.
The following gives 404 BAD REQUEST:
webTestClient.get()
.uri("/customer-view")
.accept(MediaType.TEXT_HTML)a
.exchange().isOk
I believe that webTestClient doesn't give any server-side views testing support, only mockMvc. But I have only the webflux dependency "org.springframework.boot:spring-boot-starter-webflux", and mockMvc is not present in webflux as it's a blocking client of course.
I am wondering if there is a way to test a thymeleaf view in webflux. For example a method like the following:
#Controller
class CustomerController {
#GetMapping("/customer-view")
suspend fun getCustomerView(model: Model
): String {
val customer = customerService.getCustomer()
model["customer"] = customer
return "customer-view"
}
}
Thanks
My understanding is thymeleaf requires resources to be present, so while you are testing you need to load resources as well.
#Controller
class CustomerController {
#GetMapping("/customer-view")
suspend fun getCustomerView(model:Model):String{
model["something"] = "this is an example"
return "customer-view"
}
}
// make sure that your html template here -> /resource/templates/cusotmer-view.html . Remember to add xmlns attribute to your template, too.
<!DOCTYPE html>
<html xmlns:th="https://www.thymelaf.org" lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="${something}"/>
</body>
</html>
// Now you can test this
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
import org.springframework.test.web.reactive.server.WebTestClient
// Again you need resources so add this annotation #WebFluxTest to load them
#WebFluxTest
class CustomerControllerTest{
#Autowired
private lateinit var webTestClient: WebTestClient
#Test
fun testReturns200Ok() {
runBlocking {
webTestClient
.get()
.uri("/customer-view")
.exchange()
.expectStatus().isOk
}
}
}
// I hope this helps
Modifying umit's answer - removing blocking
#RunWith(SpringRunner::class)
#WebFluxTest(CustomerController::class)
class CustomerControllerTest {
#Autowired
private lateinit var webTestClient: WebTestClient
#Test
fun `should return customer view`() {
val customer = Customer("John", "Doe")
`when`(customerService.getCustomer()).thenReturn(customer)
webTestClient.get().uri("/customer-view")
.exchange()
.expectStatus().isOk
.expectBody<String>()
.consumeWith {
//assertions
}
}
}
Related
I am trying to understand how the Session scoped bean work and have tried the example from here.
HelloMessageGenerator.java
public class HelloMessageGenerator {
private String message;
public HelloMessageGenerator() {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
HelloMessageBean.java
#Configuration
public class HelloMessageBean {
#Bean
#Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public HelloMessageGenerator requestScopedBean() {
System.out.println("bean created");
return new HelloMessageGenerator();
}
}
HelloMessageController.java
#Controller
public class HelloMessageController {
#Resource(name = "sessionScopedBean")
HelloMessageGenerator sessionScopedBean;
#RequestMapping("/scopes/session")
public String getSessionScopeMessage(final Model model) {
model.addAttribute("previousMessage", sessionScopedBean.getMessage());
sessionScopedBean.setMessage("Good afternoon!");
model.addAttribute("currentMessage", sessionScopedBean.getMessage());
return "scopesExample";
}
}
When I go to http://localhost:8080/scopes/session I get an error.
scopesExample.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Hello World!</title>
</head>
<body>
<p th:text="${previousMessage}">previous message</p>
<p th:text="${currentMessage}">current message</p>
</body>
</html>
The error I am getting is as if the mapping would not exist:
This application has no explicit mapping for /error, so you are seeing this as a fallback. There was an unexpected error (type=Not Found, status=404).
I was missing the thymeleaf dependency in the pom.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
This is why I was getting a 404 when going to localhost:8080/scopes/request
After adding the thymeleaf dependency the bean scope was working as expected. For a session scope bean the bean is created only once per session or after the configured session timeout. For a request scope bean it is created for every request (ie. each time one hits the endpoint).
I'm trying to upload .pdf file with jQuery AJAX to Spring MVC 5 with Spring Security 5 back-end running on Tomcat and faced multiple issues depending on Spring configuration
NOTE:
File upload should be available without authentication
Front-end
Markup:
<div id="upload-modal" class="modal">
<div class="modal-content">
<h4>Upload</h4>
<form action="#" enctype="multipart/form-data">
<div class="file-field input-field">
<div class="btn">
<span>View...</span>
<input type="file" name="file" accept="application/pdf">
</div>
<div class="file-path-wrapper">
<label>
<input class="file-path validate" type="text">
</label>
</div>
</div>
</form>
</div>
<div class="modal-footer">
Cancel
Upload
</div>
</div>
csrf header for all the requests:
$(document).ready(function () {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function (e, xhr, options) {
xhr.setRequestHeader(header, token);
});
});
Uploading with jQuery AJAX:
$("#upload-bttn").click(function () {
var $uploadModal = $("#upload-modal");
const fileName = $uploadModal.find(".file-path").val();
const extension = fileName.substr(fileName.lastIndexOf(".") + 1);
if (extension === "pdf") {
$.ajax({
url: "/upload",
type: "POST",
data: new FormData($uploadModal.find("form").get(0)),
processData: false,
contentType: false,
success: function () {
console.log("success")
},
error: function () {
console.log("error")
}
});
} else {
M.toast({html: 'Selected file is not .pdf'});
}
});
Back-end
General configuration looks like below. It is modified depending on the cases
Security Initialization:
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(SecurityContext.class);
}
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
}
Application initialization:
public class ApplicationInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) {
servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));
servletContext.getSessionCookieConfig().setHttpOnly(true);
servletContext.getSessionCookieConfig().setSecure(true);
AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
dispatcherServlet.register(WebAppContext.class);
ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
Case 1 - CommonsMultipartResolver bean definition
CommonsMultipartResolver bean definition:
#Bean
public CommonsMultipartResolver multipartResolver(
#Value("${max.upload.size}") Integer maxNumber,
#Value("${max.size}") Integer maxSize) {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(1024 * maxSize * maxNumber);
resolver.setMaxUploadSizePerFile(maxSize);
resolver.setMaxInMemorySize(maxSize);
resolver.setDefaultEncoding("UTF-8");
try {
resolver.setUploadTempDir(new FileSystemResource(System.getProperty("java.io.tmpdir")));
} catch (IOException e) {
e.printStackTrace();
}
return resolver;
}
I remember there was strange Spring behavior when MultipartResolver bean should be named "multipartResolver" explicitly. I tried both #Bean and #Bean("multipartResolver") with configuration above and had same result (despite bean above is named "multipartResolver" as per method name)
Result:
Error 500 - Unable to process parts as no multi-part configuration has been provided
Case 2 - MultipartConfigElement in Servlet registry
removed CommonsMultipartResolver bean
added StandardServletMultipartResolver bean
added MultipartConfigElement to ApplicationInitializer
StandardServletMultipartResolver bean definition:
#Bean
public StandardServletMultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}
Updated ApplicationInitializer:
#Override
public void onStartup(ServletContext servletContext) {
...
servlet.setMultipartConfig(new MultipartConfigElement(
System.getProperty("java.io.tmpdir")
));
}
As per Spring documentation:
Ensure that the MultipartFilter is specified before the Spring Security filter. Specifying the MultipartFilter after the Spring Security filter means that there is no authorization for invoking the MultipartFilter which means anyone can place temporary files on your server. However, only authorized users will be able to submit a File that is processed by your application
As I need to allow not authenticated users to upload the files I tried both before and after in SecurityInitializer as below with the same result
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
or
#Override
protected void afterSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
Result:
Error 403
Questions
What do I miss in the configuration?
Thoughts
CommonsMultipartResolver would be preferable as allows to drive it with Spring properties
Something wrong with Spring Security context setup
There is allowCasualMultipartParsing="true" option (did not test) which I wouldn't like to stick to as its Tomcat specific
Updates
With disabled Spring Security everything works properly
http.authorizeRequests().antMatchers("/**").permitAll(); remains as the only security context configuration so don't think its Security context configuration issue
Set multipart resolver bean name explicitly in MultipartFilter in
beforeSpringSecurityFilterChain(ServletContext servletContext) and still no luck
Adding of _csrf token to the request header did not work for both cases
Realized that I miss additional WebAppContext class in SecurityInitializer constructor. Now error 500 disappeared but 403 appeared for case 1. Logging says that I have invalid csrf token despite I added it to the header like above
Tried to submit the form with csrf token including hidden input <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> yet the result is the same - error 403 with invalid token statement
After two days of struggling:
Constructor should contain both security and application context configuration classes
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(SecurityContext.class, WebAppContext.class);
}
}
Application context (WebAppContext) should contain MultipartResolver bean definition
#Bean
public CommonsMultipartResolver multipartResolver(
#Value("${max.upload.size}") Integer maxNumber,
#Value("${max.size}") Integer maxSize) {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(1024 * maxSize * maxNumber);
resolver.setMaxUploadSizePerFile(maxSize);
resolver.setMaxInMemorySize(maxSize);
resolver.setDefaultEncoding("UTF-8");
try {
resolver.setUploadTempDir(new FileSystemResource(System.getProperty("java.io.tmpdir")));
} catch (IOException e) {
e.printStackTrace();
}
return resolver;
}
In my case after application initialization csrf token inside Spring CsrfTokenRepository was empty for some reason so when Spring been comparing token from client request header with null in CsrfFilter Spring was returning error 403. I configured csrf in security context in the following way:
#Override
protected void configure(HttpSecurity http) throws Exception {
...
http.csrf().csrfTokenRepository(new CookieCsrfTokenRepository());
...
}
Now csrf token is passed in cookies with first server response to the browser and the repository generates and caches a token to compare against the one coming from the client so comparison passes successfully
Here CookieCsrfTokenRepository may also be declared as CookieCsrfTokenRepository.withHttpOnlyFalse() if you would like to grab the token from cookie and set it into csrf header, but I have chosen to go with meta tags approach above
In my current project I have a number of Thymeleaf fragments stored under the src tree src/main/resources/templates/fragments. In my test suite i have a number of test templates that will pull in individual fragments so that I may process them and compare the output with some test output files. These are located in src/test/resources/test-templates/input and look like the following:
title-and-subtitle-test-input.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">>
<body>
<table
th:replace="~{fragments/titleAndSubtitle :: titleAndSubtitleTable(titleText = 'Test Title', subtitleText = 'Test Subtitle')}"></table>
</body>
</html>
and the titleAndSubtitle fragment looks like this:
titleAndSubtitle.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<body>
<table class="fragment" th:fragment="titleAndSubtitleTable(titleText, subtitleText)">
<tr>
<td class="title">[[${titleText}]]</td>
</tr>
<tr>
<td class="subtitle">[[${subtitleText}]]</td>
</tr>
</table>
</body>
</html>
and my test thymeleaf config class looks as follows:
ThymeleafTestConfiguration.java
#Configuration
public class ThymeleafTestConfiguration extends WebMvcConfigurerAdapter implements ApplicationContextAware {
private static final String TEST_TEMPLATE_DIRECTORY = "classpath:/test-templates/input/";
private static final String TEMPLATE_SUFFIX = ".html";
#Autowired
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* THYMELEAF: View Resolver - implementation of Spring's ViewResolver
* interface.
*
* #return ViewResolver
*/
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
}
/**
* THYMELEAF: Template Engine (Spring4-specific version).
*
* #return SpringTemplateEngine
*/
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setEnableSpringELCompiler(true);
templateEngine.addTemplateResolver(templateResolver());
return templateEngine;
}
/**
* THYMELEAF: Template Resolver for test templates.
*
* #return TemplateResolver
*/
private ITemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(applicationContext);
templateResolver.setPrefix(TEST_TEMPLATE_DIRECTORY);
templateResolver.setSuffix(TEMPLATE_SUFFIX);
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setOrder(1);
return templateResolver;
}
}
I haven't been able to correctly configure a Thymeleaf template resolver and engine to be able to locate the test input fiel and still resolve the fragment expression, i.e. pull in the fragment from the src tree. Instead I decided to copy the contents of my fragment directory in the src tree to a temporary folder - src/test/resources/test-templates/input/fragments, with the idea being that the fragments are placed in the test resource temporarily so the thymeleaf template resolver could successfully locate and retrieve them, pull them into the test template for processing, and once all tests have ran the temp directory would be deleted. I am able to successfully copy the directory and its contents to the test tree but I'm getting errors like :
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [test-templates/input//fragments/titleAndSubtitle.html]" - line 5, col 9)
//etc
Caused by: java.io.FileNotFoundException: class path resource [test-templates/input/fragments/titleAndSubtitle.html] cannot be opened because it does not exist
This is similar to the errors I was seeing when I was still messing around with template resolvers. The test looks as follows:
FragmentTest.java
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = ThymeleafTestConfiguration.class)
public class ClaimSnapshotFragmentTest {
private static File sourceFragmentDirectory = new File("src/main/resources/templates/fragments");
private static File tempFragmentDirectory = new File("src/test/resources/test-templates/input/fragments");
#BeforeClass
public static void setupClass() throws Exception {
if(tempFragmentDirectory.exists()) {
deleteDirectory(tempFragmentDirectory);
}
copyDirectory(sourceFragmentDirectory, tempFragmentDirectory);
}
#AfterClass
public static void teardownClass() throws Exception {
if(tempFragmentDirectory.exists()) {
deleteDirectory(tempFragmentDirectory);
}
}
#Autowired
private SpringTemplateEngine templateEngine;
#Test
public void test_titleAndSubtitle_fragmentParsedWithData() throws Exception {
final String expected = FileUtils.getFileFromFolderAsString("title-and-subtitle-test-expected-output.html",
FOLDER_TEST_TEMPLATES_OUTPUT);
final String actual = templateEngine.process("title-and-subtitle-test-input", new Context());
final Diff diff = DiffBuilder
.compare(expected)
.withTest(actual)
.checkForSimilar()
.normalizeWhitespace()
.build();
assertThat(diff.hasDifferences()).isFalse();
}
Am I missing something with the Thymeleaf configuration? I have checked via code and manually that the fragments directory is created, contains the fragment template in question, and is successfully removed after the tests have ran but I'm still getting an error about files not existing. Note: I am using Thymeleaf version 3.0.9.RELEASE with Thymeleaf layout dialect version 2.3.0. The application runs Thymeleaf in "offline mode" and is used primarily to create email bodies by combining data received by MQ and processing it in a template. The copyDirectory and deleteDirectory methods are from Apache Commons FileUtils class
I want to add Paging functionality in Spring Boot + Spring Data JPA App, I have Repository :
#Repository
public interface PostRepository extends JpaRepository<Post, Long> {
List<Post> findAllByOrderByPostIdDesc(Pageable pageable);
}
and in Service class I have :
#Service
public class PostService {
#Autowired
private PostRepository postRepository;
public List<Post> findAll(Pageable pageable) {
return postRepository.findAllByOrderByPostIdDesc(pageable);
}
}
in the end Controller is
#Controller
public class HomeController {
#Autowired
private PostService postService;
#GetMapping(value = "/")
public String getHomePage(Model model, Pageable pageable){
model.addAttribute("postList", postService.findAll(pageable));
return "home";
}
}
the code above on http://localhost:8080 mapping I get all the records as response, but if I pass page and size params as http://localhost:8080/?page=0&size=1 I get paging working.
Now I want to make http://localhost:8080/?page=0&size=1 by default landing page address, and in HTML code how can I dynamically update values of params page and size which is :
<ul class="pagination justify-content-center mb-4">
<li class="page-item">
<a class="page-link" th:href="#{/(page=0,size=1)}">← Older</a>
</li>
<li class="page-item disabled">
<a class="page-link" th:href="#{/(page=0,size=1)}">Newer →</a>
</li>
</ul>
I have tried a many tricks but none worked for me!!
Update
I tried and added this class as suggested but #Nyle Hassan,
#Configuration
public class DefaultView extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers( ViewControllerRegistry registry ) {
registry.addViewController( "/" ).setViewName( "forward:/?page=0&size=10" );
registry.setOrder( Ordered.HIGHEST_PRECEDENCE );
super.addViewControllers( registry );
}
}
it resulted in :
2019-01-01 17:26:43.484 ERROR 3824 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exception
java.lang.StackOverflowError: null
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:597) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:597) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:597) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:597) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:597) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
the code above on http://localhost:8080 mapping I get all the records as response
Are you sure about this? Which version of Spring Boot are you using? I checked latest version (2.1.1) and calling http://localhost:8080 without any query parameters will instantiate a Pageable object with page = 0, size = 20, no sorting. Just try to set a debug point to org.springframework.data.web.PageableHandlerMethodArgumentResolver#resolveArgument to learn, how Spring performs this binding.
This argument resolver also comes with an annotation, which allows you to change defaults, which are used, when query parameters are not present. You can use it like this:
#GetMapping(value = "/")
public String getHomePage(Model model, #PageableDefault(page=0, size = 1) Pageable pageable) {
...
}
Page 0 is annotation default, so this has the same effect:
#GetMapping(value = "/")
public String getHomePage(Model model, #PageableDefault(size = 1) Pageable pageable) {
...
}
in HTML code how can I dynamically update values of params page and size
Next time, please post two separate questions, but here it goes:
First, change List<Post> to Page<Post> in your repository and service. In your controller, bind the result to the model:
Page<Post> page = postService.findAll(pageable);
model.addAttribute("page", page); // page.getContent() contains the List<Page> now
And in the view, use the page to render paging links, e.g. like this:
<li class="page-item">
<a class="page-link" th:if="${page.hasPrevious()}" th:href="#{/(page=${page.previousPageable().getPageNumber()},size=${page.getSize()})}">← Older</a>
</li>
<li class="page-item disabled">
<a class="page-link" th:if="${page.hasNext()}" th:href="#{/(page=${page.nextPageable().getPageNumber()},size=${page.getSize()})}">Newer →</a>
</li>
need your help here please.
I have a login page. After I enter username/password, I want to see Dashboard page. But I am getting 404 page not found. Can anyone please tell what is going on here.
When I hit http://localhost:8080 -> It goes to http://localhost:8080/login - Which is expected.
After I enter username/password, it goes to http://localhost:8080 - Expected: to go to Dashboard page i.e. http://localhost:8080/dashboard
#Component
public class SimpleAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
...
redirectStrategy.sendRedirect(request, response, "dashboard");
}
DashboardController.java
#Controller
#RequestMapping("/dashboard")
public class DashboardController {
#RequestMapping("/")
String init(){
System.out.println("Dashboard - init()");
return "dashboard_init";
}
}
app.component.html
Hello... {{name}}
Hello... {{title}}
<h1>
Welcome {{title}}!
</h1>
<p>Id: <span>{{greeting.id}}</span></p>
<p>Message: <span>{{greeting.content}}!</span></p>
app-routing.module.ts
import {DashboardComponent} from "./dashboard/dashboard.component";
const routes: Routes = [
{ path: '', redirectTo: '/login', pathMatch: 'full' },
// { path: 'dashboard', redirectTo: '/dashboard', pathMatch: 'full' },
{
path: 'dashboard_init',
component: DashboardComponent,
data: { title: 'Dashboard' }
}
];
dashboard.component.ts
#Component({
selector: 'dashboard-component',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css'],
})
export class DashboardComponent implements OnInit {
private currentAssociate: Associate;
constructor(private http: Http,
private router: Router) {
}
ngOnInit(): void {
// initialize services and data
this.http
.get('/dashboard')
.toPromise()
.then(response => {
let data = response.json();
if (data.currentAssociate) this.currentAssociate = data.currentAssociate as Associate;
})
.catch(error => {
// this.alertService.error(error);
});
}
}
dashboard.component.html
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="utf-8" />
<title>Dashboard</title>
</head>
<div>
<B>Dashboard...</B>
</div>
</html>
Error: (When the url is http://localhost:8080/dashboard/)
Dashboard - init()
[2m2018-03-26 10:07:20.421[0;39m [31mERROR[0;39m [35m13184[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36morg.thymeleaf.TemplateEngine [0;39m [2m:[0;39m [THYMELEAF][http-nio-8080-exec-2] Exception processing template "dashboard_init": Error resolving template "dashboard_init", template might not exist or might not be accessible by any of the configured Template Resolvers
[2m2018-03-26 10:07:20.422[0;39m [31mERROR[0;39m [35m13184[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.a.c.c.C.[.[.[/].[dispatcherServlet] [0;39m [2m:[0;39m Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "dashboard_init", template might not exist or might not be accessible by any of the configured Template Resolvers] with root cause
org.thymeleaf.exceptions.TemplateInputException: Error resolving template "dashboard_init", template might not exist or might not be accessible by any of the configured Template Resolvers
at org.thymeleaf.TemplateRepository.getTemplate(TemplateRepository.java:246)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1104)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1060)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1011)
something wrong overall..
in Controller replace top /dashboard with /
#Controller
#RequestMapping("/")
public class DashboardController {
#RequestMapping("/")
String init(){
System.out.println("Dashboard - init()");
return "dashboard_init";
}
}
Also as fas as I remember return "dashboard_init" is expecting dashboard_init.html template to be returned
Probably you want redirect or something to /dashboard_init, do like
#RequestMapping(value = "/", method = RequestMethod.GET)
public void index(final HttpServletResponse response) {
response.setStatus(HttpStatus.OK.value());
response.sendRedirect( "/wherever-you-want");
}
This series of tutorials will help you to integrate Spring Security with Angular, using different approaches. Starting with Basic Auth
Spring Security and Angular