Bagaimana cara kerja anotasi Spring @ResponseBody?

89

Saya memiliki metode yang dijelaskan dengan cara berikut:

/**
* Provide a list of all accounts.
*/
//  TODO 02: Complete this method.  Add annotations to respond
//  to GET /accounts and return a List<Account> to be converted.
//  Save your work and restart the server.  You should get JSON results when accessing 
//  http://localhost:8080/rest-ws/app/accounts
@RequestMapping(value="/orders", method=RequestMethod.GET)
public @ResponseBody List<Account> accountSummary() {
    return accountManager.getAllAccounts();
}

Jadi saya tahu bahwa dengan anotasi ini:

@RequestMapping(value="/orders", method=RequestMethod.GET)

metode ini menangani permintaan GET HTTP yang dibuat ke sumber daya yang diwakili oleh URL / pesanan .

Metode ini memanggil objek DAO yang mengembalikan Daftar .

di mana Akun mewakili pengguna di sistem dan memiliki beberapa bidang yang mewakili pengguna ini, seperti:

public class Account {

    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long entityId;

    @Column(name = "NUMBER")
    private String number;

    @Column(name = "NAME")
    private String name;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name = "ACCOUNT_ID")
    private Set<Beneficiary> beneficiaries = new HashSet<Beneficiary>();

    ...............................
    ...............................
    ...............................
}

Pertanyaan saya adalah: Bagaimana tepatnya cara @ResponseBodykerja anotasi?

Itu terletak sebelum List<Account>objek yang dikembalikan jadi saya pikir itu mengacu pada Daftar ini. Dokumentasi kursus menyatakan bahwa anotasi ini berfungsi untuk:

memastikan bahwa hasilnya akan ditulis ke respons HTTP oleh HTTP Message Converter (bukan Tampilan MVC).

Dan juga membaca dokumentasi resmi Musim Semi: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html

tampaknya ia mengambil List<Account>objek dan memasukkannya ke dalam Http Response. Apakah ini benar atau saya salah paham?

Ditulis ke dalam komentar dari accountSummary()metode sebelumnya ada:

Anda harus mendapatkan hasil JSON saat mengakses http: // localhost: 8080 / rest-ws / app / accounts

Jadi apa sebenarnya artinya ini? Apakah ini berarti bahwa List<Account>objek yang dikembalikan oleh accountSummary()metode secara otomatis diubah ke dalam JSONformat dan kemudian dimasukkan ke dalam Http Response? Atau apa?

Jika pernyataan ini benar, di manakah ditentukan bahwa objek akan secara otomatis diubah ke dalam JSONformat? Apakah format standar diadopsi saat @ResponseBodyanotasi digunakan atau ditentukan di tempat lain?

AndreaNobili
sumber

Jawaban:

160

Pertama-tama, anotasi tidak memberi anotasi List. Ini menjelaskan metode, seperti RequestMappinghalnya. Kode Anda setara dengan

@RequestMapping(value="/orders", method=RequestMethod.GET)
@ResponseBody
public List<Account> accountSummary() {
    return accountManager.getAllAccounts();
}

Sekarang yang dimaksud dengan anotasi adalah bahwa nilai yang dikembalikan dari metode tersebut akan membentuk badan respons HTTP. Tentu saja, respons HTTP tidak boleh berisi objek Java. Jadi daftar akun ini diubah ke format yang sesuai untuk aplikasi REST, biasanya JSON atau XML.

Pilihan format tergantung pada konverter pesan diinstal, pada nilai-nilai dari producesatribut dari @RequestMappingpenjelasan, dan pada jenis konten yang klien menerima (yang tersedia dalam header permintaan HTTP). Misalnya, jika permintaan mengatakan menerima XML, tetapi tidak JSON, dan ada konverter pesan terpasang yang dapat mengubah daftar menjadi XML, maka XML akan dikembalikan.

JB Nizet
sumber
3
hai, bagaimana kita mengatur "pengonversi pesan yang diinstal"? Saya ingin default selalu dikonversi ke Json. Apakah saya bisa melakukan itu?
Sam YC
@JB Nizet dapatkah Anda menjelaskan mengapa tanggapan http tidak dapat berisi objek java. Saya seorang pemula di java.
Naved Ali
3
@ Er.NavedAli membaca spesifikasi http, jenis konten tanggapan http menentukan konten legal yang dapat dimuat dalam tanggapan http. nilai legal untuk itu bisa berupa "application / octet-stream", "image / jpeg", "text / HTML", tetapi objek java bukan nilai legal untuk itu.
ZhaoGang
@ZhaoGang, Content-type 'application / json' telah diganti dengan 'application / xml' dalam test case saya, tetapi respon body sepertinya tidak berubah, masih menampilkan format json.
LeafiWan
1
di mana tepatnya, maksud saya di kelas pegas mana, keputusan untuk menentukan bagaimana mengembalikan respons dibuat? Bisakah Anda mengarahkan saya ke sumber di github, di mana keputusan ini dilakukan atau nama kelas? Juga apa yang akan terjadi jika klien menerima XML, tetapi tidak ada konverter XML yang diinstal?
anir
65

Hal dasar pertama yang harus dipahami adalah perbedaan arsitektur.

Salah satu ujungnya Anda memiliki arsitektur MVC, yang didasarkan pada aplikasi web normal Anda, menggunakan halaman web, dan browser membuat permintaan untuk halaman:

Browser <---> Controller <---> Model
               |      |
               +-View-+

Browser membuat permintaan, controller (@Controller) mendapatkan model (@Entity), dan membuat view (JSP) dari model dan view tersebut dikembalikan ke klien. Ini adalah arsitektur aplikasi web dasar.

Di sisi lain, Anda memiliki arsitektur RESTful. Dalam hal ini, tidak ada View. Controller hanya mengirim kembali model (atau representasi sumber daya, dalam istilah yang lebih RESTful). Klien dapat berupa aplikasi JavaScript, aplikasi server Java, aplikasi apa pun yang kami gunakan untuk membuka REST API kami. Dengan arsitektur ini, klien memutuskan apa yang harus dilakukan dengan model ini. Ambil contoh Twitter. Twitter sebagai Web (REST) ​​API, yang memungkinkan aplikasi kita menggunakan API-nya untuk mendapatkan hal-hal seperti pembaruan status, sehingga kita dapat menggunakannya untuk meletakkan data itu di aplikasi kita. Data itu akan datang dalam beberapa format seperti JSON.

Karena itu, saat bekerja dengan Spring MVC, ini pertama kali dibuat untuk menangani arsitektur aplikasi web dasar. Mungkin ada ragam tanda tangan metode berbeda yang memungkinkan tampilan dihasilkan dari metode kami. Metode ini bisa mengembalikan ModelAndViewtempat kita membuatnya secara eksplisit, atau ada cara implisit di mana kita bisa mengembalikan beberapa objek arbitrer yang disetel ke atribut model. Namun bagaimanapun, di suatu tempat di sepanjang siklus permintaan-respons, akan ada tampilan yang dihasilkan.

Namun saat kami menggunakan @ResponseBody, kami mengatakan bahwa kami tidak ingin tampilan dibuat. Kami hanya ingin mengirim objek yang dikembalikan sebagai badan, dalam format apa pun yang kami tentukan. Kami tidak ingin itu menjadi objek Java serial (meskipun mungkin). Jadi ya, itu perlu dikonversi ke beberapa jenis umum lainnya (jenis ini biasanya ditangani melalui negosiasi konten - lihat tautan di bawah). Sejujurnya, saya tidak banyak bekerja dengan Spring, meskipun saya mencoba-coba di sana-sini. Biasanya, saya menggunakan

@RequestMapping(..., produces = MediaType.APPLICATION_JSON_VALUE)

untuk menyetel jenis konten, tapi mungkin JSON adalah defaultnya. Jangan mengutip saya, tetapi jika Anda mendapatkan JSON, dan Anda belum menentukannya produces, mungkin itu adalah defaultnya. JSON bukan satu-satunya format. Misalnya, di atas dapat dengan mudah dikirim dalam XML, tetapi Anda harus memiliki produceske MediaType.APPLICATION_XML_VALUEdan saya yakin Anda perlu mengkonfigurasi HttpMessageConverteruntuk JAXB. Adapun JSON MappingJacksonHttpMessageConverterdikonfigurasi, ketika kita memiliki Jackson di jalur kelas.

Saya akan meluangkan waktu untuk mempelajari tentang Negosiasi Konten . Ini adalah bagian yang sangat penting dari REST. Ini akan membantu Anda mempelajari tentang format respons yang berbeda dan cara memetakannya ke metode Anda.

Paul Samsotha
sumber
Jika menemukan jawaban Anda saat menyelidiki tentang cara membuat pengontrol REST generik / dinamis, tanpa menggunakan @Controller/@RestController. Saya menemukan, bahwa saya harus menghilangkan lapisan view resolver. Ini tidak sesederhana itu karena kelas AbstractController menyediakan metode yang harus mengembalikan nama tampilan. Saya mengajukan pertanyaan tentang itu: stackoverflow.com/questions/41016018/… , jika Anda memiliki beberapa ide tentang bagaimana saya bisa menyelesaikan masalah saya, silakan kirim komentar.
nowszy94
1

Selanjutnya, jenis pengembalian ditentukan oleh

  1. Apa yang diminta HTTP Request - di header Accept-nya. Coba lihat permintaan awal untuk melihat ke mana Terima diatur ke.

  2. Apa yang disiapkan HttpMessageConverters Spring. Spring MVC akan menyiapkan konverter untuk XML (menggunakan JAXB) dan JSON jika pustaka Jackson berada di jalur kelasnya.

Jika ada pilihan, ia memilih satu - dalam contoh ini, itu kebetulan JSON.

Ini adalah tercakup dalam catatan saja. Cari catatan tentang Pengonversi Pesan dan Negosiasi Konten.

paulchapman
sumber