Ich versuche, mein Formular so umzuschreiben, dass keine Seitenaktualisierung erforderlich ist. Mit anderen Worten, ich möchte nicht, dass der Browser beim Senden irgendwelche GET/POST-Anfragen stellt. jQuery sollte mir bei der Lösung dieses Problems helfen können. Hier ist meine Tabelle (ich habe ein paar):
<!-- I guess this action doesn't make much sense anymore --> <form action="/save-user" th:object="${user}" method="post"> <input type="hidden" name="id" th:value="${user.id}"> <input type="hidden" name="username" th:value="${user.username}"> <input type="hidden" name="password" th:value="${user.password}"> <input type="hidden" name="name" th:value="${user.name}"> <input type="hidden" name="lastName" th:value="${user.lastName}"> <div class="form-group"> <label for="departments">Department: </label> <select id="departments" class="form-control" name="department"> <option th:selected="${user.department == 'accounting'}" th:value="accounting">Accounting </option> <option th:selected="${user.department == 'sales'}" th:value="sales">Sales </option> <option th:selected="${user.department == 'information technology'}" th:value="'information technology'">IT </option> <option th:selected="${user.department == 'human resources'}" th:value="'human resources'">HR </option> <option th:selected="${user.department == 'board of directors'}" th:value="'board of directors'">Board </option> </select> </div> <div class="form-group"> <label for="salary">Salary: </label> <input id="salary" class="form-control" name="salary" th:value="${user.salary}" min="100000" aria-describedby="au-salary-help-block" required/> <small id="au-salary-help-block" class="form-text text-muted">100,000+ </small> </div> <input type="hidden" name="age" th:value="${user.age}"> <input type="hidden" name="email" th:value="${user.email}"> <input type="hidden" name="enabledByte" th:value="${user.enabledByte}"> <!-- I guess I should JSON it somehow instead of turning into regular strings --> <input type="hidden" th:name="authorities" th:value="${#strings.toString(user.authorities)}"/> <input class="btn btn-primary d-flex ml-auto" type="submit" value="Submit"> </form>
Das ist mein JS:
$(document).ready(function () { $('form').on('submit', async function (event) { event.preventDefault(); let user = { id: $('input[name=id]').val(), username: $('input[name=username]').val(), password: $('input[name=password]').val(), name: $('input[name=name]').val(), lastName: $('input[name=lastName]').val(), department: $('input[name=department]').val(), salary: $('input[name=salary]').val(), age: $('input[name=age]').val(), email: $('input[name=email]').val(), enabledByte: $('input[name=enabledByte]').val(), authorities: $('input[name=authorities]').val() /* ↑ i tried replacing it with authorities: JSON.stringify($('input[name=authorities]').val()), same result */ }; await fetch(`/users`, { method: 'PUT', headers: { ...getCsrfHeaders(), 'Content-Type': 'application/json', }, body: JSON.stringify(user) // tried body : user too }); }); }); function getCsrfHeaders() { let csrfToken = $('meta[name="_csrf"]').attr('content'); let csrfHeaderName = $('meta[name="_csrf_header"]').attr('content'); let headers = {}; headers[csrfHeaderName] = csrfToken; return headers; }
Das ist mein REST-Controller-Handler:
// maybe I'll make it void. i'm not sure i actually want it to return anything @PutMapping("/users") public User updateEmployee(@RequestBody User user) { service.save(user); // it's JPARepository's regular save() return user; }
User
Entität:
@Entity @Table(name = "users") @Data @EqualsAndHashCode public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column private long id; @Column(nullable = false, unique = true) private String username; @Column(nullable = false) private String password; @Column private String name; @Column(name = "last_name") private String lastName; @Column private String department; @Column private int salary; @Column private byte age; @Column private String email; @Column(name = "enabled") private byte enabledByte; @ManyToMany @JoinTable(name = "user_role", joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id"), @JoinColumn(name = "username", referencedColumnName = "username")}, inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id"), @JoinColumn(name = "role", referencedColumnName = "role")}) @EqualsAndHashCode.Exclude private Set<Role> authorities;
角色
Entität:
@Entity @Table(name = "roles") @Data @EqualsAndHashCode public class Role implements GrantedAuthority { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column private long id; @Column(name = "role", nullable = false, unique = true) private String authority; @ManyToMany(mappedBy = "authorities") @EqualsAndHashCode.Exclude private Set<User> userList;
Wenn ich auf die Schaltfläche „Senden“ drücke, wird dies in der Konsole angezeigt
WARN 18252 --- [io-8080-exec-10] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.HashSet<pp.spring_bootstrap.models.Role>` from String value (token `JsonToken.VALUE_STRING`)]
Es scheint, ich sollte die Collection
的 JSON 表示,而不仅仅是 String
。在我之前的项目中,没有使用 jQuery,String
已使用我的自定义 Formatter
erfolgreiche Deserialisierung irgendwie bestehen
@Override public void addFormatters(FormatterRegistry registry) { registry.addFormatter(new Formatter<Set<Role>>() { @Override public Set<Role> parse(String text, Locale locale) { Set<Role> roleSet = new HashSet<>(); String[] roles = text.split("^\[|]$|(?<=]),\s?"); for (String roleString : roles) { if (roleString.length() == 0) continue; String authority = roleString.substring(roleString.lastIndexOf("=") + 2, roleString.indexOf("]") - 1); roleSet.add(service.getRoleByName(authority)); } return roleSet; } @Override public String print(Set<Role> object, Locale locale) { return null; } }); }
Ich habe es gegoogelt und es scheint, dass Thymeleaf keine toJson()
Methode hat. Ich meine, ich kann meine eigenen Methoden schreiben, weiß aber nicht, wie ich sie in Thymeleaf-Vorlagen verwenden soll. Außerdem ist dies möglicherweise nicht die optimale Lösung
Dies ist ein Boot-Projekt, daher habe ich die Jackson-Datenbindungsbibliothek
Wie übergebe ich eine Sammlung von meinem Formular ordnungsgemäß an einen JS-Ereignishandler und dann an einen REST-Controller?
Ich habe mehrere ähnliche von StackOverflow vorgeschlagene Fragen überprüft. Sie scheinen nicht verwandt zu sein (zum Beispiel beinhalten sie verschiedene Programmiersprachen wie C# oder PHP)
UPD: Ich habe es gerade versucht. Leider hat es auch nicht funktioniert! (Gleiche Fehlermeldung)
// inside my config @Bean public Function<Set<Role>, String> jsonify() { return s -> { StringJoiner sj = new StringJoiner(", ", "{", "}"); for (Role role : s) { sj.add(String.format("{ \"id\" : %d, \"authority\" : \"%s\" }", role.getId(), role.getAuthority())); } return sj.toString(); }; }
<input type="hidden" th:name="authorities" th:value="${@jsonify.apply(user.authorities)}"/>
Die Methode funktioniert jedoch wie erwartet
$(document).ready(function () { $('form').on('submit', async function (event) { /* ↓ logs: authorities input: {{ "id" : 1, "authority" : "USER" }} */ console.log('authorities input: ' + $('input[name=authorities]').val());
UPD2: GPT4 empfiehlt dies
authorities: JSON.parse($('input[name=authorities]').val())
Es ist jetzt wirklich seltsam. Die Datenbank hat sich jedoch immer noch nicht geändert! Die IDE-Konsole weist jetzt keine Fehler mehr auf und die PUT-Anfrage wird überhaupt nicht erwähnt (die auch im vorherigen Versuch vorhanden war)! Außerdem enthält das Browserprotokoll diese Meldung
Uncaught (in promise) SyntaxError: Expected property name or '}' in JSON at position 1 at JSON.parse (<anonymous>) at HTMLFormElement.<anonymous> (script.js:28:31) at HTMLFormElement.dispatch (jquery.slim.min.js:2:43114) at v.handle (jquery.slim.min.js:2:41098)
Ich weiß nicht, was es bedeutet!
UPD3: GPT4 ist intelligent. Jedenfalls schlauer als ich. Das ist absolut wahr. Der Grund, warum es in UPD2 nicht funktioniert, ist, dass ich eine andere Sache ignoriert habe:
Berechtigungsfelder sollten als Array von Objekten und nicht als Zeichenfolgen gesendet werden.
Das bedeutet, dass ich für meine StringJoiner
Präfixe und Suffixe eckige Klammern und keine geschweiften Klammern verwenden sollte:
// I also added some line breaks, but I doubt it was necessary @Bean public Function<Set<Role>, String> jsonify() { return s -> { StringJoiner sj = new StringJoiner(",\n", "[\n", "\n]"); for (Role role : s) { sj.add(String.format("{\n\"id\" : %d,\n\"authority\" : \"%s\"\n}", role.getId(), role.getAuthority())); } return sj.toString(); }; }
Ich habe es auch so geändert
username: $('input[name=username]').val()
Dazu (ich war dumm, das nicht gleich gemacht zu haben)
username: $(this).find('input[name=username]').val()
Und – viola – es ist jetzt erhältlich!
GPT4 hat auch bemerkt, dass ich es verwendet habe
'input[name=department]'
statt
'select[name=department]'
Ich habe dieses Problem auch gelöst
Collection
,而不是一个数组),所以new StringJoiner(", ", "{", "}")
→new StringJoiner(", ", "[", "]")
p>用户名:$('input[name=username]').val()
→用户名:$(this).find('input[name=username]')。 val()
或更好用户名:$(this).find('[name=username]').val()
等等department
由元素表示,因此
'input[name=department]'
→'select[name=department]'
或'[name=department]'