Bagaimanakah cara saya menghantar Koleksi dengan betul dari borang saya kepada pengendali acara JS dan kemudian kepada pengawal REST?
P粉807397973
P粉807397973 2024-01-10 17:25:43
0
1
404

Saya cuba menulis semula borang saya dengan cara yang tidak melibatkan sebarang muat semula halaman. Dalam erti kata lain, saya tidak mahu penyemak imbas membuat sebarang permintaan GET/POST semasa diserahkan. jQuery sepatutnya dapat membantu saya menyelesaikan masalah ini. Inilah meja saya (saya ada beberapa):

    <!-- 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>

Ini JS saya:

$(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;
}

Ini ialah pengendali pengawal REST saya:

    // 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 Entiti:

@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;

角色Entiti:

@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;

Apabila saya menekan butang hantar saya melihat ini dalam konsol

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`)]

Nampaknya saya entah bagaimana harus lulus Collection 的 JSON 表示,而不仅仅是 String。在我之前的项目中,没有使用 jQuery,String 已使用我的自定义 Formatter penyahserikatan yang berjaya

@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;
            }
        });
    }

Saya telah google dan nampaknya Thymeleaf tidak mempunyai sebarang kaedah toJson(). Maksud saya saya boleh menulis kaedah saya sendiri tetapi saya tidak tahu cara menggunakannya dalam templat Thymeleaf. Juga, ini mungkin bukan penyelesaian yang optimum

Ini ialah projek Boot, jadi saya mempunyai perpustakaan pengikat data Jackson

Bagaimanakah cara saya menghantar Koleksi dengan betul dari borang saya kepada pengendali acara JS dan kemudian kepada pengawal REST?

Saya menyemak berbilang soalan serupa yang dicadangkan oleh StackOverflow. Ia tidak kelihatan berkaitan (contohnya, ia melibatkan bahasa pengaturcaraan yang berbeza seperti C# atau PHP)

UPD: Saya baru mencuba ini. Malangnya ia tidak berjaya juga! (Mesej ralat yang sama)

// 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)}"/>

Namun, kaedahnya berfungsi seperti yang diharapkan

$(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 mengesyorkan ini

authorities: JSON.parse($('input[name=authorities]').val())

Sungguh pelik sekarang. Pangkalan data masih tidak berubah, walaupun! Konsol IDE kini tidak mempunyai ralat dan tidak menyebut sama sekali permintaan PUT (yang turut hadir dalam percubaan sebelumnya)! Selain itu, log penyemak imbas mempunyai mesej ini

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)

Saya tidak tahu apa maksudnya!

UPD3: GPT4 bijak. Lagi pintar dari saya. Ini benar sekali. Sebab ia tidak berfungsi di UPD2 ialah saya mengabaikan perkara lain yang dikatakan:

Medan kebenaran hendaklah dihantar sebagai tatasusunan objek dan bukannya rentetan.

Ini bermakna saya harus menggunakan kurungan segi empat sama, bukan pendakap, untuk StringJoiner awalan dan akhiran saya:

    // 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();
        };
    }

Saya pun tukar, macam ni

username: $('input[name=username]').val()

Untuk ini (Saya bodoh kerana tidak melakukan ini dengan segera)

username: $(this).find('input[name=username]').val()

Dan - viola - ia kini tersedia!

GPT4 pun perasan dah guna

'input[name=department]'

bukannya

'select[name=department]'

Saya juga menyelesaikan masalah ini

P粉807397973
P粉807397973

membalas semua(1)
P粉138871485
  1. Ia mestilah susunan objek (walaupun ia adalah Collection, bukan tatasusunan), jadi

new StringJoiner(", ", "{", "}")new StringJoiner(", ", "[", "]")p>

  1. Ia sepatutnya menyasarkan kanak-kanak tingkatan

用户名:$('input[name=username]').val()用户名:$(this).find('input[name=username]')。 val() 或更好用户名:$(this).find('[name=username]').val() Tunggu

  1. department perwakilan elemen, jadi

'input[name=department]''select[name=department]''[name=department]'

Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan