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

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

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

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

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


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

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



-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 ) {}

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.