cari

Rumah  >  Soal Jawab  >  teks badan

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

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粉807397973P粉807397973365 hari yang lalu442

membalas semua(1)saya akan balas

  • P粉138871485

    P粉1388714852024-01-11 16:18:40

    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