I would like to display the data I get from a search, personalized for my taste. At this moment, It is just plain text.
For example, I am searching for "Titanic", and I get the name, a few links, and some information from IMDB.
I have the following code:
search.html
<!DOCTYPE HTML>
<html xmlns:th="https://www.thymeleaf.org">
<head>
<title>Getting Started: Handling Form Submission</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Form</h1>
<form action="#" th:action="#{/search}" th:object="${search}" method="post">
<p>Message: <input type="text" th:field="*{content}" /></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
</body>
</html>
result.html
<!DOCTYPE HTML>
<html xmlns:th="https://www.thymeleaf.org">
<head>
<title>Getting Started: Handling Form Submission</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Result</h1>
<p th:text="'content: ' + ${main.content}"></p>
Submit another message
</body>
</html>
SearchController.java
#Controller
public class SearchController {
#GetMapping("/search")
public String greetingForm(Model model) {
model.addAttribute("search", new Main());
model.addAttribute("main", new Main().getContent());
return "search";
}
#PostMapping("/search")
public String greetingSubmit(#ModelAttribute Main main) {
return "result";
}
}
and Main.java
private String content;
private List<Result> finalList;
private List<Result> resultList;
public void setContent(String content) throws IOException {
//code to compute finalList
}
public List<Result> getContent() {
return this.finalList;
}
The main problem is that I have no ideea where to being with. finalList is a list of objects of type "Result", which have fields such as
private List<String> link = new ArrayList<>();
private String name;
private TitleProp titleProp;
and TitleProp has
private String trailer;
private String rating;
private String description;
private String genre;
I would like to manipulate each field to show it on a different way, such as a table with more rows, etc.
Any link or sample of code would help me a lot to understand Thymeleaf and Spring Boot more.
I am coming with an answer to my question. I managed to get the result I wanted using Ajax, as SnakeDoc suggested. It was a long road, mostly because even if I had a working code, I spent a few hours searching for the Forbidden 403 error on ajax post request.
So, for the js part:
function ajaxPost() {
// Here we prepare data for the JSON
var formData = {
moviename: $("#moviename").val()
}
$.ajax({
type: "POST",
contentType: "application/json",
url: "MYURL",
data: JSON.stringify(formData),
dataType: 'json',
success: function (result) {
{
$.each(result,
function (i, title) {
// do whatever you want with what you got from the server
});
console.log("Success: ", result);
}
console.log(result);
},
error: function (e) {
console.log("ERROR: ", e);
}
});
}
If this seems confusing, I access the fields you can see in my question by
title.name, title.link, title.titleProp.description, etc, inside function (i, title)'s body.
For the HTML,
<label for="moviename" style="margin-right:5px">Title:</label>
<input type="text" class="form-control" id="moviename" placeholder="Enter a title"/>
Where moviename is the variable name you get from the input.
Now, on the backend, we have to configure our path
#PostMapping("/MYURL")
public ResponseEntity<Object> addSearch(#RequestBody SearchCriteria searchCriteria)
throws IOException {
// do whatever you want to get a result. I used a custom class "SearchCriteria"
// which has a getter and a setter for the field
// **private String moviename;**
return ResponseEntity.ok(THIS GETS SENT TO AJAX);
}
The main problem was that I have web.security, and you have two choices. First one, you disabling csrf. You have to add this line to your security config.
http.csrf().disable();
in protected void configure(HttpSecurity http) method.
Or, you add csrf to the ajax request. More info on this topic was discussed here
With thymeleaf, you can display a list in html like so:
<tr th:each="student: ${students}">
<td th:text="${student.id}" />
<td th:text="${student.name}" />
</tr>
More info: https://www.baeldung.com/thymeleaf-iteration
Related
I have a Controller:
#Controller
public class ImageController {
#GetMapping("/upload")
public String uploadImageGet(Model model) {
return "uploadForm";
}
#PostMapping("/upload")
public String uploadImagePost(#ModelAttribute Image image, Model model) throws IOException {
// what should I do here?
return "result";
}
}
An HTML form:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Serving Web Content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<form action="#" th:action="#{/upload}" th:object="${image}" method="post" enctype="multipart/form-data">
<label for="name">Name:</label><br>
<input type="text" id="name" name="name" value=""><br>
<label for="description">Description:</label><br>
<input type="text" id="description" name="description" value=""><br>
<label for="author">Author:</label><br>
<input type="text" id="author" name="author" value=""><br>
<label for="image">Image:</label><br>
<input type="file" id="image" name="image"><br><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
And a class for storing the form data:
public class Image {
private String name;
private String author;
private String description;
private MultipartFile image;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public MultipartFile getImage() {
return image;
}
public void setImage(MultipartFile image) {
this.image = image;
}
}
I want to send the form data to another host (Spring API), but also display a "Uploaded successfully" response page. Because of that I wanted to handle all that in the controller, but can't figure out how to do this. I was able to find some resources on creating a request manually, but this seems like there must be some simpler way to do this. If I'm approaching it wrong please let me know.
It hardly depends what you mean by:
I want to send the form data to another host (Spring API)
One approach would be to send the Data async with REST to another Spring application or any other application using springs rest template: https://www.baeldung.com/rest-template.
And show the user a succesfull html site.
But if you do it async you should keep in mind that the REST call could fail but you showed the user a successfull page..
My recommendation would be to create an additional REST Endpoint in your spring boot application and send the data from the site with an fetch or ajax call via javascript to it, from where it could be sent to the other spring application
I am new to Spring and yesterday I created a simple app. I type book's title, author and genre and it saves it to List<Book>. Book.java contains private fields (title, author, genre).
So creating books and saving them to list works fine. Also I can view all books I have added. So it works fine. But now I want to create and delete them. So I have BookService.java that can add and delete books.
BookService.java
private List<Book> allBooks = new ArrayList<>();
public List<Book> getAllBooks() {
return allBooks;
}
public void addBook(String title, String author, String genre) {
allBooks.add(new Book(title, author, genre));
}
public void deleteBook(Book book) {
allBooks.remove(book);
}
This is stuff in my controller to delete books
#GetMapping("/books/delete")
public String deleteBook(Model model) {
model.addAttribute("BookList", bookService.getAllBooks()); // Works fine
return "delete";
}
#PostMapping("/books/delete")
public String deletedBook(#ModelAttribute Book book, Model model) {
System.out.println(book.getTitle()); // null
bookService.deleteBook(book); // can't delete null so nothing happens to the list
model.addAttribute("deletedBook", book);
return "deletedBookResult";
}
delete.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<h2>Delete page</h2>
<div th:each="bookObj : ${BookList}"> <!-- BookList - all books I add using submit form -->
<form action="#" th:action="#{/books/delete}" th:object="${bookObj}" method="post"> <!-- I SEND bookObj -->
<input type="submit" th:value="${bookObj.title}"/> <!-- WORKS. I GET BOOK'S NAME ON THIS BUTTON-->
</form>
</div>
</body>
</html>
I use th:each="bookObj : ${BookList}". So bookObj is every book I add. That's why I use th:object=${bookObj}. There is a form for each book I added later. And it display's it's title on the button. But when I press it, I get null to IDEA's console and on webpage. Why?
Thank you in advance.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<h2 th:text=" 'You have just deleted' + ${deletedBook.title}"></h2>
<!-- You have just deleted null -->
</body>
</html>
You are not sending anything in your form. Add some hidden fields:
<div th:each="bookObj : ${BookList}">
<form action="#" th:action="#{/books/delete}" method="post">
<input type="hidden" th:name="genre" th:value="${bookObj.genre}"/>
<input type="hidden" th:name="author" th:value="${bookObj.author}"/>
<input type="hidden" th:name="title" th:value="${bookObj.title}"/>
<input type="submit" th:value="${bookObj.title}"/>
</form>
</div>
Or if you don't want to expose your data in html, you can try to use some sort of session objects instead (Probably an overkill, but sometimes can be useful):
#GetMapping("/books/delete")
public String deleteBook(Model model, HttpSession session) {
session.setAttribute("BookList", new Book[]{
new Book("Title", "Tom","genre"),
new Book("Title 2", "Jerry","genre2")}
);
return "delete";
}
#PostMapping("/books/delete")
public String deletedBook(HttpSession session, Integer id, Model model) {
Book[] books = (Book[]) session.getAttribute("BookList");
Book book = books[id];
System.out.println(book);
model.addAttribute("deletedBook", book);
return "deletedBookResult";
}
And use it like:
<div th:each="bookObj, iter : ${session.BookList}">
<form action="#" th:action="#{/books/delete}" method="post">
<input type="hidden" th:name="id" th:value="${iter.index}"/>
<input type="submit" th:value="${bookObj.title}"/>
</form>
</div>
When I am using Jquery with spring MVC I got an error at browser side "Bad Request" and control not going to the controller.While I am using simple form and sending a request to same controller then it is going.
Below is my code please tell me where am I going wrong?
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
<script src="files/jquery-1.10.2.js"></script>
<script src="files/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
var isJpg = function(name) {
return name.match(/jpg$/i)
};
var isPng = function(name) {
return name.match(/png$/i)
};
$(document).ready(function() {
var file = $('[name="file"]');
var imgContainer = $('#imgContainer');
$('#btnUpload').on('click', function() {
var filename = $.trim(file.val());
if (!(isJpg(filename) || isPng(filename))) {
alert('Please browse a JPG/PNG file to upload ...');
return;
}
$.ajax({
url: 'FileData.htm',
type: "POST",
data: new FormData(document.getElementById("fileForm")),
enctype: 'multipart/form-data',
processData: false,
contentType: false
}).done(function(data) {
imgContainer.html('');
var img = '<img src="data:' + data.contenttype + ';base64,'
+ data.base64 + '"/>';
imgContainer.append(img);
}).fail(function(jqXHR, textStatus) {
//alert(jqXHR.responseText);
alert('File upload failed ...');
});
});
$('#btnClear').on('click', function() {
imgContainer.html('');
file.val('');
});
});
</script>
</head>
<body>
<!-- <form name="dlgContent" action="FileData.htm" id="dlgcon" enctype="multipart/form-data" method="POST">
<input type="file" name="excelfile"/>
<input type="submit"/>
</form> -->
<div>
<form id="fileForm">
<input type="file" name="file" />
<button id="btnUpload" type="button">Upload file</button>
<button id="btnClear" type="button">Clear</button>
</form>
<div id="imgContainer"></div>
</div>
</body>
</html>
And my Controller Class in spring mapping given below
#RequestMapping(value="/FileData.htm", method = RequestMethod.POST)
public void FileData(Model model, #RequestParam CommonsMultipartFile[] excelfile, HttpServletRequest request, HttpServletResponse response){
System.out.println("bhjsbfjhsbfbdesfbsfb");
response.setContentType("application/json");
FileData fd = new FileData();
//Map<String, String> data = fd.submitFileData(excelfile);
Gson gson = new Gson();
// String values = gson.toJson(data);
try {
//response.getWriter().write(values);
//System.out.println(values);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
Thanks.
Actually you are sending Json and not html you should use #ResponseBody
#RequestMapping(value="/upload", method = RequestMethod.POST)
public #ResponseBody
FileData upload(MultipartHttpServletRequest request,
#RequestParam String albumName,
HttpServletResponse response) {
Iterator<String> itr = request.getFileNames();
//others code here
Also Don't forget !! to config multipart data ,
Plus send back Object using jackson lib to jquery done function to be work
Gson lib is not good to use with #ResponseBody , we are using Gson with RestTemplate instead.
I have a JSP page (client-side)
<form action="http://localhost:8080/REST-WS/rest/token" method="POST">
<label for="email">Email</label>
<input name="email" />
<br/>
<label for="password">Password</label>
<input name="password" />
<br/>
<input type="submit" value="Submit" />
</form>
It points to a function in REST Web Service (server-side)
#POST
#Produces(MediaType.TEXT_HTML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Code verify(#FormParam("email") String email,
#FormParam("password") String password,
#Context HttpServletResponse servletResponse) throws IOException {
Code code = generateRandomCode(email,password);
return token;
}
The problem is I want to give response to the client side containing the random-generated code from the server side.
First, it will be redirected to another JSP page and then the client side can receive the random-generated code from server.
How do I do it?
The problem is that you can't send arbitrary Java objects in a redirect. You can however add the data into query parameters. For example
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response post(#FormParam("name") String name,
#FormParam("email") String email) {
String message = "Hello " + name + ". Your email is " + email;
URI uri = UriBuilder.fromPath("/index.jsp")
.queryParam("message", message)
.build();
return Response.seeOther(uri).build();
}
Here, you are building a URI from the location of the jsp page, and adding a query parameter to the end of the URI. So the redirect will go to
http://localhost:8080/index.jsp?message=<the message>
From the index.jsp page you can get the parameter with request.getParameter("message"). For example
<h1><%= request.getParameter("message") %></h1>
Another option to work with JSP and Jersey is to implement MVC, which Jersey provides support for. You can check out this answer, though the examples use Maven (to get all the required jars). If you are interested and don't know how to use Maven, let me know and I'll see if I can help you get all the jars you need.
UPDATE
Ajax Example.
Easiest Javascript library to get started with (if you have no experience) is jQuery. I won't really give much explanation about the code, that's kinda out of scope. I would go through some tutorials (W3Schools has some good getting started guides), and there are answers all over SO that can answer your questions.
Here's a complete working html page. Just change var url = "/api/submit"; to whatever endpoint you are sending the request to.
<!DOCTYPE html>
<html>
<head>
<title>Ajax Example</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script>
$(document).ready(function(){
var url = "/api/submit";
$("#submitBtn").click(function(e) {
e.preventDefault();
var formData = $("#nameForm").serialize();
$.ajax({
url: url,
data: formData,
dataType: "json",
type: "POST",
success: function(data) {
var message = data.message;
var date = data.date;
var h1 = $("<h1>").text(message);
var h3 = $("<h3>").text(date);
$("#content").empty()
.append(h1).append(h3);
},
error: function(jqxhr, status, errorMsg) {
alert(status + ": " + errorMsg);
}
});
});
});
</script>
</head>
<body>
<div id="content">
<form id="nameForm">
First Name: <input type="text" name="fname"/><br/>
Last Name : <input type="text" name="lname"/><br/>
<button id="submitBtn">Submit</button>
</form>
</div>
</body>
</html>
Here is the test resource class
#Path("submit")
public class FormResource {
public static class Model {
public String message;
public String date;
}
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Model post(#FormParam("fname") String fname,
#FormParam("lname") String lname) {
String message = "Hello " + fname + " " + lname;
Model model = new Model();
model.message = message;
model.date = new Date().toString();
return model;
}
}
You will need to make sure you have a JSON provider to handle the JSON Pojo serialization or it won't work (the Model won't be able to serizalize to JSON).
hi i'm working in a spring mvc project and i'm getting this error when i hit the button in my form
500 (Internal Server Error) jquery.min.js:6
x.ajaxTransport.x.support.cors.e.crossDomain.send jquery.min.js:6
x.extend.ajax AddUser:19
doAjaxPost AddUser:41
onclick
i'm trying to do a simple AJAX JQuery example that adds users to a list but i get that error when i press the add button in my form
this is my controller class:
#Controller
public class UserListController {
private List<User> userList = new ArrayList<User>();
#RequestMapping(value="AddUser",method=RequestMethod.GET)
public String showForm(){
return "AddUser";
}
#RequestMapping(value="AddUser",method=RequestMethod.POST)
public #ResponseBody String addUser(#ModelAttribute(value="user") User user, BindingResult result )
{
String returnText;
if(!result.hasErrors())
{
userList.add(user);
returnText = "User has been added to the list. Total number of users are " + userList.size();
}
else
{
returnText = "Sorry, an error has occur. User has not been added to list.";
}
return returnText;
}
#RequestMapping(value="ShowUsers")
public String showUsers(ModelMap model)
{
model.addAttribute("Users", userList);
return "ShowUsers";
}
}
and this is my AddUser.jsp page
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Add Users using ajax</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<!-- <script src="resources/js/libs/jquery-2.0.2.min.js"></script> -->
<script type="text/javascript">
function doAjaxPost() {
// get the form values
var name = $('#name').val();
var education = $('#education').val();
$.ajax({
type: "POST",
url: "AddUser",
data: "name=" + name + "&education=" + education,
success: function(response){
// we have the response
$('#info').html(response);
$('#name').val('');
$('#education').val('');
},
error: function(e){
alert('Error: ' + e);
}
});
}
</script>
</head>
<body>
<h1>Add Users using Ajax ........</h1>
<table>
<tr><td>Enter your name : </td><td> <input type="text" id="name"><br/></td></tr>
<tr><td>Education : </td><td> <input type="text" id="education"><br/></td></tr>
<tr><td colspan="2"><input type="button" value="Add Users" onclick="doAjaxPost()"><br/></td></tr>
<tr><td colspan="2"><div id="info" style="color: green;"></div></td></tr>
</table>
Show All Users
</body>
</html>
and my MvcConfiguration class since i'm using a java based configuration and not using XML
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = { "controllers" })
public class MvcConfig extends WebMvcConfigurerAdapter
{
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
// JSP VIEW-RESOLVER
#Bean
public InternalResourceViewResolver jspViewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setOrder(2);
bean.setPrefix("/WEB-INF/views/");
bean.setSuffix(".jsp");
return bean;
}
}
EDIT: i starter a new project just for the sake of trying to know what error i'm having, i delete spring secuirty in my application, but i still can figure out whats wrong.
1) i actually dont delete spring security i just starte a new project to try to solve my url problem
2) i change my controllers and the URL attribute in my ajax script
new RequestMapping controllers:
#RequestMapping(value="AddUser",method=RequestMethod.GET)
i deleted the "/" in the value="AddUser"
i dont have a "/" in any of my controllers if put a "/" in the controllers i have the same 500 Internal server error
This might be because of the CSRF protection which is enabled by default in Java configuration. Try in your configuration...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.csrf().disable();
}
Let me know if this works.
EDIT**
To include CSRF token in AJAX request, if you are using JSON, you need to put it on the http header. Sample JSP example typically would be...
<html>
<head>
<meta name="_csrf" content="${_csrf.token}"/>
<meta name="_csrf_header" content="${_csrf.headerName}"/>
</head>
Then in your javascript call, get this parameters and add it to XMLHttpRequest's header.
Hope this helps.
Further reading
In my case, i had to add the below dependency to my pom
<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.2.2</version> </dependency>