JAX-RS - JSON और HTTP स्टेटस कोड को एक साथ कैसे लौटाएं?


248

मैं एक REST वेब ऐप (NetBeans 6.9, JAX-RS, TopLink Essentials) लिख रहा हूं और JSON और HTTP स्थिति कोड को वापस करने की कोशिश कर रहा हूं । मेरे पास कोड तैयार है और काम कर रहा है जो क्लाइंट से HTTP GET विधि कहे जाने पर JSON देता है। अनिवार्य रूप से:

@Path("get/id")
@GET
@Produces("application/json")
public M_機械 getMachineToUpdate(@PathParam("id") String id) {

    // some code to return JSON ...

    return myJson;
}

लेकिन मैं JSON डेटा के साथ एक HTTP स्थिति कोड (500, 200, 204, आदि) भी वापस करना चाहता हूं ।

मैंने उपयोग करने की कोशिश की HttpServletResponse:

response.sendError("error message", 500);

लेकिन इससे ब्राउज़र को लगता है कि यह "वास्तविक" 500 है, इसलिए आउटपुट वेब पेज एक नियमित HTTP 500 त्रुटि पृष्ठ था।

मैं एक HTTP स्थिति कोड वापस करना चाहता हूं ताकि मेरा क्लाइंट-साइड जावास्क्रिप्ट इसके आधार पर कुछ तर्क को संभाल सके (जैसे HTML पृष्ठ पर त्रुटि कोड और संदेश प्रदर्शित करना)। क्या यह संभव है या ऐसी स्थिति के लिए HTTP स्थिति कोड का उपयोग नहीं किया जाना चाहिए?


2
500 के बीच का अंतर क्या है जो आप चाहते हैं (असत्य? :)) और असली 500?
रेज़र

@razor यहां असली 500 का मतलब JSON रिस्पॉन्स के बजाय एक HTML एरर पेज है
Nupur

वेब ब्राउज़र JSON के साथ काम करने के लिए डिज़ाइन नहीं किया गया था, लेकिन HTML पृष्ठों के साथ, इसलिए यदि आप 500 (और कुछ संदेश निकाय) के साथ प्रतिक्रिया करते हैं, तो ब्राउज़र आपको बस एक त्रुटि संदेश दिखा सकता है (वास्तव में ब्राउज़र कार्यान्वयन पर निर्भर करता है), सिर्फ इसलिए कि यह उपयोगी है एक सामान्य उपयोगकर्ता।
रेजर

जवाबों:


347

यहाँ एक उदाहरण है:

@GET
@Path("retrieve/{uuid}")
public Response retrieveSomething(@PathParam("uuid") String uuid) {
    if(uuid == null || uuid.trim().length() == 0) {
        return Response.serverError().entity("UUID cannot be blank").build();
    }
    Entity entity = service.getById(uuid);
    if(entity == null) {
        return Response.status(Response.Status.NOT_FOUND).entity("Entity not found for UUID: " + uuid).build();
    }
    String json = //convert entity to json
    return Response.ok(json, MediaType.APPLICATION_JSON).build();
}

रिस्पांस क्लास पर एक नजर ।

ध्यान दें कि आपको हमेशा एक सामग्री प्रकार निर्दिष्ट करना चाहिए, खासकर यदि आप कई सामग्री प्रकारों को पास कर रहे हैं, लेकिन यदि हर संदेश को JSON के रूप में दर्शाया जाएगा, तो आप विधि के साथ टिप्पणी कर सकते हैं @Produces("application/json")


12
यह काम करता है, लेकिन रिस्पॉन्स रिटर्न वैल्यू के बारे में मुझे जो पसंद नहीं है वह यह है कि मेरी राय में यह आपके कोड को प्रदूषित करता है, विशेष रूप से इसका उपयोग करने की कोशिश करने वाले किसी भी क्लाइंट के बारे में। यदि आप किसी तीसरे पक्ष को रिस्पॉन्स लौटाने वाला इंटरफ़ेस प्रदान करते हैं, तो वह नहीं जानता कि आप वास्तव में किस प्रकार के हैं। स्प्रिंग एक एनोटेशन के साथ इसे और अधिक स्पष्ट करता है, यदि आप हमेशा एक स्टेटस कोड (यानी HTTP 204)
Guido

19
उस वर्ग को सामान्य बनाना (रिस्पांस <T>) दोनों विकल्पों के फायदे के लिए, jax-rs के लिए एक दिलचस्प सुधार होगा।
गुइडो

41
इकाई को किसी भी तरह से json में बदलने की कोई आवश्यकता नहीं है। आप return Response.status(Response.Status.Forbidden).entity(myPOJO).build();जैसे चाहे वैसे काम कर सकते हैं return myPOJO;, लेकिन HTTP-Status कोड की अतिरिक्त सेटिंग के साथ।
क्रतेंको

1
मुझे लगता है कि बिजनेस लॉजिक को अलग सर्विस क्लास में अलग करने से अच्छा काम करता है। समापन बिंदु प्रतिसाद का उपयोग रिटर्न प्रकार के रूप में करता है और यह विधियां ज्यादातर सेवा विधियों और पथ और एनोटेशन के लिए कॉल होती हैं। यह तर्क को url / सामग्री प्रकार के मानचित्रण से अलग करता है (जहां रबर बोलने के लिए सड़क को हिट करता है)।
स्टिजिन डी विट

वास्तव में, कोई भी रिस्पांस के लिए लपेटे हुए ऑब्जेक्ट को वापस कर सकता है।
Ses

192

REST वेब सेवा में HTTP स्थिति कोड सेट करने के लिए कई उपयोग के मामले हैं, और कम से कम एक को मौजूदा उत्तरों में पर्याप्त रूप से प्रलेखित नहीं किया गया था (यानी जब आप JAXB का उपयोग करके ऑटो-जादुई JSON / XML क्रमांकन का उपयोग कर रहे हैं, और आप एक वापस लौटना चाहते हैं क्रमबद्ध होने वाली वस्तु, लेकिन डिफ़ॉल्ट 200 से भिन्न स्थिति कोड भी)।

तो मुझे अलग-अलग उपयोग के मामलों और हर एक के समाधान के लिए प्रयास करना चाहिए:

1. त्रुटि कोड (500, 404, ...)

जब आप 200 OKकिसी त्रुटि के कारण स्थिति कोड को अलग से लौटना चाहते हैं तो सबसे आम उपयोग मामला है।

उदाहरण के लिए:

  • एक इकाई का अनुरोध किया गया है, लेकिन यह मौजूद नहीं है (404)
  • अनुरोध शब्दार्थ गलत है (400)
  • उपयोगकर्ता अधिकृत नहीं है (401)
  • डेटाबेस कनेक्शन के साथ एक समस्या है (500)
  • आदि..

क) एक अपवाद फेंक दें

उस मामले में, मुझे लगता है कि समस्या को संभालने का सबसे साफ तरीका अपवाद को फेंकना है। इस अपवाद को एक द्वारा नियंत्रित किया ExceptionMapperजाएगा, जो उचित त्रुटि कोड के साथ प्रतिक्रिया में अपवाद का अनुवाद करेगा।

आप ExceptionMapperजर्सी के साथ पूर्व-कॉन्फ़िगर किए गए डिफ़ॉल्ट का उपयोग कर सकते हैं (और मुझे लगता है कि यह अन्य कार्यान्वयनों के साथ समान है) और के किसी भी मौजूदा उप-वर्गों को फेंक दें javax.ws.rs.WebApplicationException। ये पूर्व-परिभाषित अपवाद प्रकार हैं जो विभिन्न त्रुटि कोड के लिए पूर्व-मैप किए गए हैं, उदाहरण के लिए:

  • BadRequestException (400)
  • इंटरनलसर्वरर एक्ससेप्शन (500)
  • NotFoundException (404)

आदि आप यहाँ सूची पा सकते हैं: एपीआई

वैकल्पिक रूप से, आप अपने स्वयं के कस्टम अपवादों और ExceptionMapperवर्गों को परिभाषित कर सकते हैं , और @Providerएनोटेशन ( इस उदाहरण का स्रोत ) के माध्यम से जर्सी में इन मैपर्स को जोड़ सकते हैं :

public class MyApplicationException extends Exception implements Serializable
{
    private static final long serialVersionUID = 1L;
    public MyApplicationException() {
        super();
    }
    public MyApplicationException(String msg)   {
        super(msg);
    }
    public MyApplicationException(String msg, Exception e)  {
        super(msg, e);
    }
}

प्रदाता:

    @Provider
    public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException> 
    {
        @Override
        public Response toResponse(MyApplicationException exception) 
        {
            return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();  
        }
    }

नोट: आप मौजूदा अपवाद प्रकारों के लिए ExceptionMappers भी लिख सकते हैं जिनका आप उपयोग करते हैं।

बी) रिस्पांस बिल्डर का उपयोग करें

स्टेटस कोड सेट करने का दूसरा तरीका यह है कि आप Responseबिल्डर का उपयोग इच्छित कोड के साथ प्रतिक्रिया बनाने के लिए करें।

उस स्थिति में, आपके विधि का रिटर्न प्रकार अवश्य होना चाहिए javax.ws.rs.core.Response। इसे विभिन्न अन्य प्रतिक्रियाओं में वर्णित किया गया है, जैसे कि उनका जवाब 'स्वीकार किए जाते हैं और इस तरह दिखता है:

@GET
@Path("myresource({id}")
public Response retrieveSomething(@PathParam("id") String id) {
    ...
    Entity entity = service.getById(uuid);
    if(entity == null) {
        return Response.status(Response.Status.NOT_FOUND).entity("Resource not found for ID: " + uuid).build();
    }
    ...
}

2. सफलता, लेकिन 200 नहीं

एक अन्य मामला जब आप रिटर्न की स्थिति सेट करना चाहते हैं, जब ऑपरेशन सफल रहा था, लेकिन आप 200 से अधिक सफलता कोड वापस करना चाहते हैं, साथ ही उस सामग्री के साथ जो आप शरीर में वापस करते हैं।

एक बार-बार उपयोग का मामला तब होता है जब आप एक नई इकाई ( POSTअनुरोध) बनाते हैं और इस नई इकाई के बारे में जानकारी वापस करना चाहते हैं या हो सकता है कि इकाई ही, एक साथ एक 201 Createdस्थिति कोड के साथ।

एक दृष्टिकोण प्रतिक्रिया वस्तु का उपयोग करना है जैसा कि ऊपर वर्णित है और अनुरोध के शरीर को स्वयं सेट करें। हालाँकि, ऐसा करने से आप JAXB द्वारा प्रदान किए गए XML या JSON को स्वचालित क्रमांकन का उपयोग करने की क्षमता को ढीला कर देते हैं।

यह एक मूल वस्तु है, जो JAXB द्वारा JSON को क्रमबद्ध की जाएगी, एक इकाई वस्तु लौटाती है:

@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user){
    User newuser = ... do something like DB insert ...
    return newuser;
}

यह नए बनाए गए उपयोगकर्ता का JSON प्रतिनिधित्व लौटाएगा, लेकिन रिटर्न की स्थिति 200 होगी, न कि 201।

अब समस्या यह है कि अगर मैं Responseरिटर्न कोड सेट करने के लिए बिल्डर का उपयोग करना चाहता हूं, तो मुझे Responseअपने तरीके से एक ऑब्जेक्ट वापस करना होगा । मैं अभी भी Userधारावाहिक होने के लिए वस्तु कैसे लौटाऊं?

a) सर्वलेट रिस्पांस पर कोड सेट करें

इसे हल करने के लिए एक दृष्टिकोण एक सर्वलेट अनुरोध ऑब्जेक्ट प्राप्त करना है और मैन्युअल रूप से प्रतिक्रिया कोड स्वयं सेट करना है, जैसे गैरेट विल्सन के उत्तर में दिखाया गया है:

@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user, @Context final HttpServletResponse response){

    User newUser = ...

    //set HTTP code to "201 Created"
    response.setStatus(HttpServletResponse.SC_CREATED);
    try {
        response.flushBuffer();
    }catch(Exception e){}

    return newUser;
}

विधि अभी भी एक इकाई वस्तु लौटाती है और स्थिति कोड 201 होगा।

ध्यान दें कि इसे काम करने के लिए, मुझे प्रतिक्रिया को फ्लश करना पड़ा। यह हमारे अच्छे JAX_RS संसाधन में निम्न-स्तर के सर्वलेट एपीआई कोड का एक अप्रिय पुनरुत्थान है, और इससे भी बदतर, यह हेडर को इसके बाद अनम्य होने का कारण बनता है क्योंकि वे पहले से ही तार पर भेजे गए थे।

ख) इकाई के साथ प्रतिक्रिया वस्तु का उपयोग करें

उस मामले में सबसे अच्छा समाधान, प्रतिक्रिया वस्तु का उपयोग करना है और इस प्रतिक्रिया वस्तु पर क्रमबद्ध होने के लिए इकाई निर्धारित करना है। उस मामले में पेलोड इकाई के प्रकार को इंगित करने के लिए रिस्पांस ऑब्जेक्ट को सामान्य बनाना अच्छा होगा, लेकिन वर्तमान में ऐसा नहीं है।

@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response addUser(User user){

    User newUser = ...

    return Response.created(hateoas.buildLinkUri(newUser, "entity")).entity(restResponse).build();
}

उस स्थिति में, हम स्थिति कोड को 201 पर सेट करने के लिए रिस्पॉन्स बिल्डर वर्ग की बनाई गई विधि का उपयोग करते हैं। हम इकाई () विधि के माध्यम से प्रतिक्रिया के लिए इकाई वस्तु (उपयोगकर्ता) पास करते हैं।

इसका नतीजा यह है कि HTTP कोड 401 है जैसा हम चाहते थे, और प्रतिक्रिया का शरीर ठीक उसी JSON है जैसा कि हमने पहले किया था जब हमने उपयोगकर्ता ऑब्जेक्ट वापस किया था। यह एक स्थान हेडर भी जोड़ता है।

रिस्पांस क्लास में विभिन्न स्टेटस (स्टेटि) के लिए कई बिल्डर विधि होती है जैसे:

Response.accepted () Response.ok () Response.noContent () Response.notAcable ()

NB: हटियोस ऑब्जेक्ट एक सहायक वर्ग है जिसे मैंने संसाधन URI बनाने में मदद करने के लिए विकसित किया है। आपको अपने स्वयं के तंत्र के साथ आने की आवश्यकता होगी;)

यह इसके बारे में।

मुझे उम्मीद है कि यह लंबी प्रतिक्रिया किसी की मदद करती है :)


मुझे आश्चर्य है कि यदि प्रतिक्रिया के बजाय डेटा ऑब्जेक्ट को स्वयं वापस करने का एक साफ तरीका है। flushवास्तव में गंदा है।
एलिकएल्ज़िन-किलाका

1
सिर्फ मेरा एक पालतू जानवर: 401 का मतलब यह नहीं है कि उपयोगकर्ता अधिकृत नहीं है। इसका मतलब है कि ग्राहक अधिकृत नहीं है, क्योंकि सर्वर को नहीं पता है कि आप कौन हैं। यदि एक लॉग / अन्यथा मान्यता प्राप्त उपयोगकर्ता को एक निश्चित कार्रवाई करने की अनुमति नहीं है, तो सही प्रतिक्रिया कोड 403 निषिद्ध है।
डेमनब्लेक

69

उनके जवाब से काम चलेगा, लेकिन यह एक प्रदाता को जैक्सन + JAXB जैसे स्वचालित रूप से आपकी लौटी हुई वस्तु को JSON जैसे कुछ आउटपुट स्वरूप में बदलने की अनुमति देने के लिए पूरे दृष्टिकोण को संशोधित करता है। एक अपाचे सीएक्सएफ पोस्ट से प्रेरित (जो एक CXF- विशिष्ट वर्ग का उपयोग करता है) मैंने प्रतिक्रिया कोड को सेट करने का एक तरीका पाया है जो किसी भी JAX-RS कार्यान्वयन में काम करना चाहिए: HttpServletResponse संदर्भ को इंजेक्ट करें और मैन्युअल रूप से प्रतिक्रिया कोड सेट करें। उदाहरण के लिए, CREATEDजब उपयुक्त हो तो प्रतिक्रिया कोड सेट करने का तरीका यहां बताया गया है।

@Path("/foos/{fooId}")
@PUT
@Consumes("application/json")
@Produces("application/json")
public Foo setFoo(@PathParam("fooID") final String fooID, final Foo foo, @Context final HttpServletResponse response)
{
  //TODO store foo in persistent storage
  if(itemDidNotExistBefore) //return 201 only if new object; TODO app-specific logic
  {
    response.setStatus(Response.Status.CREATED.getStatusCode());
  }
  return foo;  //TODO get latest foo from storage if needed
}

सुधार: एक अन्य संबंधित उत्तर खोजने के बाद , मैंने सीखा कि एक एकल HttpServletResponseसदस्य के रूप में, यहां तक ​​कि सिंगलटन सेवा वर्ग (कम से कम RESTEasy) के लिए इंजेक्शन लगा सकता है !! कार्यान्वयन विवरण के साथ एपीआई को प्रदूषित करने की तुलना में यह बहुत बेहतर दृष्टिकोण है। यह इस तरह दिखेगा:

@Context  //injected response proxy supporting multiple threads
private HttpServletResponse response;

@Path("/foos/{fooId}")
@PUT
@Consumes("application/json")
@Produces("application/json")
public Foo setFoo(@PathParam("fooID") final String fooID, final Foo foo)
{
  //TODO store foo in persistent storage
  if(itemDidNotExistBefore) //return 201 only if new object; TODO app-specific logic
  {
    response.setStatus(Response.Status.CREATED.getStatusCode());
  }
  return foo;  //TODO get latest foo from storage if needed
}

आप वास्तव में दृष्टिकोणों को जोड़ सकते हैं: विधि को एनोटेट करें @Produces, और अंतिम रूप में मीडिया प्रकार को निर्दिष्ट न करें Response.ok, और आप अनुरोध को मिलान करने के लिए अपनी रिटर्न ऑब्जेक्ट को सही तरीके से JAXB-serialized उपयुक्त मीडिया प्रकार में प्राप्त करेंगे। (मैंने इसे एक एकल विधि के साथ आज़माया, जो XML या JSON में वापस आ सकती है: इस विधि को केवल @Producesएनोटेशन को छोड़कर या तो उल्लेख करने की आवश्यकता नहीं है ।)
Royston Shufflebotham

तुम सही हो गैरेट। मेरा उदाहरण एक सामग्री प्रकार प्रदान करने के जोर का एक चित्रण था। हमारे दृष्टिकोण समान हैं, लेकिन एक का उपयोग करने का विचार है MessageBodyWriterऔर Providerअंतर्निहित सामग्री बातचीत के लिए अनुमति देता है, हालांकि ऐसा लगता है कि आपका उदाहरण कुछ कोड गायब है। यहाँ एक और जवाब है जो मैंने दिया है जो इसे दिखाता है: stackoverflow.com/questions/5161466/…
उसकी आयु

8
मैं स्थिति कोड को ओवरराइड करने में असमर्थ हूं response.setStatus()। उदाहरण के लिए 404 नॉट फाउंड रिस्पॉन्स भेजने का एकमात्र तरीका रेस्पॉन्स स्टेटस कोड response.setStatus(404)एन सेट करना है, फिर आउटपुट स्ट्रीम को बंद कर दें response.getOutputStream().close()ताकि JAX-RS मेरी स्थिति को रीसेट न कर सके।
Rob Juurlink

2
मैं एक 201 कोड को सेट करने के लिए इस दृष्टिकोण का उपयोग करने में सक्षम था, लेकिन response.flushBuffer()मेरे प्रतिक्रिया कोड को ओवरराइड करने से पहले फ्रेमवर्क से बचने के लिए एक ट्राइ-कैच ब्लॉक जोड़ना पड़ा । बहुत साफ नहीं है।
पियरे हेनरी

1
@RobJuurlink, यदि आप विशेष रूप से वापस लौटना चाहते हैं, तो 404 Not Foundबस उपयोग करना आसान हो सकता है throw new NotFoundException()
गैरेट विल्सन

34

यदि आप Responseवस्तुओं की अपनी संसाधन परत को साफ रखना पसंद करते हैं, तो मैं आपको इसके @NameBindingकार्यान्वयन के लिए उपयोग और बाध्यकारी करने की सलाह देता हूं ContainerResponseFilter

यहाँ एनोटेशन का मांस है:

package my.webservice.annotations.status;

import javax.ws.rs.NameBinding;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Status {
  int CREATED = 201;
  int value();
}

यहाँ फ़िल्टर का मांस है:

package my.webservice.interceptors.status;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;

@Provider
public class StatusFilter implements ContainerResponseFilter {

  @Override
  public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) throws IOException {
    if (containerResponseContext.getStatus() == 200) {
      for (Annotation annotation : containerResponseContext.getEntityAnnotations()) {
        if(annotation instanceof Status){
          containerResponseContext.setStatus(((Status) annotation).value());
          break;
        }
      }
    }
  }
}

और फिर आपके संसाधन पर कार्यान्वयन बस हो जाता है:

package my.webservice.resources;

import my.webservice.annotations.status.StatusCreated;
import javax.ws.rs.*;

@Path("/my-resource-path")
public class MyResource{
  @POST
  @Status(Status.CREATED)
  public boolean create(){
    return true;
  }
}

एपीआई को साफ, अच्छा जवाब देता है। क्या यह @Status (कोड = 205) की तरह आपके एनोटेशन को परिमार्जित करना संभव होगा, और इंटरसेप्टर को आपके द्वारा निर्दिष्ट किए गए कोड से बदल दिया जाएगा? मुझे लगता है कि मूल रूप से आपको कोड्स को ओवरराइड करने के लिए जब भी आपको आवश्यकता होगी, आपको एक एनोटेशन देना होगा।
user2800708

@ user2800708, मैंने पहले ही अपने स्थानीय कोड के लिए ऐसा किया था, जैसा कि आपने सुझाव दिया था, उत्तर को अपडेट किया।
१५:५० बजे नटखट

अच्छा धन्यवाद। इसके साथ और कुछ आउटर ट्रिक्स के साथ, अब मैं मूल रूप से अपने कोड में REST एपीआई को साफ करने में सक्षम हूं, ताकि यह एक सरल जावा इंटरफ़ेस के अनुरूप हो, जिसमें REST का कोई उल्लेख न हो; इसका सिर्फ एक और RMI तंत्र है।
user2800708

6
StatusFilter में एनोटेशन के लिए लूपिंग के बजाय, आप @ प्रोवाइडर के अलावा @ स्टेटस के साथ फ़िल्टर को एनोटेट कर सकते हैं। फिर फ़िल्टर केवल उन संसाधनों पर बुलाया जाएगा जो @ स्थिति के साथ एनोटेट किए गए हैं। @ NameBinding
trevorism

1
अच्छा कॉलआउट @trevorism। वहाँ एक व्याख्या की नहीं-तो-अच्छा पक्ष प्रभाव है StatusFilterके साथ @Status: आप टिप्पणियों का इसके लिए एक सामान्य आपूर्ति करने के लिए या तो की जरूरत valueक्षेत्र, या एक घोषित जब वर्ग व्याख्या (पूर्व: @Status(200))। यह स्पष्ट रूप से आदर्श नहीं है।
फिल

6

यदि आप अपवाद के कारण स्थिति कोड को बदलना चाहते हैं, तो JAX-RS 2.0 के साथ आप इस तरह से एक ExceptionMapper लागू कर सकते हैं। यह पूरे ऐप के लिए इस तरह के अपवाद को संभालता है।

@Provider
public class UnauthorizedExceptionMapper implements ExceptionMapper<EJBAccessException> {

    @Override
    public Response toResponse(EJBAccessException exception) {
        return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build();
    }

}

6

यदि आपके WS-RS को एक त्रुटि की आवश्यकता है तो सिर्फ WebApplicationException का उपयोग क्यों न करें?

@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Path("{id}")
public MyEntity getFoo(@PathParam("id") long id,  @QueryParam("lang")long idLanguage) {

if (idLanguage== 0){
    // No URL parameter idLanguage was sent
    ResponseBuilder builder = Response.status(Response.Status.BAD_REQUEST);
    builder.entity("Missing idLanguage parameter on request");
    Response response = builder.build();
    throw new WebApplicationException(response);
    }
... //other stuff to return my entity
return myEntity;
}

4
मेरी राय में WebApplicationException क्लाइंट साइड त्रुटियों के लिए उपयुक्त नहीं है क्योंकि वे बड़े स्टैक के निशान फेंकते हैं। क्लाइंट त्रुटियों को सर्वर साइड स्टैक के निशान नहीं फेंकना चाहिए और इसके साथ लॉगिंग को प्रदूषित करना चाहिए।
Rob Juurlink

5

JAX-RS में मानक / कस्टम HTTP कोड के लिए समर्थन है। उदाहरण के लिए, ResponseBuilder और ResponseStatus देखें:

http://jackson.codehaus.org/javadoc/jax-rs/1.0/javax/ws/rs/core/Response.ResponseBuilder.html#status%28javax.ws.rs.core.Response.Status%29

ध्यान रखें कि JSON जानकारी संसाधन / एप्लिकेशन से जुड़े डेटा के बारे में अधिक है। HTTP कोड CRUD ऑपरेशन की स्थिति के बारे में अधिक अनुरोध किए जा रहे हैं। (कम से कम यह है कि यह REST-ful सिस्टम में कैसे माना जाता है)


लिंक टूट गया है
Umpa

5

मुझे बार-बार कोड के साथ एक जोंस संदेश बनाने के लिए यह बहुत उपयोगी लगा, इस तरह:

@POST
@Consumes("application/json")
@Produces("application/json")
public Response authUser(JsonObject authData) {
    String email = authData.getString("email");
    String password = authData.getString("password");
    JSONObject json = new JSONObject();
    if (email.equalsIgnoreCase(user.getEmail()) && password.equalsIgnoreCase(user.getPassword())) {
        json.put("status", "success");
        json.put("code", Response.Status.OK.getStatusCode());
        json.put("message", "User " + authData.getString("email") + " authenticated.");
        return Response.ok(json.toString()).build();
    } else {
        json.put("status", "error");
        json.put("code", Response.Status.NOT_FOUND.getStatusCode());
        json.put("message", "User " + authData.getString("email") + " not found.");
        return Response.status(Response.Status.NOT_FOUND).entity(json.toString()).build();
    }
}

4

कृपया यहाँ उदाहरण देखें, यह समस्या का सबसे अच्छा चित्रण करता है और यह जर्सी के नवीनतम (2.3.1) संस्करण में कैसे हल किया गया है।

https://jersey.java.net/documentation/latest/representations.html#d0e3586

इसमें मूल रूप से एक कस्टम अपवाद को परिभाषित करना और रिटर्न प्रकार को इकाई के रूप में रखना शामिल है। जब कोई त्रुटि होती है, तो अपवाद को फेंक दिया जाता है, अन्यथा, आप POJO लौटाते हैं।


मैं जोड़ना चाहूंगा कि ब्याज का उदाहरण वह है जहां वे अपने अपवाद वर्ग को परिभाषित करते हैं और Responseउसमें निर्माण करते हैं। बस CustomNotFoundExceptionवर्ग की तलाश करें और हो सकता है कि इसे अपनी पोस्ट पर कॉपी करें।
JBert

मैं त्रुटियों के लिए इस दृष्टिकोण का उपयोग करता हूं और मुझे यह पसंद है। लेकिन यह सफलता के कोड (अलग-अलग 200) पर लागू नहीं है, जैसे कि '201 क्रिएट'।
पियरे हेनरी

3

मैं JAX-RS का उपयोग नहीं कर रहा हूं, लेकिन मुझे एक समान परिदृश्य मिला है जहां मैं उपयोग करता हूं:

response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());

यह मेरे लिए स्प्रिंग एमवीसी का उपयोग करता है लेकिन यह पता लगाने का एक आसान तरीका है!
कैप्स

1

इसके अलावा, ध्यान दें कि डिफ़ॉल्ट रूप से जर्सी एक HTTP कोड 400 या अधिक के मामले में प्रतिक्रिया निकाय को ओवरराइड करेगा।

अपनी निर्दिष्ट इकाई को प्रतिक्रिया निकाय के रूप में प्राप्त करने के लिए, अपने in.xml कॉन्फ़िगरेशन फ़ाइल में अपने जर्सी में निम्नलिखित init-param को जोड़ने का प्रयास करें:

    <init-param>
        <!-- used to overwrite default 4xx state pages -->
        <param-name>jersey.config.server.response.setStatusOverSendError</param-name>
        <param-value>true</param-value>
    </init-param>

0

निम्नलिखित कोड ने मेरे लिए काम किया। MessageContext को एनोटेट सेटर के माध्यम से इंजेक्ट करना और मेरी "जोड़ें" विधि में स्थिति कोड सेट करना।

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;

import org.apache.cxf.jaxrs.ext.MessageContext;

public class FlightReservationService {

    MessageContext messageContext;

    private final Map<Long, FlightReservation> flightReservations = new HashMap<>();

    @Context
    public void setMessageContext(MessageContext messageContext) {
        this.messageContext = messageContext;
    }

    @Override
    public Collection<FlightReservation> list() {
        return flightReservations.values();
    }

    @Path("/{id}")
    @Produces("application/json")
    @GET
    public FlightReservation get(Long id) {
        return flightReservations.get(id);
    }

    @Path("/")
    @Consumes("application/json")
    @Produces("application/json")
    @POST
    public void add(FlightReservation booking) {
        messageContext.getHttpServletResponse().setStatus(Response.Status.CREATED.getStatusCode());
        flightReservations.put(booking.getId(), booking);
    }

    @Path("/")
    @Consumes("application/json")
    @PUT
    public void update(FlightReservation booking) {
        flightReservations.remove(booking.getId());
        flightReservations.put(booking.getId(), booking);
    }

    @Path("/{id}")
    @DELETE
    public void remove(Long id) {
        flightReservations.remove(id);
    }
}

0

पर विस्तार जवाब की Nthalk साथ Microprofile OpenAPI आप अपने प्रलेखन उपयोग करने के साथ वापसी कोड संरेखित कर सकते हैं @APIResponse एनोटेशन।

यह एक JAX-RS विधि को टैग करने की अनुमति देता है

@GET
@APIResponse(responseCode = "204")
public Resource getResource(ResourceRequest request) 

आप एक साथ इस मानकीकृत एनोटेशन पार्स कर सकते हैं ContainerResponseFilter

@Provider
public class StatusFilter implements ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
        if (responseContext.getStatus() == 200) {
            for (final var annotation : responseContext.getEntityAnnotations()) {
                if (annotation instanceof APIResponse response) {
                    final var rawCode = response.responseCode();
                    final var statusCode = Integer.parseInt(rawCode);

                    responseContext.setStatus(statusCode);
                }
            }
        }
    }

}

जब आप अपनी पद्धति पर कई एनोटेशन डालते हैं, तो कैविएट होता है

@APIResponse(responseCode = "201", description = "first use case")
@APIResponse(responseCode = "204", description = "because you can")
public Resource getResource(ResourceRequest request) 

-1

मैं संदेश बॉडी पाठकों और लेखकों के साथ जर्सी 2.0 का उपयोग कर रहा हूं। मेरे पास एक विशिष्ट इकाई के रूप में मेरी विधि वापसी प्रकार था जो कि संदेश बॉडी लेखक के कार्यान्वयन में भी उपयोग किया गया था और मैं उसी पूजो, एक SkuListDTO को वापस कर रहा था। @GET @Consumes ({"application / xml", "application / json"}) @Produces ({"application / xml", "application / json"}) @Path ("/ skuResync")

public SkuResultListDTO getSkuData()
    ....
return SkuResultListDTO;

सब बदल गया मैं यह था, मैंने लेखक कार्यान्वयन को अकेला छोड़ दिया और यह अभी भी काम कर रहा है।

public Response getSkuData()
...
return Response.status(Response.Status.FORBIDDEN).entity(dfCoreResultListDTO).build();
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.