स्प्रिंग MVC: GET @RequestParam के रूप में जटिल वस्तु


192

मान लीजिए कि मेरे पास एक ऐसा पृष्ठ है जो किसी तालिका पर वस्तुओं को सूचीबद्ध करता है और मुझे तालिका को फ़िल्टर करने के लिए एक फ़ॉर्म डालने की आवश्यकता है। फ़िल्टर को अजाक्स GET के रूप में URL पर भेजा जाता है: http://foo.com/system/controller/action?page=1&prop1=x&prop2=y&prop3=z

और मेरे नियंत्रक पर बहुत सारे पैरामीटर होने के बजाय जैसे:

@RequestMapping(value = "/action")
public @ResponseBody List<MyObject> myAction(
    @RequestParam(value = "page", required = false) int page,
    @RequestParam(value = "prop1", required = false) String prop1,
    @RequestParam(value = "prop2", required = false) String prop2,
    @RequestParam(value = "prop3", required = false) String prop3) { ... }

और मान लीजिए कि मेरे पास MyObject है:

public class MyObject {
    private String prop1;
    private String prop2;
    private String prop3;

    //Getters and setters
    ...
}

मैं कुछ करना चाहता हूँ:

@RequestMapping(value = "/action")
public @ResponseBody List<MyObject> myAction(
    @RequestParam(value = "page", required = false) int page,
    @RequestParam(value = "myObject", required = false) MyObject myObject,) { ... }

क्या यह संभव है? मैं उसे कैसे कर सकता हूँ?


1
'@RodestMapping' के साथ संयुक्त '@ModelAttribute' का उपयोग करने का प्रयास करें, जहाँ MyObject आपका मॉडल होगा।
मीकल

@ सिंहल १। यहाँ ट्यूटोरियल के एक जोड़े को दिखाया गया है कि कैसे करें: स्प्रिंग 3 एमवीसी: स्प्रिंग 3.0 एमवीसी में हैंडलिंग फॉर्म , क्या है और कैसे उपयोग करना है@ModelAttribute , स्प्रिंग एमवीसी फॉर्म हैंडलिंग उदाहरण । बस Google " स्प्रिंग एमवीसी फॉर्म हैंडलिंग " और आपको ट्यूटोरियल / उदाहरणों का एक टन मिलेगा। लेकिन रूप से निपटने के आधुनिक तरीके से उपयोग करने के लिए सुनिश्चित हो, यानी वसंत v2.5 +
informatik01

इसके अलावा उपयोगी: क्या है @ModelAttributeवसंत MVC में
informatik01

जवाबों:


248

आप पूरी तरह से ऐसा कर सकते हैं, बस @RequestParamएनोटेशन को हटा दें , स्प्रिंग सफाई से आपके कक्षा के उदाहरण के लिए आपके अनुरोध पैरामीटर को बांध देगा:

public @ResponseBody List<MyObject> myAction(
    @RequestParam(value = "page", required = false) int page,
    MyObject myObject)

8
बीजू, क्या आप इस बात का उदाहरण दे सकते हैं कि यह कैसे कहा जाए? मैं रेस्ट एपीआई में एक बुनियादी http जीईटी कॉल कर रहा हूं और इसका कोई फैंसी रूप नहीं है।
bschandramohan

33
ध्यान दें कि स्प्रिंग डिफ़ॉल्ट रूप से प्राप्तकर्ताओं को स्वचालित रूप से बांधने के लिए MyObject के लिए गेटर्स / सेटर करता है। अन्य यह myObject बाँध नहीं होगा।
उर्फ_श

20
आप कैसे सेट कर सकते हैं फ़ील्ड MyObject में वैकल्पिक / गैर-वैकल्पिक हैं? यकीन नहीं है कि इसके लिए उचित दस्तावेज कैसे
ढूंढे

6
@ बायजू, क्या डिफ़ॉल्ट मूल्यों को नियंत्रित करने और MyObjectउसके लिए आवश्यक तरीका है, इसी तरह हम @RequestParam के साथ कर सकते हैं?
एलेक्सी

7
@BijuKunjummen मैं MyObjectसांप के मामले में क्वेरी मापदंडों को स्वीकार करने और इसे ऊंट मामले के सदस्य को मैप करने के लिए कैसे अपडेट कर सकता हूं MyObject। उदाहरण के लिए, ?book_id=4के bookIdसदस्य के साथ मैप किया जाना चाहिए MyObject?
विवेक वर्धन

55

मैं मुझसे कुछ छोटा उदाहरण जोड़ूंगा।

डीटीओ वर्ग:

public class SearchDTO {
    private Long id[];

    public Long[] getId() {
        return id;
    }

    public void setId(Long[] id) {
        this.id = id;
    }
    // reflection toString from apache commons
    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}

नियंत्रक वर्ग के अंदर मानचित्रण का अनुरोध करें:

@RequestMapping(value="/handle", method=RequestMethod.GET)
@ResponseBody
public String handleRequest(SearchDTO search) {
    LOG.info("criteria: {}", search);
    return "OK";
}

प्रश्न:

http://localhost:8080/app/handle?id=353,234

परिणाम:

[http-apr-8080-exec-7] INFO  c.g.g.r.f.w.ExampleController.handleRequest:59 - criteria: SearchDTO[id={353,234}]

मुझे उम्मीद है यह मदद करेगा :)

अद्यतन / कोटलिन

क्योंकि वर्तमान में मैं कोटलिन के साथ बहुत काम कर रहा हूं, अगर कोई इसी तरह के डीटीओ को परिभाषित करना चाहता है तो कोटलिन में निम्न रूप होना चाहिए:

class SearchDTO {
    var id: Array<Long>? = arrayOf()

    override fun toString(): String {
        // to string implementation
    }
}

dataइस तरह एक वर्ग के साथ :

data class SearchDTO(var id: Array<Long> = arrayOf())

वसंत (बूट में परीक्षण किया गया) उत्तर में उल्लिखित अनुरोध के लिए निम्न त्रुटि देता है:

"Java.lang.String [] 'प्रकार के मान को आवश्यक प्रकार में बदलने में विफल" java.lang.Long []'; नेस्टेड अपवाद java.lang.NumberFormatException: इनपुट स्ट्रिंग के लिए \ "353,234 \" है "

डेटा क्लास केवल निम्नलिखित अनुरोध फॉर्म के लिए काम करेगा:

http://localhost:8080/handle?id=353&id=234

इस बारे में पता है!


1
क्या खेतों को धराशायी करने के लिए "आवश्यक" सेट करना संभव है?
सामान्य

4
मैं स्प्रिंग एमवीसी सत्यापनकर्ताओं के साथ प्रयास करने का सुझाव देता हूं। उदाहरण: codejava.net/frameworks/spring/...
Przemek नोवाक

यह बहुत उत्सुक है कि इसके लिए एनोटेशन की आवश्यकता नहीं है! मुझे आश्चर्य है, क्या इस आरोप के लिए स्पष्ट रूप से एक एनोटेशन है?
जेम्स वाटकिंस

8

मुझे एक समान समस्या है। वास्तव में समस्या गहरी है जैसा मैंने सोचा था। मैं jquery का उपयोग कर रहा हूं $.postजो Content-Type:application/x-www-form-urlencoded; charset=UTF-8डिफ़ॉल्ट रूप में उपयोग करता है। दुर्भाग्य से मैंने उस पर अपना सिस्टम आधारित कर दिया और जब मुझे एक जटिल वस्तु की आवश्यकता हुई तो मैं @RequestParamइसे बस नहीं कर सका।

मेरे मामले में मैं कुछ के साथ उपयोगकर्ता वरीयताएँ भेजने की कोशिश कर रहा हूँ;

 $.post("/updatePreferences",  
    {id: 'pr', preferences: p}, 
    function (response) {
 ...

क्लाइंट की ओर से सर्वर को भेजे गए वास्तविक कच्चे डेटा है;

...
id=pr&preferences%5BuserId%5D=1005012365&preferences%5Baudio%5D=false&preferences%5Btooltip%5D=true&preferences%5Blanguage%5D=en
...

के रूप में;

id:pr
preferences[userId]:1005012365
preferences[audio]:false
preferences[tooltip]:true
preferences[language]:en

और सर्वर साइड है;

@RequestMapping(value = "/updatePreferences")
public
@ResponseBody
Object updatePreferences(@RequestParam("id") String id, @RequestParam("preferences") UserPreferences preferences) {

    ...
        return someService.call(preferences);
    ...
}

मैंने @ModelAttributeसभी संभावनाओं के साथ सेटर / गेटर्स, कंस्ट्रक्टरों को जोड़ने की कोशिश की , UserPreferencesलेकिन कोई भी मौका नहीं मिला क्योंकि यह भेजे गए डेटा को 5 मापदंडों के रूप में मान्यता देता है लेकिन वास्तव में मैप किए गए तरीके में केवल 2 पैरामीटर हैं। मैंने बीजू के समाधान की भी कोशिश की, लेकिन क्या होता है कि, स्प्रिंग डिफॉल्ट कंस्ट्रक्टर के साथ UserPreferences ऑब्जेक्ट बनाता है और डेटा में नहीं भरता है।

मैंने ग्राहक पक्ष से वरीयताओं के जेसन स्ट्रिंग भेजकर समस्या को हल किया और इसे संभाल लिया जैसे कि यह सर्वर पक्ष पर एक स्ट्रिंग है;

ग्राहक:

 $.post("/updatePreferences",  
    {id: 'pr', preferences: JSON.stringify(p)}, 
    function (response) {
 ...

सर्वर:

@RequestMapping(value = "/updatePreferences")
public
@ResponseBody
Object updatePreferences(@RequestParam("id") String id, @RequestParam("preferences") String preferencesJSon) {


        String ret = null;
        ObjectMapper mapper = new ObjectMapper();
        try {
            UserPreferences userPreferences = mapper.readValue(preferencesJSon, UserPreferences.class);
            return someService.call(userPreferences);
        } catch (IOException e) {
            e.printStackTrace();
        }
}

संक्षिप्त रूप में, मैंने REST पद्धति के अंदर मैन्युअल रूप से रूपांतरण किया। मेरी राय में वसंत को भेजे गए डेटा को पहचानने का कारण सामग्री-प्रकार नहीं है।


1
मैंने बस एक ही मुद्दे का अनुभव किया, और इसे उसी तरीके से हल किया। वैसे, क्या आपको आज के रूप में एक बेहतर समाधान मिला?
शाय एलक्यम

1
मैं वास्तव में एक ही मुद्दा रहा हूँ। मैंने किया था क्लीनर का उपयोग करके वैकल्पिक हल@RequestMapping(method = POST, path = "/settings/{projectId}") public void settings(@PathVariable String projectId, @RequestBody ProjectSettings settings)
पेट्र Újezdský

4

चूंकि प्रत्येक पोस्ट के अंतर्गत फ़ील्ड्स को अनिवार्य रूप से पॉप अप करने के तरीके पर सवाल है, इसलिए मैंने फ़ील्ड्स को आवश्यकतानुसार सेट करने के लिए एक छोटा उदाहरण लिखा है:

public class ExampleDTO {
    @NotNull
    private String mandatoryParam;

    private String optionalParam;

    @DateTimeFormat(iso = ISO.DATE) //accept Dates only in YYYY-MM-DD
    @NotNull
    private LocalDate testDate;

    public String getMandatoryParam() {
        return mandatoryParam;
    }
    public void setMandatoryParam(String mandatoryParam) {
        this.mandatoryParam = mandatoryParam;
    }
    public String getOptionalParam() {
        return optionalParam;
    }
    public void setOptionalParam(String optionalParam) {
        this.optionalParam = optionalParam;
    }
    public LocalDate getTestDate() {
        return testDate;
    }
    public void setTestDate(LocalDate testDate) {
        this.testDate = testDate;
    }
}

@RequestMapping(value = "/test", method = RequestMethod.GET)
public String testComplexObject (@Valid ExampleDTO e){
    System.out.println(e.getMandatoryParam() + " " + e.getTestDate());
    return "Does this work?";
}

0

जबकि जवाब है कि को देखें @ModelAttribute, @RequestParam, @PathParamऔर पसंद मान्य हैं, वहाँ एक छोटा सा पकड़ लिया मैं में भाग है। परिणामस्वरूप विधि पैरामीटर एक प्रॉक्सी है जो स्प्रिंग आपके डीटीओ के चारों ओर लपेटता है। इसलिए, यदि आप इसे ऐसे संदर्भ में उपयोग करने का प्रयास करते हैं जिसके लिए आपको अपने स्वयं के कस्टम प्रकार की आवश्यकता होती है, तो आपको कुछ अप्रत्याशित परिणाम मिल सकते हैं।

निम्नलिखित काम नहीं करेगा:

@GetMapping(produces = APPLICATION_JSON_VALUE)
public ResponseEntity<CustomDto> request(@ModelAttribute CustomDto dto) {
    return ResponseEntity.ok(dto);
}

मेरे मामले में, जैक्सन बंधन में इसका उपयोग करने का प्रयास करने के परिणामस्वरूप ए com.fasterxml.jackson.databind.exc.InvalidDefinitionException

आपको dto से एक नई ऑब्जेक्ट बनाने की आवश्यकता होगी।


0

हां, आप इसे सरल तरीके से कर सकते हैं। लाइनों के नीचे कोड देखें।

URL - http: // localhost: 8080 / get / request / multiple / param / by / map? नाम = 'एबीसी' और आईडी = '123'

 @GetMapping(path = "/get/request/header/by/map")
    public ResponseEntity<String> getRequestParamInMap(@RequestParam Map<String,String> map){
        // Do your business here 
        return new ResponseEntity<String>(map.toString(),HttpStatus.OK);
    } 

0

स्वीकृत उत्तर एक आकर्षण की तरह काम करता है, लेकिन अगर ऑब्जेक्ट में वस्तुओं की एक सूची है तो यह अपेक्षित रूप से काम नहीं करेगा, इसलिए यहां कुछ खुदाई के बाद मेरा समाधान है।

इस थ्रेड सलाह के बाद , यहाँ बताया गया है कि मैंने कैसे किया है।

  • सीमांत: अपनी वस्तु को प्रस्तुत करने के लिए आधार ६४ में कूटबद्ध करने की तुलना में उसे बढ़ाएँ।
  • बैकएंड: बेस 64 स्ट्रिंग को डीकोड करें फिर स्ट्रिंग जेन्स को वांछित ऑब्जेक्ट में परिवर्तित करें।

पोस्टमैन के साथ अपने एपीआई को डिबग करने के लिए यह सबसे अच्छा नहीं है, लेकिन यह मेरे लिए उम्मीद के मुताबिक काम कर रहा है।

मूल ऑब्जेक्ट: {पेज: 1, आकार: 5, फिल्टर: [{फ़ील्ड: "आईडी", मूल्य: 1, तुलना: "क्यूक्यू"}

एन्कोड की गई वस्तु: eyJwYWdlIjoxLCJzaXplIjo1LCJmaWx0ZXJzIjpbeyJmaWVsZCI6ImlkUGFyZW50WiwIYYttGFyaXNvbiI6Ik5VTEwifV19

@GetMapping
fun list(@RequestParam search: String?): ResponseEntity<ListDTO> {
    val filter: SearchFilterDTO = decodeSearchFieldDTO(search)
    ...
}

private fun decodeSearchFieldDTO(search: String?): SearchFilterDTO {
    if (search.isNullOrEmpty()) return SearchFilterDTO()
    return Gson().fromJson(String(Base64.getDecoder().decode(search)), SearchFilterDTO::class.java)
}

और यहां SearchFilterDTO और FilterDTO

class SearchFilterDTO(
    var currentPage: Int = 1,
    var pageSize: Int = 10,
    var sort: Sort? = null,
    var column: String? = null,
    var filters: List<FilterDTO> = ArrayList<FilterDTO>(),
    var paged: Boolean = true
)

class FilterDTO(
    var field: String,
    var value: Any,
    var comparison: Comparison
)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.