Dengan Spring, bisakah saya membuat variabel path opsional?

187

Dengan Spring 3.0, dapatkah saya memiliki variabel jalur opsional?

Sebagai contoh

@RequestMapping(value = "/json/{type}", method = RequestMethod.GET)
public @ResponseBody TestBean testAjax(
        HttpServletRequest req,
        @PathVariable String type,
        @RequestParam("track") String track) {
    return new TestBean();
}

Di sini saya ingin /json/abcatau /jsonmemanggil metode yang sama.
Satu solusi nyata menyatakan typesebagai parameter permintaan:

@RequestMapping(value = "/json", method = RequestMethod.GET)
public @ResponseBody TestBean testAjax(
        HttpServletRequest req,
        @RequestParam(value = "type", required = false) String type,
        @RequestParam("track") String track) {
    return new TestBean();
}

dan kemudian /json?type=abc&track=aaatau /json?track=rrakan bekerja

Shamik
sumber

Jawaban:

194

Anda tidak dapat memiliki variabel jalur opsional, tetapi Anda dapat memiliki dua metode pengontrol yang memanggil kode layanan yang sama:

@RequestMapping(value = "/json/{type}", method = RequestMethod.GET)
public @ResponseBody TestBean typedTestBean(
        HttpServletRequest req,
        @PathVariable String type,
        @RequestParam("track") String track) {
    return getTestBean(type);
}

@RequestMapping(value = "/json", method = RequestMethod.GET)
public @ResponseBody TestBean testBean(
        HttpServletRequest req,
        @RequestParam("track") String track) {
    return getTestBean();
}
earldouglas
sumber
5
@Shamik: Ini adalah alasan kuat untuk tidak menggunakan variabel path, menurut saya. Proliferasi kombinatorial dapat dengan cepat keluar dari tangan.
skaffman
9
Sebenarnya bukan karena path tidak bisa sekompleks itu sedang diisi dengan komponen opsional. Jika Anda memiliki lebih dari satu atau maksimal dua elemen jalur opsional, Anda harus mempertimbangkan dengan serius untuk mengganti beberapa elemen tersebut untuk meminta parameter.
Patrick Cornelissen
1
Dan bagi sebagian orang, memiliki metode pengontrol kedua memanggil metode pengontrol pertama dapat bekerja juga, jika misalnya parameter yang berbeda dapat disediakan oleh beberapa cara lain
chrismarx
3
Silakan pertimbangkan untuk memperbarui jawaban Anda, alih-alih membuat dua metode pengontrol di versi Spring yang lebih baru, kami hanya dapat menggunakan @RequestMappingdua nilai seperti di: stackoverflow.com/questions/17821731/…
csharpfolk
omg bagaimana Anda berharap untuk mempertahankan titik akhir ini? Dan jika alih-alih hanya satu variabel path yang kita miliki 5, lakukan matematika untuk saya, berapa banyak titik akhir yang akan Anda lakukan? Tolong bantu saya dan ganti @PathVariableke@RequestParam
Guilherme Alencar
114

Jika Anda menggunakan Spring 4.1 dan Java 8 Anda dapat menggunakan java.util.Optionalyang didukung dalam @RequestParam, @PathVariable, @RequestHeaderdan @MatrixVariabledi Spring MVC -

@RequestMapping(value = {"/json/{type}", "/json" }, method = RequestMethod.GET)
public @ResponseBody TestBean typedTestBean(
    @PathVariable Optional<String> type,
    @RequestParam("track") String track) {      
    if (type.isPresent()) {
        //type.get() will return type value
        //corresponds to path "/json/{type}"
    } else {
        //corresponds to path "/json"
    }       
}
Aniket Thakur
sumber
Anda yakin ini akan berhasil? Jawaban Anda sendiri di sini menunjukkan bahwa controller tidak akan terkena jika {type} tidak ada dari path. Saya pikir PathVariable Map akan menjadi pendekatan yang lebih baik, atau menggunakan pengontrol terpisah.
Anshul Tiwari
4
Ya jika Anda baru saja "/json/{type}"dan ketik tidak ada, itu tidak akan terkena (seperti yang disarankan jawaban saya) tetapi di sini Anda miliki value = {"/json/{type}", "/json" }. Jadi jika ada yang cocok dengan metode controller akan dipukul.
Aniket Thakur
Apakah mungkin bahwa nilai yang sama, dll., Jenisnya adalah RequestParam dan RequestParam juga?
zygimantus
Ini bekerja, tetapi fungsi pengontrol tidak akan dipanggil karena mengharapkan parameter di sana
EliuX
15
Ini berfungsi, dan sejak Musim Semi 4.3.3, Anda juga bisa pergi dengan @PathVariable(required = false)dan mendapatkan nol jika variabel tidak ada.
Nicolai Ehemann
76

Tidak diketahui bahwa Anda juga dapat menyuntikkan Peta variabel jalur menggunakan anotasi @PathVariable. Saya tidak yakin apakah fitur ini tersedia di Spring 3.0 atau jika ditambahkan nanti, tapi di sini ada cara lain untuk menyelesaikan contoh:

@RequestMapping(value={ "/json/{type}", "/json" }, method=RequestMethod.GET)
public @ResponseBody TestBean typedTestBean(
    @PathVariable Map<String, String> pathVariables,
    @RequestParam("track") String track) {

    if (pathVariables.containsKey("type")) {
        return new TestBean(pathVariables.get("type"));
    } else {
        return new TestBean();
    }
}
Paul Wardrip
sumber
1
Saya menggunakannya sepanjang waktu. Ini berguna ketika saya ingin satu metode untuk menangani berbagai jenis uri ex: {"/ json / {type}", "/ json / {type} / {xyz}", "/ json / {type} / {abc} "," / json / {type} / {abc} / {something} "," / json "}
Vaibs
25

Anda bisa menggunakan:

@RequestParam(value="somvalue",required=false)

untuk params opsional daripada pathVariable

Maleck13
sumber
1
Tampaknya ini versi spesifik. Jangan pergi untuk Musim Semi 3.
Stu Thompson
5
Saat ini menggunakan metode ini untuk proyek pegas 3.1, dan dokumen mengatakan bahwa itu berfungsi untuk 2.5+, jadi pasti berfungsi untuk Pegas 3. EDIT: sumber .
Evan B.
22
Benar, tapi ini bukan pertanyaannya. Menggunakan parameter permintaan memang disebutkan dalam pertanyaan sebagai "Satu solusi yang jelas" , tetapi pertanyaan itu sendiri adalah tentang parameter jalur . Ini bukan solusi untuk parameter jalur opsional.
Arjan
9
PathVariable dan RequestParam berbeda.
Abadi
10

Contoh Spring 5 / Spring Boot 2:

pemblokiran

@GetMapping({"/dto-blocking/{type}", "/dto-blocking"})
public ResponseEntity<Dto> getDtoBlocking(
        @PathVariable(name = "type", required = false) String type) {
    if (StringUtils.isEmpty(type)) {
        type = "default";
    }
    return ResponseEntity.ok().body(dtoBlockingRepo.findByType(type));
}

reaktif

@GetMapping({"/dto-reactive/{type}", "/dto-reactive"})
public Mono<ResponseEntity<Dto>> getDtoReactive(
        @PathVariable(name = "type", required = false) String type) {
    if (StringUtils.isEmpty(type)) {
        type = "default";
    }
    return dtoReactiveRepo.findByType(type).map(dto -> ResponseEntity.ok().body(dto));
}
kinjelom
sumber
6

Contoh sederhana dari komentar Nicolai Ehmann dan jawaban wildloop (berfungsi dengan Spring 4.3.3+), pada dasarnya Anda dapat menggunakan required = falsesekarang:

  @RequestMapping(value = {"/json/{type}", "/json" }, method = RequestMethod.GET)
  public @ResponseBody TestBean testAjax(@PathVariable(required = false) String type) {
    if (type != null) {
      // ...
    }
    return new TestBean();
  }
rogerdpack
sumber
-5
$.ajax({
            type : 'GET',
            url : '${pageContext.request.contextPath}/order/lastOrder',
            data : {partyId : partyId, orderId :orderId},
            success : function(data, textStatus, jqXHR) });

@RequestMapping(value = "/lastOrder", method=RequestMethod.GET)
public @ResponseBody OrderBean lastOrderDetail(@RequestParam(value="partyId") Long partyId,@RequestParam(value="orderId",required=false) Long orderId,Model m ) {}
Ankush Mundada
sumber
3
Anda mungkin ingin mengedit dalam beberapa teks dalam jawaban Anda, menjelaskan mengapa menurut Anda ini berkontribusi untuk menyelesaikan masalah yang ada (4 tahun kemudian).
Qirel