英文:
Passing results from ServerRequest handler function to HTML/Thymeleaf
问题
我决定开始学习Spring Boot和Webflux,以便制作一个在我脑海中存在已久的Web服务。我对这方面完全是新手,但我了解一些Java,所以我正在尝试一些示例,并遇到了一些无法解决的问题。
目前,我正在尝试通过Thymeleaf将处理程序函数生成的结果(视频名称和链接)显示在我的HTML页面上,但页面上不会显示。
**处理程序函数**
---------------------
(这里有一个独立的路由器函数)
```java
public Mono<ServerResponse> listVideos(ServerRequest request) {
Flux<Path> files = fileService.getAllFiles();
Flux<VideoDetails> videoDetailsFlux = files.map(path -> {
VideoDetails videoDetails = new VideoDetails();
videoDetails.setName(path.getFileName().toString());
videoDetails.setLink(request.uri().toString() + '/' + videoDetails.getName());
return videoDetails;
}).filter(videoDetails -> !videoDetails.getName().startsWith(".")).doOnError(t -> {
throw Exceptions.propagate(t);
});
return ServerResponse.ok()
.contentType(MediaType.TEXT_HTML) // 这里原来是APPLICATION_JSON
.render("videos", videoDetailsFlux);
}
@Data
private static class VideoDetails {
private String name;
private String link;
}
原本这个函数返回的是JSON。我知道我需要使用JavaScript将JSON传递到网页,但我想使用Thymeleaf来完成。除非JavaScript是更好的方法?
videos.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Stream Home</title>
</head>
<body>
<h1>这是视频页面</h1>
</div>
<table id="allMovies" class="table table-striped">
<thead>
<tr>
<th width="70%">名称</th>
<th>链接</th>
</tr>
</thead>
<tbody>
<tr class="" data-th-each="video : ${VideoDetailsFlux}">
<td>${video.name}</td>
<td>[[${video.link}]]</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
我还尝试将这个方法放到自己的控制器类中,像这样:
VideoController.java
@GetMapping("/videos")
public Mono<ServerResponse> listVideos(ServerRequest request, final Model model) {
Flux<Path> files = fileService.getAllFiles();
Flux<VideoDetails> videoDetailsFlux = files.map(path -> {
VideoDetails videoDetails = new VideoDetails();
videoDetails.setName(path.getFileName().toString());
videoDetails.setLink(request.uri().toString() + '/' + videoDetails.getName());
return videoDetails;
}).filter(videoDetails -> !videoDetails.getName().startsWith(".")).doOnError(t -> {
throw Exceptions.propagate(t);
});
IReactiveDataDriverContextVariable reactiveDataDrivenMode =
new ReactiveDataDriverContextVariable(videoDetailsFlux);
model.addAttribute("videos", reactiveDataDrivenMode);
System.out.println("|||||||||||||||LIST_VIDEOS");
return ServerResponse.ok().render("videos");
}
@Data
private static class VideoDetails {
private String name;
private String link;
}
videos.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Stream Home</title>
</head>
<body>
<h1>这是视频页面</h1>
</div>
<table id="allMovies" class="table table-striped">
<thead>
<tr>
<th width="70%">名称</th>
<th>链接</th>
</tr>
</thead>
<tbody>
<tr class="" data-th-each="video : ${videos}">
<td>${video.name}</td>
<td>[[${video.link}]]</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
但我猜我不能混合使用ServerRequest
和Model
,因为我会得到(type=Internal Server Error,status=500)NullPointerException。然后我尝试将其作为仅返回字符串的方法,但我不知道如何在没有“response.uri()”的情况下获取URI。也许有人可以帮助我更好地理解我做错了什么。我可能对这应该如何工作有一些误解。
编辑:
我像这样让它工作了:
但是,我不确定自制的BaseUri
是否比“ServerRequest request”更好。这会在以后造成问题吗?
VideoController.java
@GetMapping("/videos")
public String listVideos(final Model model) {
String baseUrl = "/videos";
Flux<Path> files = fileService.getAllFiles();
Flux<VideoDetails> videoDetailsFlux = files.map(path -> {
VideoDetails videoDetails = new VideoDetails();
videoDetails.setName(path.getFileName().toString());
videoDetails.setLink(baseUrl + '/' + videoDetails.getName());
return videoDetails;
}).filter(videoDetails -> !videoDetails.getName().startsWith(".")).doOnError(t -> {
throw Exceptions.propagate(t);
});
IReactiveDataDriverContextVariable reactiveDataDrivenMode =
new ReactiveDataDriverContextVariable(videoDetailsFlux);
model.addAttribute("videos", reactiveDataDrivenMode);
System.out.println("|||||||||||||||LIST_VIDEOS");
return "videos";
}
**videos.html**
```html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Stream Home</title>
</head>
<body>
<h1>这是视频页面</h1>
</div>
<table id="allMovies" class="table table-striped">
<thead>
<tr>
<th width="70%">名称</th>
<th>链接</th>
</tr>
</thead>
<tbody>
<tr class="" data-th-each="video : ${videos}">
<td>[[${video.name}]]</td>
<td><a th:href="${video.link}">[[${video.link}]]</a></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
<details>
<summary>英文:</summary>
I decided to get into Spring-Boot and Webflux to make a web service that was on my mind for some years. I'm totally new to this, but I know some Java, so I'm playing around with some examples and got into some problems that I can't figure out.
Right now I'm trying to display the results(videoname and Link) a handler function generates to my HTML page via Thymeleaf but it wont show on the page.
<br />
<br />
**Handeler Function**
---------------------
(There is a seperate router function)
public Mono<ServerResponse> listVideos(ServerRequest request) {
Flux<Path> files = fileService.getAllFiles();
Flux<VideoDetails> videoDetailsFlux = files.map(path -> {
VideoDetails videoDetails = new VideoDetails();
videoDetails.setName(path.getFileName().toString());
videoDetails.setLink(request.uri().toString() + '/' + videoDetails.getName());
return videoDetails;
}).filter(videoDetails -> !videoDetails.getName().startsWith(".")).doOnError(t -> {
throw Exceptions.propagate(t);
});
return ServerResponse.ok()
.contentType(MediaType.TEXT_HTML) //This was APPLICATION_JSON
.render("videos", videoDetailsFlux);
/*
.cacheControl(CacheControl.noCache())
.location(request.uri())
.body(videoDetailsFlux, VideoDetails.class);
*/
}
@Data
private static class VideoDetails {
private String name;
private String link;
}
This originally returned JSON. I know I would have to use Javascript to pass the JSON to the web page but I'd like to do it with thymeleaf. Unless js would be the better way to do it?
<br />
<br />
**videos.html**
---------------
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Stream Home</title>
</head>
<body>
<h1 > THIS IS THE VIDEOS PAGE</h1>
</div>
<table id="allMovies" class="table table-striped">
<thead>
<tr>
<th width="70%">Name</th>
<th>Link</th>
</tr>
</thead>
<tbody>
<tr class="" data-th-each="video : ${VideoDetailsFlux}">
<td>${video.name}</td>
<td>[[${video.link}]]</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
<br />
<br />
I also tried to put the method into its own controller class like this:
**VideoController.java**
------------------------
@GetMapping("/videos")
public Mono<ServerResponse> listVideos(ServerRequest request, final Model model) {
Flux<Path> files = fileService.getAllFiles();
Flux<VideoDetails> videoDetailsFlux = files.map(path -> {
VideoDetails videoDetails = new VideoDetails();
videoDetails.setName(path.getFileName().toString());
videoDetails.setLink(request.uri().toString() + '/' + videoDetails.getName());
return videoDetails;
}).filter(videoDetails -> !videoDetails.getName().startsWith(".")).doOnError(t -> {
throw Exceptions.propagate(t);
});
IReactiveDataDriverContextVariable reactiveDataDrivenMode =
new ReactiveDataDriverContextVariable(videoDetailsFlux);
model.addAttribute("videos", reactiveDataDrivenMode);
System.out.println("|||||||||||||||LIST_VIDEOS");
return ServerResponse.ok().render("videos");
}
@Data
private static class VideoDetails {
private String name;
private String link;
}
**videos.html**
---------------
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Stream Home</title>
</head>
<body>
<h1 > THIS IS THE VIDEOS PAGE</h1>
</div>
<table id="allMovies" class="table table-striped">
<thead>
<tr>
<th width="70%">Name</th>
<th>Link</th>
</tr>
</thead>
<tbody>
<tr class="" data-th-each="video : ${videos}">
<td>${video.name}</td>
<td>[[${video.link}]]</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
But i guess I can't mix ServerRequest and Model, because I get (type=Internal Server Error, status=500) NullPointerException.
Then I tried it as a String only method, but then I don't know how to get the uri() without "response.uri()".
Maybe someone could help me to better understand what I'm doing wrong. I probably have a general missunderstanding of how this should work.
<br />
<br />
**EDIT:**
---------
I got it working like this:<br />
But I'm not sure about the selfmade BaseUri instead of a "ServerRequest request". Cold this cause any problems later on?
<br />
**VideoController.java**
@GetMapping("/videos")
public String listVideos(final Model model) {
String baseUrl = "/videos";
Flux<Path> files = fileService.getAllFiles();
Flux<VideoDetails> videoDetailsFlux = files.map(path -> {
VideoDetails videoDetails = new VideoDetails();
videoDetails.setName(path.getFileName().toString());
videoDetails.setLink(baseUrl + '/' + videoDetails.getName());
return videoDetails;
}).filter(videoDetails -> !videoDetails.getName().startsWith(".")).doOnError(t -> {
throw Exceptions.propagate(t);
});
IReactiveDataDriverContextVariable reactiveDataDrivenMode =
new ReactiveDataDriverContextVariable(videoDetailsFlux);
model.addAttribute("videos", reactiveDataDrivenMode);
System.out.println("|||||||||||||||LIST_VIDEOS");
return "videos";
}
<br />
**videos.html**
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Stream Home</title>
</head>
<body>
<h1 > THIS IS THE VIDEOS PAGE</h1>
</div>
<table id="allMovies" class="table table-striped">
<thead>
<tr>
<th width="70%">Name</th>
<th>Link</th>
</tr>
</thead>
<tbody>
<tr class="" data-th-each="video : ${videos}">
<td>[[${video.name}]]</td>
<a th:href=${video.link}>[[${video.link}]]</a>
</tr>
</tbody>
</table>
</div>
</body>
</html>
</details>
专注分享java语言的经验与见解,让所有开发者获益!
评论