java एक स्प्रिंग एमवीसी @ResponseBody विधि स्ट्रिंग लौटने में HTTP 400 त्रुटि के साथ कैसे प्रतिक्रिया करें?




spring spring-mvc (8)

मैं एक साधारण JSON एपीआई के लिए स्प्रिंग एमवीसी का उपयोग कर रहा हूं, जिसमें निम्नलिखित के रूप में @ResponseBody आधारित दृष्टिकोण है। (मेरे पास पहले से ही जेएसओएन का उत्पादन करने वाली एक सेवा परत है।)

@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public String match(@PathVariable String matchId) {
    String json = matchService.getMatchJson(matchId);
    if (json == null) {
        // TODO: how to respond with e.g. 400 "bad request"?
    }
    return json;
}

प्रश्न यह है कि, दिए गए परिदृश्य में, एक HTTP 400 त्रुटि के साथ प्रतिक्रिया करने का सबसे सरल, साफ तरीका क्या है ?

मैं दृष्टिकोण के पार आया था जैसे:

return new ResponseEntity(HttpStatus.BAD_REQUEST);

... लेकिन मैं इसका उपयोग यहां नहीं कर सकता क्योंकि मेरी विधि का रिटर्न प्रकार स्ट्रिंग है, प्रतिक्रिया नहीं है।


मुझे लगता है कि इस धागे में वास्तव में सबसे आसान, स्वच्छ समाधान है, जो वसंत प्रदान करता है जो JSON मार्शलिंग उपकरण का त्याग नहीं करता है:

https://.com/a/16986372/1278921


अपने रिटर्न प्रकार को ResponseEntity<> बदलें, फिर आप 400 के लिए नीचे उपयोग कर सकते हैं

return new ResponseEntity<>(HttpStatus.BAD_REQUEST);

और सही अनुरोध के लिए

return new ResponseEntity<>(json,HttpStatus.OK);

अद्यतन 1

वसंत 4.1 के बाद ResponseEntity में सहायक तरीके के रूप में उपयोग किया जा सकता है

return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);

तथा

return ResponseEntity.ok(json);

मैं अपने वसंत बूट आवेदन में इसका उपयोग कर रहा हूँ

@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public ResponseEntity<?> match(@PathVariable String matchId, @RequestBody String body,
            HttpServletRequest request, HttpServletResponse response) {

    Product p;
    try {
      p = service.getProduct(request.getProductId());
    } catch(Exception ex) {
       return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
    }

    return new ResponseEntity(p, HttpStatus.OK);
}

स्प्रिंग बूट के साथ, मुझे पूरी तरह से यकीन नहीं है कि यह क्यों जरूरी था (मुझे /error फॉलबैक मिला, भले ही @ResponseBody को @ResponseBody पर परिभाषित किया गया था), लेकिन निम्नलिखित में स्वयं काम नहीं किया:

@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorMessage handleIllegalArguments(HttpServletRequest httpServletRequest, IllegalArgumentException e) {
    log.error("Illegal arguments received.", e);
    ErrorMessage errorMessage = new ErrorMessage();
    errorMessage.code = 400;
    errorMessage.message = e.getMessage();
    return errorMessage;
}

यह अभी भी एक अपवाद फेंक दिया, जाहिर है क्योंकि कोई उत्पादक मीडिया प्रकार को अनुरोध विशेषता के रूप में परिभाषित नहीं किया गया था:

// AbstractMessageConverterMethodProcessor
@SuppressWarnings("unchecked")
protected <T> void writeWithMessageConverters(T value, MethodParameter returnType,
        ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

    Class<?> valueType = getReturnValueType(value, returnType);
    Type declaredType = getGenericType(returnType);
    HttpServletRequest request = inputMessage.getServletRequest();
    List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
    List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
if (value != null && producibleMediaTypes.isEmpty()) {
        throw new IllegalArgumentException("No converter found for return value of type: " + valueType);   // <-- throws
    }

// ....

@SuppressWarnings("unchecked")
protected List<MediaType> getProducibleMediaTypes(HttpServletRequest request, Class<?> valueClass, Type declaredType) {
    Set<MediaType> mediaTypes = (Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
    if (!CollectionUtils.isEmpty(mediaTypes)) {
        return new ArrayList<MediaType>(mediaTypes);

तो मैंने उन्हें जोड़ा।

@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorMessage handleIllegalArguments(HttpServletRequest httpServletRequest, IllegalArgumentException e) {
    Set<MediaType> mediaTypes = new HashSet<>();
    mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
    httpServletRequest.setAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
    log.error("Illegal arguments received.", e);
    ErrorMessage errorMessage = new ErrorMessage();
    errorMessage.code = 400;
    errorMessage.message = e.getMessage();
    return errorMessage;
}

और यह मुझे "समर्थित संगत मीडिया प्रकार" के माध्यम से मिला, लेकिन फिर भी यह काम नहीं किया, क्योंकि मेरी ErrorMessage दोषपूर्ण था:

public class ErrorMessage {
    int code;

    String message;
}

जैक्सनमैपर ने इसे "परिवर्तनीय" के रूप में नहीं संभाला, इसलिए मुझे @JsonProperty / सेटर्स जोड़ना पड़ा, और मैंने @JsonProperty एनोटेशन भी जोड़ा

public class ErrorMessage {
    @JsonProperty("code")
    private int code;

    @JsonProperty("message")
    private String message;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

तब मुझे अपना संदेश इरादे के रूप में मिला

{"code":400,"message":"An \"url\" parameter must be defined."}

यहां एक अलग दृष्टिकोण है। @ResponseStatus साथ एनोटेटेड कस्टम Exception बनाएं, जैसे कि निम्न एक।

@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Not Found")
public class NotFoundException extends Exception {

    public NotFoundException() {
    }
}

और जरूरत पड़ने पर इसे फेंक दें।

@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public String match(@PathVariable String matchId) {
    String json = matchService.getMatchJson(matchId);
    if (json == null) {
        throw new NotFoundException();
    }
    return json;
}

यहां स्प्रिंग प्रलेखन देखें: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-ann-annotated-exceptions


इस तरह कुछ काम करना चाहिए, मुझे यकीन नहीं है कि एक आसान तरीका है या नहीं:

@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public String match(@PathVariable String matchId, @RequestBody String body,
            HttpServletRequest request, HttpServletResponse response) {
    String json = matchService.getMatchJson(matchId);
    if (json == null) {
        response.setStatus( HttpServletResponse.SC_BAD_REQUEST  );
    }
    return json;
}

मैं थोड़ा कार्यान्वयन बदल दूंगा:

सबसे पहले, मैं एक UnknownMatchException बना देता UnknownMatchException :

@ResponseStatus(HttpStatus.NOT_FOUND)
public class UnknownMatchException extends RuntimeException {
    public UnknownMatchException(String matchId) {
        super("Unknown match: " + matchId);
    }
}

@ResponseStatus के उपयोग पर ध्यान दें, जिसे स्प्रिंग के ResponseStatusExceptionResolver द्वारा पहचाना जाएगा। अगर अपवाद फेंक दिया जाता है, तो यह संबंधित प्रतिक्रिया स्थिति के साथ प्रतिक्रिया देगा। (मैंने स्टेटस कोड को 404 - Not Found में बदलने की स्वतंत्रता भी ली 404 - Not Found कि मुझे इस उपयोग के मामले के लिए और अधिक उपयुक्त लगता है, लेकिन यदि आप चाहें तो आप HttpStatus.BAD_REQUEST चिपके रह सकते हैं।)

इसके बाद, मैं निम्नलिखित हस्ताक्षर MatchService लिए MatchService को बदल MatchService :

interface MatchService {
    public Match findMatch(String matchId);
}

अंत में, मैं नियंत्रक को अद्यतन करता हूं और जेएसओएन क्रमशः स्वचालित रूप से संभालने के लिए स्प्रिंग के MappingJackson2HttpMessageConverter को प्रतिनिधि करता MappingJackson2HttpMessageConverter (यदि आप क्लासपाथ में जैक्सन जोड़ते हैं और या तो @EnableWebMvc या <mvc:annotation-driven /> को अपनी कॉन्फ़िगरेशन में जोड़ते हैं तो इसे डिफ़ॉल्ट रूप से जोड़ा जाता है, देखें संदर्भ दस्तावेज़ ):

@RequestMapping(value = "/matches/{matchId}", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Match match(@PathVariable String matchId) {
    // throws an UnknownMatchException if the matchId is not known 
    return matchService.findMatch(matchId);
}

नोट, डोमेन ऑब्जेक्ट्स को ऑब्जेक्ट्स या डीटीओ ऑब्जेक्ट्स से अलग करना बहुत आम है। इसे आसानी से एक छोटा डीटीओ कारखाना जोड़कर हासिल किया जा सकता है जो धारावाहिक JSON ऑब्जेक्ट देता है:

@RequestMapping(value = "/matches/{matchId}", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public MatchDTO match(@PathVariable String matchId) {
    Match match = matchService.findMatch(matchId);
    return MatchDtoFactory.createDTO(match);
}

ऐसा करने का सबसे कॉम्पैक्ट तरीका जरूरी नहीं है, लेकिन काफी साफ आईएमओ है

if(json == null) {
    throw new BadThingException();
}
...

@ExceptionHandler(BadThingException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public @ResponseBody MyError handleException(BadThingException e) {
    return new MyError("That doesnt work");
}

संपादित करें यदि आप स्प्रिंग 3.1+ का उपयोग करते हुए अपवाद हैंडलर विधि में @ResponseBody का उपयोग कर सकते हैं, अन्यथा ModelAndView और ModelAndView या कुछ का उपयोग करें।

https://jira.springsource.org/browse/SPR-6902





http-error