2015-12-30 11 views
5

में एक ही मूल्य के लिए एकाधिक @QueryParam कुंजी जर्सी में एक ही ऑब्जेक्ट/चर के लिए एकाधिक@QueryParam कुंजी को अनुमति देना संभव है?जर्सी

enter image description here

वास्तविक:

@POST 
public Something getThings(@QueryParam("customer-number") Integer n) { 
    ... 
} 

इसलिए, अगर मैं यूआरएल के बाद ?customer-number=3 जोड़ने यह काम करता है।

उम्मीद:

मैं अगर मैं निम्न मानों में से किसी को भी जोड़ ऊपर व्यवहार प्राप्त करना चाहते हैं:

  • ?customer-number=3
  • ?customerNumber=3
  • ?customerNo=3

Obs:

... 
public @interface QueryParam { 
    String value(); 
} 

हां, तो यह कई स्ट्रिंग मान (@Produces) की तरह स्वीकार नहीं कर सकते:

  1. QueryParam एनोटेशन तरह दिखता है।

  2. दृष्टिकोण नीचे उपयोगकर्ता एकाधिक कुंजियों एक ही समय में एक ही अर्थ होने का उपयोग करने के लिए (और मैं "या" उन दोनों के बीच हालत एक करना चाहते हैं) की अनुमति देता है:

    @POST 
    public Something getThings(@QueryParam("customer-number") Integer n1, 
              @QueryParam("customerNumber") Integer n2, 
              @QueryParam("customerNo") Integer n3) { 
        ... 
    } 
    
  3. कुछ इस तरह नहीं करता है 'टी काम:

    @POST 
    public Something getThings(@QueryParam("customer-number|customerNumber|customerNo") Integer n) { 
        ... 
    } 
    

मैं यह कैसे कर सकते हैं?

विवरण:

  • जर्सी 2.22।1
  • जावा 8

उत्तर

1

शायद सबसे सरल और आसान तरीका है एक कस्टम @BeanParam उपयोग करने के लिए होगा:

पहले के रूप में सभी क्वेरी पैरामीटर विलय कस्टम सेम को परिभाषित:

class MergedIntegerValue { 

    private final Integer value; 

    public MergedIntegerValue(
     @QueryParam("n1") Integer n1, 
     @QueryParam("n2") Integer n2, 
     @QueryParam("n3") Integer n3) { 
    this.value = n1 != null ? n1 
     : n2 != null ? n2 
     : n3 != null ? n3 
     : null; 
    // Throw an exception if value == null ? 
    } 

    public Integer getValue() { 
    return value; 
    } 
} 

और फिर इसे अपने संसाधन विधि में @BeanParam के साथ उपयोग करें:

public Something getThings(
    @BeanParam MergedIntegerValue n) { 

    // Use n.getValue() ... 
} 

संदर्भ: https://jersey.java.net/documentation/latest/user-guide.html#d0e2403

1

ईमानदार होने के लिए: यह नहीं है कि वेबसाइसेस को कैसे डिजाइन किया जाना चाहिए। आप एक सख्त अनुबंध डालते हैं जो क्लाइंट और सर्वर दोनों का पालन करते हैं; आप एक पैरामीटर को परिभाषित करते हैं और यही वह है।

लेकिन निश्चित रूप से यह एक आदर्श दुनिया होगी जहां आपको यह होने की स्वतंत्रता है कि क्या होने जा रहा है। इसलिए यदि आपको तीन पैरामीटर की अनुमति देनी है, तो आपको अनुबंध करना होगा। यह एक तरीका है निम्नलिखित दृष्टिकोण # 2 जो मैं goofs के लिए यह परीक्षण करने के लिए सक्षम किया जा रहा बिना प्रदान करने के लिए है:

public Something getThings(@QueryParam("customer-number") Integer n1, 
          @QueryParam("customerNumber") Integer n2, 
          @QueryParam("customerNo") Integer n3) throws YourFailureException { 

    Integer customerNumber = getNonNullValue("Customer number", n1, n2, n3); 
    // things with stuff 
} 

private static Integer getNonNullValue(String label, Integer... params) throws YourFailureException { 

    Integer value = null; 

    for(Integer choice : params){ 
    if(choice != null){ 
     if(value != null){ 
     // this means there are at least two query parameters passed with a value 
     throw new YourFailureException("Ambiguous " + label + " parameters"); 
     } 

     value = choice; 
    } 
    } 

    if(value == null){ 
    throw new YourFailureException("Missing " + label + " parameter"); 
    } 

    return value; 
} 

तो मूल रूप से किसी भी कॉल कि विशेष रूप से एक पैरामीटर पारित नहीं होता है, और एक अपवाद नक्शाकार का अनुवाद करते हैं अस्वीकार अपवाद जो आप 4xx रेंज में एक HTTP प्रतिक्रिया कोड में फेंक देते हैं।

(मैंने getNonNullValue() विधि स्थैतिक बना दिया है यह मुझे पुन: प्रयोज्य उपयोगिता फ़ंक्शन के रूप में मारता है)।

1

आप एक कस्टम एनोटेशन बना सकते हैं। मैं इसे कैसे करना है इसके बारे में बहुत कुछ नहीं जाऊंगा, आप this post, या this post देख सकते हैं। असल में यह जर्सी के साथ सामान्य निर्भरता इंजेक्शन की तुलना में एक अलग बुनियादी ढांचे पर निर्भर करता है। आप जर्सी परियोजना से this package देख सकते हैं। यह वह जगह है जहां सभी इंजेक्शन प्रदाता रहते हैं जो @XxxParam इंजेक्शन को संभालते हैं। यदि आप स्रोत कोड की जांच करते हैं, तो आप देखेंगे कि कार्यान्वयन काफी समान हैं। ऊपर दिए गए दो लिंक एक ही पैटर्न का पालन करते हैं, साथ ही नीचे दिए गए कोड का पालन करते हैं।

क्या मैं एक कस्टम एनोटेशन

@Target({ElementType.FIELD, ElementType.PARAMETER}) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface VaryingParam { 

    String value(); 

    @SuppressWarnings("AnnotationAsSuperInterface") 
    public static class Factory 
      extends AnnotationLiteral<VaryingParam> implements VaryingParam { 

     private final String value; 

     public static VaryingParam create(final String newValue) { 
      return new Factory(newValue); 
     } 

     public Factory(String newValue) { 
      this.value = newValue; 
     } 

     @Override 
     public String value() { 
      return this.value; 
     } 
    } 
} 

यह अजीब है कि मैं एक कारखाने यह बनाने के लिए लग सकता है बनाया गया था था, लेकिन यह नीचे दिए गए कोड के कार्यान्वयन, जहां मैं का मूल्य विभाजित के लिए आवश्यक था स्ट्रिंग, और प्रत्येक विभाजन मूल्य के लिए एक नया एनोटेशन उदाहरण बनाते हैं।

यहां ValueFactoryProvider है (जो, यदि आपने उपर्युक्त लेखों में से कोई भी पढ़ा है, तो आप देखेंगे कि कस्टम विधि पैरामीटर इंजेक्शन के लिए आवश्यक है)। यह एक बड़ी कक्षा है, केवल इसलिए कि मैंने जर्सी परियोजना में देखे गए पैटर्न के बाद, सभी आवश्यक कक्षाओं को एक ही कक्षा में रखा है।

public class VaryingParamValueFactoryProvider extends AbstractValueFactoryProvider { 

    @Inject 
    public VaryingParamValueFactoryProvider(
      final MultivaluedParameterExtractorProvider mpep, 
      final ServiceLocator locator) { 
     super(mpep, locator, Parameter.Source.UNKNOWN); 
    } 

    @Override 
    protected Factory<?> createValueFactory(final Parameter parameter) { 
     VaryingParam annotation = parameter.getAnnotation(VaryingParam.class); 
     if (annotation == null) { 
      return null; 
     } 
     String value = annotation.value(); 
     if (value == null || value.length() == 0) { 
      return null; 
     } 
     String[] variations = value.split("\\s*\\|\\s*"); 
     return new VaryingParamFactory(variations, parameter); 
    } 

    private static Parameter cloneParameter(final Parameter original, final String value) { 
     Annotation[] annotations = changeVaryingParam(original.getAnnotations(), value); 
     Parameter clone = Parameter.create(
       original.getRawType(), 
       original.getRawType(), 
       true, 
       original.getRawType(), 
       original.getRawType(), 
       annotations); 
     return clone; 
    } 

    private static Annotation[] changeVaryingParam(final Annotation[] annos, final String value) { 
     for (int i = 0; i < annos.length; i++) { 
      if (annos[i] instanceof VaryingParam) { 
       annos[i] = VaryingParam.Factory.create(value); 
       break; 
      } 
     } 
     return annos; 
    } 

    private class VaryingParamFactory extends AbstractContainerRequestValueFactory<Object> { 

     private final String[] variations; 
     private final Parameter parameter; 
     private final boolean decode; 
     private final Class<?> paramType; 
     private final boolean isList; 
     private final boolean isSet; 

     VaryingParamFactory(final String[] variations, final Parameter parameter) { 
      this.variations = variations; 
      this.parameter = parameter; 
      this.decode = !parameter.isEncoded(); 
      this.paramType = parameter.getRawType(); 
      this.isList = paramType == List.class; 
      this.isSet = paramType == Set.class; 
     } 

     @Override 
     public Object provide() { 
      MultivaluedParameterExtractor<?> e = null; 
      try { 
       Object value = null; 
       MultivaluedMap<String, String> params 
         = getContainerRequest().getUriInfo().getQueryParameters(decode); 
       for (String variant : variations) { 
        e = get(cloneParameter(parameter, variant)); 
        if (e == null) { 
         return null; 
        } 
        if (isList) { 
         List list = (List<?>) e.extract(params); 
         if (value == null) { 
          value = new ArrayList(); 
         } 
         ((List<?>) value).addAll(list); 
        } else if (isSet) { 
         Set set = (Set<?>) e.extract(params); 
         if (value == null) { 
          value = new HashSet(); 
         } 
         ((Set<?>) value).addAll(set); 
        } else { 
         value = e.extract(params); 
         if (value != null) { 
          return value; 
         }   
        } 
       } 
       return value; 
      } catch (ExtractorException ex) { 
       if (e == null) { 
        throw new ParamException.QueryParamException(ex.getCause(), 
          parameter.getSourceName(), parameter.getDefaultValue()); 
       } else { 
        throw new ParamException.QueryParamException(ex.getCause(), 
          e.getName(), e.getDefaultValueString()); 
       } 
      } 
     } 
    } 

    private static class Resolver extends ParamInjectionResolver<VaryingParam> { 

     public Resolver() { 
      super(VaryingParamValueFactoryProvider.class); 
     } 
    } 

    public static class Binder extends AbstractBinder { 

     @Override 
     protected void configure() { 
      bind(VaryingParamValueFactoryProvider.class) 
        .to(ValueFactoryProvider.class) 
        .in(Singleton.class); 
      bind(VaryingParamValueFactoryProvider.Resolver.class) 
        .to(new TypeLiteral<InjectionResolver<VaryingParam>>() { 
        }) 
        .in(Singleton.class); 
     } 
    } 
} 

आप जर्सी के साथ इस वर्ग 'Binder (वर्ग के नीचे) रजिस्टर करने के लिए इसका इस्तेमाल करने की आवश्यकता होगी।

जर्सी QueryParamValueFactoryProvider से इस कक्षा को अलग करता है यह है कि एनोटेशन के एक स्ट्रिंग मान को प्रोसेस करने की बजाय, यह मान को विभाजित करता है, और क्वेरी पैराम मानचित्र से मूल्य निकालने का प्रयास करता है। पाया गया पहला मूल्य वापस कर दिया जाएगा।यदि पैरामीटर List या Set है, तो यह अभी भी सभी विकल्पों को देख रहा है, और उन्हें सूची में जोड़ना जारी रखता है।

अधिकांश भाग के लिए यह @XxxParam एनोटेशन से अपेक्षित सभी कार्यक्षमता रखता है। एकमात्र चीज जिसे कार्यान्वित करना मुश्किल था (इसलिए मैंने इस उपयोग के मामले का समर्थन छोड़ दिया), कई पैरामीटर हैं, उदा।

@GET 
@Path("multiple") 
public String getMultipleVariants(@VaryingParam("param-1|param-2|param-3") String value1, 
            @VaryingParam("param-1|param-2|param-3") String value2) { 
    return value1 + ":" + value2; 
} 

मैं वास्तव में यह है कि लागू करने के लिए मुश्किल होना चाहिए नहीं लगता कि, अगर तुम सच में इसकी जरूरत है, यह एक नया MultivaluedMap बनाने, एक मूल्य को हटाने अगर यह पाया जाता है की बस एक बात है। इसे ऊपर VaryingParamFactory की provide() विधि में कार्यान्वित किया जाएगा। यदि आपको इस उपयोग के मामले की आवश्यकता है, तो आप इसके बजाय List या Set का उपयोग कर सकते हैं।

जर्सी टेस्ट फ्रेमवर्क का उपयोग करके, पूर्ण परीक्षण मामले के लिए this GitHub Gist (यह काफी लंबा है) देखें। आप QueryTestResource में परीक्षण किए गए सभी उपयोग मामलों को देख सकते हैं, और जहां मैं के साथ ResourceConfig परीक्षण configure() विधि में Binder पंजीकृत करता हूं।