স্ট্রিং ফিরতি একটি স্প্রিং এমভিসি @ রেসপনসবডি পদ্ধতিতে এইচটিটিপি 400 ত্রুটির সাথে কীভাবে প্রতিক্রিয়া জানানো হবে?


389

আমি @ResponseBodyনীচের মত ভিত্তিক পদ্ধতির সাথে একটি সহজ জেএসএন এপিআইয়ের জন্য স্প্রিং এমভিসি ব্যবহার করছি । (আমার কাছে ইতিমধ্যে সরাসরি একটি JSON উত্পাদনকারী স্তর রয়েছে))

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

প্রশ্নটি হল, প্রদত্ত দৃশ্যে, এইচটিটিপি 400 ত্রুটির সাথে সাড়া দেওয়ার সবচেয়ে সহজ এবং পরিষ্কার উপায় কী?

আমি এই মত পদ্ধতির জুড়ে এসেছি:

return new ResponseEntity(HttpStatus.BAD_REQUEST);

... তবে আমি এখানে এটি ব্যবহার করতে পারি না কারণ আমার পদ্ধতির রিটার্ন টাইপ স্ট্রিং, রেসপন্সএন্টিটি নয়।

উত্তর:


624

আপনার রিটার্নের ধরনটি এতে পরিবর্তন করুন ResponseEntity<>, তারপরে আপনি নিচে 400 এর জন্য ব্যবহার করতে পারেন

return new ResponseEntity<>(HttpStatus.BAD_REQUEST);

এবং সঠিক অনুরোধ জন্য

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

আপডেট 1

বসন্ত ৪.১-এর পরে রেসপন্সএন্টিটিতে হেল্পার পদ্ধতিগুলি ব্যবহার করা যেতে পারে

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

এবং

return ResponseEntity.ok(json);

আহ, যাতে আপনি ResponseEntityএটিও এর মতো ব্যবহার করতে পারেন । এটি দুর্দান্তভাবে কাজ করে এবং মূল কোডটিতে কেবল একটি সাধারণ পরিবর্তন — ধন্যবাদ!
জোনিক

আপনি কাস্টম শিরোনাম যুক্ত করতে পারেন যে কোনও সময় আপনাকে স্বাগত জানানো হয়েছে
রেসপন্সএন্টিটির

7
আপনি যদি স্ট্রিং ব্যাক ব্যতীত অন্য কিছু পাস করছেন? যেমন একটি পজো বা অন্যান্য অবজেক্টে?
মার্শিকাড্যান্স

11
এটি হবে 'রেসপন্স এন্টিটি <আপনার ক্লাস>'
বাসেম রেদা জোহদি

5
এই পদ্ধতির ব্যবহার করে আপনার আর রেসপন্সবিহীন টীকাগুলির আর দরকার নেই
Lu55

108

এর মতো কিছু কাজ করা উচিত, সহজ উপায় আছে কিনা তা আমি নিশ্চিত নই:

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

5
ধন্যবাদ! এটি কাজ করে এবং খুব সহজ। (এক্ষেত্রে অব্যবহৃত bodyএবং request
প্যারামগুলি

54

এটি করার সবচেয়ে কমপ্যাক্ট উপায় অগত্যা নয়, তবে বেশ পরিষ্কার আইএমও

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

স্প্রেটিং ৩.১++ ব্যবহার করে আপনি ব্যতিক্রম হ্যান্ডলার পদ্ধতিতে @ রেসপনসবডি ব্যবহার করতে পারেন, অন্যথায় একটি ModelAndViewবা কিছু ব্যবহার করুন ।

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


1
দুঃখিত, এটি কাজ করে না বলে মনে হচ্ছে। এটি লগগুলিতে দীর্ঘ স্ট্যাক ট্রেস সহ এইচটিটিপি 500 "সার্ভার ত্রুটি" উত্পন্ন করে: ERROR org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - Failed to invoke @ExceptionHandler method: public controller.TestController$MyError controller.TestController.handleException(controller.TestController$BadThingException) org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representationউত্তর থেকে এখানে কিছু নেই?
জোনিক

এছাড়াও, আমি আর একটি কাস্টম ধরণের (মাইরর) সংজ্ঞায়নের বিন্দুটি পুরোপুরি বুঝতে পারি নি। এটা কি প্রয়োজনীয়? আমি সর্বশেষ বসন্ত (3.2.2) ব্যবহার করছি using
জোনিক

1
এটা আমার জন্য কাজ করে. আমি javax.validation.ValidationExceptionপরিবর্তে ব্যবহার । (বসন্ত 3.1.4)
জেরি চেন

এটি আপনার পরিস্থিতিতে এবং ক্লায়েন্টের মধ্যে একটি মধ্যবর্তী স্তর রয়েছে যেখানে মধ্যবর্তী স্তরটির নিজস্ব ত্রুটি পরিচালনা করার ক্ষমতা রয়েছে এমন পরিস্থিতিতে এটি বেশ কার্যকর। এই উদাহরণটির জন্য আপনাকে ধন্যবাদ @ জুট্টি
স্টোরমেহক

এটি গ্রহণযোগ্য উত্তর হওয়া উচিত, কারণ এটি ব্যতিক্রম হ্যান্ডলিং কোডটি স্বাভাবিক প্রবাহের বাইরে নিয়ে যায় এবং এটি HttpServlet *
লিল্লিনাক্স

48

আমি কিছুটা বাস্তবায়ন পরিবর্তন করব:

প্রথমত, আমি একটি তৈরি করি UnknownMatchException:

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

@ রেসপনস স্ট্যাটাসের ব্যবহারটি নোট করুন , যা স্প্রিংয়ের দ্বারা স্বীকৃত হবে ResponseStatusExceptionResolver। যদি ব্যতিক্রম নিক্ষেপ করা হয়, তবে এটি সম্পর্কিত প্রতিক্রিয়া স্থিতির সাথে একটি প্রতিক্রিয়া তৈরি করবে। (আমি স্ট্যাটাস কোডটি পরিবর্তনের স্বাধীনতাও নিয়েছি 404 - Not Foundযা আমি এই ব্যবহারের ক্ষেত্রে আরও উপযুক্ত বলে মনে করি তবে আপনি HttpStatus.BAD_REQUESTযদি চান তবে আপনি আঁকতে পারেন))


পরবর্তী, আমি MatchServiceনিম্নলিখিত স্বাক্ষর আছে পরিবর্তন করতে হবে :

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

পরিশেষে, আমি নিয়ামক আপডেট করব এবং MappingJackson2HttpMessageConverterJSON সিরিয়ালাইজেশন স্বয়ংক্রিয়ভাবে পরিচালনা করতে স্প্রিংয়ের প্রতিনিধি করব (এটি ডিফল্টরূপে যোগ করা হয় যদি আপনি জ্যাকসনকে ক্লাসপথে যোগ করেন এবং হয় @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);
}

দ্রষ্টব্য, ডোমেন অবজেক্টগুলি ভিউ অবজেক্ট বা ডিটিও অবজেক্ট থেকে আলাদা করা খুব সাধারণ বিষয়। এটি একটি ছোট ডিটিও কারখানা যোগ করে সহজেই অর্জন করা যেতে পারে যা সিরিয়ালাইজযোগ্য জেএসওএন বস্তুটি দেয়:

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

আমার 500 এবং আমার লগগুলি রয়েছে: এ 28, 2015 5:23:31 পূর্বাহ্ণ org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver onMessage Sever: ত্রুটি পরিচালনা করার সময় ত্রুটি ঘটেছে, ছেড়ে দিন! org.apache.cxf.interceptor.Fault
রেজার

নিখুঁত সমাধান, আমি কেবল যুক্ত করতে চাই যে আমি আশা করি যে ডিটিও একটি সংমিশ্রণ Matchএবং অন্য কোনও অবজেক্ট।
মার্কো সুলা

32

এখানে একটি ভিন্ন পদ্ধতির। নীচের মত একটি Exceptionটীকায়িত কাস্টম তৈরি করুন @ResponseStatus

@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-references/htmlsingle/#mvc-ann-annotated- ব্যাতিক্রম


এই পদ্ধতির সাহায্যে আপনি স্ট্যাকট্রেসে যেখানেই থাকুন সেখানে এক্সিকিউশনটি শেষ করতে পারবেন "বিশেষ মান" না দিয়ে যা আপনি যে HTTP স্থিতি কোডটি প্রত্যাবর্তন করতে চান তা উল্লেখ করবে।
মুহাম্মদ জেলবানা

21

কিছু উত্তরে উল্লিখিত হিসাবে, আপনি যে প্রতিটি HTTP স্থিতি ফিরে আসতে চান তার জন্য একটি ব্যতিক্রম শ্রেণি তৈরি করার ক্ষমতা রয়েছে। প্রতিটি প্রকল্পের জন্য স্ট্যাটাস প্রতি শ্রেণি তৈরি করার ধারণাটি আমি পছন্দ করি না। পরিবর্তে আমি এখানে এসেছি।

  • একটি জেনেরিক ব্যতিক্রম তৈরি করুন যা কোনও HTTP স্থিতি গ্রহণ করে
  • একটি নিয়ামক পরামর্শ ব্যতিক্রম হ্যান্ডলার তৈরি করুন

কোডটি পাওয়া যাক

package com.javaninja.cam.exception;

import org.springframework.http.HttpStatus;


/**
 * The exception used to return a status and a message to the calling system.
 * @author norrisshelton
 */
@SuppressWarnings("ClassWithoutNoArgConstructor")
public class ResourceException extends RuntimeException {

    private HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;

    /**
     * Gets the HTTP status code to be returned to the calling system.
     * @return http status code.  Defaults to HttpStatus.INTERNAL_SERVER_ERROR (500).
     * @see HttpStatus
     */
    public HttpStatus getHttpStatus() {
        return httpStatus;
    }

    /**
     * Constructs a new runtime exception with the specified HttpStatus code and detail message.
     * The cause is not initialized, and may subsequently be initialized by a call to {@link #initCause}.
     * @param httpStatus the http status.  The detail message is saved for later retrieval by the {@link
     *                   #getHttpStatus()} method.
     * @param message    the detail message. The detail message is saved for later retrieval by the {@link
     *                   #getMessage()} method.
     * @see HttpStatus
     */
    public ResourceException(HttpStatus httpStatus, String message) {
        super(message);
        this.httpStatus = httpStatus;
    }
}

তারপরে আমি একটি নিয়ামক পরামর্শের ক্লাস তৈরি করি

package com.javaninja.cam.spring;


import com.javaninja.cam.exception.ResourceException;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;


/**
 * Exception handler advice class for all SpringMVC controllers.
 * @author norrisshelton
 * @see org.springframework.web.bind.annotation.ControllerAdvice
 */
@org.springframework.web.bind.annotation.ControllerAdvice
public class ControllerAdvice {

    /**
     * Handles ResourceExceptions for the SpringMVC controllers.
     * @param e SpringMVC controller exception.
     * @return http response entity
     * @see ExceptionHandler
     */
    @ExceptionHandler(ResourceException.class)
    public ResponseEntity handleException(ResourceException e) {
        return ResponseEntity.status(e.getHttpStatus()).body(e.getMessage());
    }
}

এটি ব্যবহার করতে

throw new ResourceException(HttpStatus.BAD_REQUEST, "My message");

http://javaninja.net/2016/06/throwing-exceptions-messages-spring-mvc-controller/


খুব ভাল পদ্ধতি .. একটি সাধারণ স্ট্রিংয়ের পরিবর্তে আমি ত্রুটি কোড এবং বার্তা ক্ষেত্রগুলির সাথে একটি জেএসএন ফিরিয়ে দিতে পছন্দ করি ..
ইয়াভুজ

1
এটি সঠিক উত্তর হতে হবে, কাস্টম স্থিতি কোড এবং বার্তা সহ একটি জেনেরিক এবং গ্লোবাল ব্যতিক্রম হ্যান্ডলার: ডি
পেড্রো সিলভা

10

আমি এটি আমার বসন্ত বুট অ্যাপ্লিকেশনটিতে ব্যবহার করছি

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

9

সবচেয়ে সহজ উপায় একটি নিক্ষেপ ResponseStatusException

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

3
সেরা উত্তর: রিটার্নের ধরন পরিবর্তন করার দরকার নেই এবং আপনার নিজস্ব ব্যতিক্রম তৈরি করার দরকার নেই। এছাড়াও, রেসপন্সস্ট্যাটাস এক্সেপশন প্রয়োজন হলে কোনও কারণ বার্তা যুক্ত করতে দেয়।
মিগস

জেনে রাখা জরুরী যে রেসপন্স স্ট্যাটাস এক্সেপশনটি কেবল স্প্রিং সংস্করণ 5+ এ উপলব্ধ
ইথান কনার

2

স্প্রিং বুটের সাহায্যে, কেন এটি প্রয়োজনীয় ছিল তা আমি পুরোপুরি নিশ্চিত নই (আমি /errorফ্যালব্যাক পেয়েছি যদিও একটিতে @ResponseBodyসংজ্ঞায়িত করা হয়েছিল @ExceptionHandler), কিন্তু নিম্নলিখিতটি নিজেই কার্যকর হয়নি:

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

JacksonMapper, যেমন "convertable" এটি পরিচালনা করা হয়নি তাই আমি getters / setters যোগ করার জন্য ছিল, এবং আমি আরও যোগ করেছেন @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."}

0

আপনি কেবল throw new HttpMessageNotReadableException("error description")বসন্তের ডিফল্ট ত্রুটি পরিচালনা থেকে উপকৃত হতে পারেন ।

তবে, সেই ডিফল্ট ত্রুটিগুলির মতোই, কোনও প্রতিক্রিয়া সংস্থা সেট করা হবে না।

আমি অনুরোধগুলি প্রত্যাখাত করার সময় এই দরকারী বলে মনে করি যখন যুক্তিযুক্তভাবে কেবল হস্তশিল্প নির্মিত হতে পারে, সম্ভাব্যভাবে একটি নীতিমূলক অভিপ্রায়টি নির্দেশ করে, যেহেতু তারা এই সত্যটিকে অস্পষ্ট করে দেয় যে অনুরোধটি গভীর, কাস্টম বৈধতা এবং এর মানদণ্ডের ভিত্তিতে প্রত্যাখাত হয়েছিল।

Hth, dtk


HttpMessageNotReadableException("error description")অবচয় করা হয়।
কুবা Šimonovský

0

আরেকটি পন্থা ব্যবহার করা @ExceptionHandlerসঙ্গে @ControllerAdvice, একই ক্লাসে সব আপনার হ্যান্ডেলার কেন্দ্রিভূত করতে যদি না আপনি প্রতি নিয়ামক আপনি একটি ব্যতিক্রম পরিচালনা করতে চান মধ্যে হ্যান্ডলার পদ্ধতি করা উচিত নয়।

আপনার হ্যান্ডলার শ্রেণি:

@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {

  @ExceptionHandler(MyBadRequestException.class)
  public ResponseEntity<MyError> handleException(MyBadRequestException e) {
    return ResponseEntity
        .badRequest()
        .body(new MyError(HttpStatus.BAD_REQUEST, e.getDescription()));
  }
}

আপনার কাস্টম ব্যতিক্রম:

public class MyBadRequestException extends RuntimeException {

  private String description;

  public MyBadRequestException(String description) {
    this.description = description;
  }

  public String getDescription() {
    return this.description;
  }
}

এখন আপনি আপনার যে কোনও নিয়ন্ত্রণকারীর থেকে ব্যতিক্রম ছুঁড়ে ফেলতে পারেন এবং আপনার পরামর্শদণ্ডের অভ্যন্তরে অন্য হ্যান্ডলারের সংজ্ঞা দিতে পারেন।


-1

আমি মনে করি এই থ্রেডটির আসলে সবচেয়ে সহজ, পরিষ্কার সমাধান রয়েছে যা স্প্রিং সরবরাহ করে এমন জেএসওএন মার্শালিং সরঞ্জামগুলিকে কোরবানি দেয় না:

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

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.