স্প্রিং বুট REST পরিষেবা ব্যতিক্রম হ্যান্ডলিং


172

আমি একটি বৃহত আকারের REST পরিষেবাদি সার্ভার স্থাপন করার চেষ্টা করছি। আমরা স্প্রিং বুট 1.2.1 স্প্রিং 4.1.5 এবং জাভা 8 ব্যবহার করছি Our আমাদের নিয়ামকরা @ রিস্টকন্ট্রোলার এবং মানক @RequestMapping টীকাগুলি বাস্তবায়ন করছেন।

আমার সমস্যাটি হ'ল স্প্রিং বুট কন্ট্রোলার ব্যতিক্রমগুলির জন্য একটি ডিফল্ট পুনর্নির্দেশ সেট আপ করে /error। ডক্স থেকে:

স্প্রিং বুট ডিফল্টরূপে একটি / ত্রুটি ম্যাপিং সরবরাহ করে যা সমস্ত ত্রুটিগুলি বোধগম্যভাবে পরিচালনা করে এবং সার্ভলেট পাত্রে এটি একটি 'গ্লোবাল' ত্রুটি পৃষ্ঠা হিসাবে নিবন্ধিত হয়েছে।

নোড.জেএস দিয়ে আরআরএসটি অ্যাপ্লিকেশনগুলি লেখার বছর থেকে আসছে, এটি আমার কাছে বোধগম্য anything কোনও পরিষেবা শেষ পয়েন্টটি যে ব্যতিক্রম উত্পন্ন করে তার প্রতিক্রিয়াতে ফিরে আসা উচিত। আমি বুঝতে পারছি না কেন আপনি সম্ভবত এমন কোনও কৌনিক বা জ্যাকিউরি এসপিএ গ্রাহককে কেন পুনর্নির্দেশ পাঠিয়েছিলেন যা কেবল উত্তর খুঁজছে এবং পুনর্নির্দেশের কোনও পদক্ষেপ নিতে পারে না বা করবে না।

আমি যা করতে চাই তা হ'ল একটি বৈশ্বিক ত্রুটি হ্যান্ডলার সেট আপ করতে পারে যা কোনও ব্যতিক্রম নিতে পারে - উদ্দেশ্যমূলকভাবে একটি অনুরোধ ম্যাপিং পদ্ধতি থেকে ছুঁড়ে দেওয়া হয় বা স্প্রিংয়ের দ্বারা স্বয়ংক্রিয়ভাবে উত্পন্ন হয় (404 যদি অনুরোধের পথে স্বাক্ষরের জন্য কোনও হ্যান্ডলার পদ্ধতি পাওয়া যায় না) এবং ফিরে আসুন কোনও এমভিসি পুনঃনির্দেশ ছাড়াই ক্লায়েন্টের কাছে স্ট্যান্ডার্ড ফর্ম্যাটেড ত্রুটির প্রতিক্রিয়া (400, 500, 503, 404)। বিশেষত, আমরা ত্রুটিটি গ্রহণ করতে যাচ্ছি, এটি কোনও ইউইউডি দিয়ে নোএসকিউএল-এ লগ করুন, তারপরে JSON বডিটিতে লগ এন্ট্রির ইউআইডি দিয়ে সঠিক HTTP ত্রুটি কোডটি ক্লায়েন্টকে ফিরিয়ে দিন।

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

আমি কী মিস করেছি তা কেউ আমাকে বলতে পারে, বা নোড.জেএস এর সাথে কাজ করা আরও সহজ হবে এমন শৃঙ্খলা না করেই কীভাবে এটি করা যায় সে সম্পর্কে আমাকে সঠিক দিকে নির্দেশ করতে পারেন?


6
ক্লায়েন্টকে আসলে কোনও পুনঃনির্দেশ পাঠানো হয় না। পুনর্নির্দেশটি সার্লেলেট ধারক (যেমন টমক্যাট) দ্বারা অভ্যন্তরীণভাবে পরিচালনা করা হয়।
অরেঞ্জডগ

1
আমার ব্যতিক্রম হ্যান্ডলারগুলিতে @ রেসপনস স্ট্যাটাস টীকা মুছে ফেলা আমার যা প্রয়োজন তা ছিল; দেখতে stackoverflow.com/questions/35563968/...
pmorken

উত্তর:


131

নতুন উত্তর (2016-04-20)

স্প্রিং বুট ব্যবহার করে 1.3.1. নিখরচায়

নতুন পদক্ষেপ 1 - নিম্নলিখিত বৈশিষ্ট্যগুলিকে অ্যাপ্লিকেশনটিতে যুক্ত করা সহজ এবং স্বল্প হস্তক্ষেপজনক ive

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

বিদ্যমান ডিসপ্যাচারসার সার্লেট উদাহরণটি পরিবর্তনের চেয়ে অনেক সহজ (নীচে হিসাবে)! - জেও '

যদি একটি সম্পূর্ণ আরএসটিফুল অ্যাপ্লিকেশন নিয়ে কাজ করা হয় তবে স্থিতিশীল সংস্থাগুলির স্বয়ংক্রিয় ম্যাপিংটি অক্ষম করা খুব গুরুত্বপূর্ণ কারণ আপনি যদি স্থির সংস্থানগুলি পরিচালনা করার জন্য স্প্রিং বুটের ডিফল্ট কনফিগারেশন ব্যবহার করেন তবে রিসোর্স হ্যান্ডলার অনুরোধটি পরিচালনা করবেন (এটি সর্বশেষে অর্ডার করা হয়েছে এবং ম্যাপ করা হয়েছে / ** যার অর্থ এটি অ্যাপ্লিকেশনটিতে অন্য কোনও হ্যান্ডলার দ্বারা পরিচালিত না হওয়া অনুরোধগুলি গ্রহণ করে) তাই প্রেরণকারী সার্লেটটি কোনও ব্যতিক্রম ছোঁড়ার সুযোগ না পায়।


নতুন উত্তর (2015-12-04)

স্প্রিং বুট 1.2.7.REREASE ব্যবহার করে

নতুন পদক্ষেপ 1 - আমি "থ্রোএক্সসেপশনআইফনোহ্যান্ডলারফাউন্ড" পতাকা সেট করার অনেক কম অনুপ্রবেশমূলক উপায় পেয়েছি। আপনার অ্যাপ্লিকেশন প্রারম্ভিককরণের ক্লাসে এটির নীচে ডিসপ্যাচারসারলিট প্রতিস্থাপন কোডটি (পদক্ষেপ 1) প্রতিস্থাপন করুন:

@ComponentScan()
@EnableAutoConfiguration
public class MyApplication extends SpringBootServletInitializer {
    private static Logger LOG = LoggerFactory.getLogger(MyApplication.class);
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(MyApplication.class, args);
        DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    }

এই ক্ষেত্রে, আমরা বিদ্যমান ডিসপ্যাচার সার্ভলেটে ফ্ল্যাগটি সেট করছি, যা স্প্রিং বুট ফ্রেমওয়ার্কের দ্বারা কোনও স্বয়ং-কনফিগারেশন সংরক্ষণ করে।

আরও একটি জিনিস আমি খুঁজে পেয়েছি - @EnableWebMvc টীকাটি স্প্রিং বুটের পক্ষে মারাত্মক। হ্যাঁ, এই টীকাটি নীচের বর্ণিত হিসাবে সমস্ত নিয়ামক ব্যতিক্রমগুলি ধরতে সক্ষম হওয়ার মতো জিনিসগুলিকে সক্ষম করে, তবে এটি স্প্রিং বুট সাধারণত সরবরাহ করে এমন সহায়ক প্রচুর পরিমাণে স্বয়ংক্রিয় কনফিগারেশনকে হত্যা করে। আপনি স্প্রিং বুট ব্যবহার করার সময় সেই টীকাটি অত্যন্ত সতর্কতার সাথে ব্যবহার করুন।


আসল উত্তর:

আরও অনেক গবেষণা এবং এখানে পোস্ট হওয়া সমাধানগুলি অনুসরণ করার পরে (সহায়তার জন্য ধন্যবাদ!) এবং স্প্রিং কোডটিতে কোনও অল্প পরিমাণ রানটাইম ট্রেস করার পরে, আমি অবশেষে এমন একটি কনফিগারেশন পেয়েছি যা সমস্ত ব্যতিক্রম (ত্রুটিগুলি নয়, তবে পড়ুন) পরিচালনা করবে 404 এস সহ

পদক্ষেপ 1 - "হ্যান্ডলারটি পাওয়া যায় নি" অবস্থার জন্য এমভিসি ব্যবহার বন্ধ করার জন্য স্প্রিংবুটকে বলুন। আমরা চাই স্প্রিং ক্লায়েন্টের কাছে ফিরে যাওয়ার পরিবর্তে "/ ত্রুটি" এ পুনঃনির্দেশিত একটি ব্যতিক্রম ছুঁড়ে ফেলে। এটি করার জন্য, আপনার কনফিগারেশন ক্লাসের একটিতে আপনাকে প্রবেশ করতে হবে:

// NEW CODE ABOVE REPLACES THIS! (2015-12-04)
@Configuration
public class MyAppConfig {
    @Bean  // Magic entry 
    public DispatcherServlet dispatcherServlet() {
        DispatcherServlet ds = new DispatcherServlet();
        ds.setThrowExceptionIfNoHandlerFound(true);
        return ds;
    }
}

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

পদক্ষেপ 2 - এখন যে কোনও হ্যান্ডলার না পাওয়া গেলে স্প্রিং বুট একটি ব্যতিক্রম ছুঁড়ে ফেলবে, সেই ব্যতিক্রমটি কোনও ইউনিফাইড ব্যতিক্রম হ্যান্ডলারটিতে অন্য কোনও ব্যক্তির সাথে পরিচালনা করা যেতে পারে:

@EnableWebMvc
@ControllerAdvice
public class ServiceExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(Throwable.class)
    @ResponseBody
    ResponseEntity<Object> handleControllerException(HttpServletRequest req, Throwable ex) {
        ErrorResponse errorResponse = new ErrorResponse(ex);
        if(ex instanceof ServiceException) {
            errorResponse.setDetails(((ServiceException)ex).getDetails());
        }
        if(ex instanceof ServiceHttpException) {
            return new ResponseEntity<Object>(errorResponse,((ServiceHttpException)ex).getStatus());
        } else {
            return new ResponseEntity<Object>(errorResponse,HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    @Override
    protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Map<String,String> responseBody = new HashMap<>();
        responseBody.put("path",request.getContextPath());
        responseBody.put("message","The URL you have reached is not in service at this time (404).");
        return new ResponseEntity<Object>(responseBody,HttpStatus.NOT_FOUND);
    }
    ...
}

মনে রাখবেন যে আমি মনে করি এখানে "@EnableWebMvc" টীকাটি উল্লেখযোগ্য। দেখে মনে হচ্ছে এটি ছাড়া এটির কোনওটিই কাজ করে না। এবং এটি হ'ল - আপনার স্প্রিং বুট অ্যাপ্লিকেশন এখন উপরের হ্যান্ডলার শ্রেণিতে 404s সহ সমস্ত ব্যতিক্রম ধরবে এবং আপনি যেমন খুশি তেমন করতে পারেন।

একটি শেষ পয়েন্ট - নিক্ষিপ্ত ত্রুটিগুলি ধরা এটির উপায় বলে মনে হচ্ছে না। আমার ত্রুটিগুলি ধরার জন্য দিকগুলি ব্যবহার করার এবং সেগুলি ব্যতিক্রমগুলিতে রূপান্তর করার জন্য একটি ভীত ধারণা আছে যা উপরের কোডটি এরপরে মোকাবেলা করতে পারে তবে বাস্তবে এটি বাস্তবায়নের চেষ্টা করার আমার এখনও সময় হয়নি। আশা করি এটি কাউকে সাহায্য করবে।

যে কোনও মন্তব্য / সংশোধন / বর্ধন প্রশংসা করা হবে।


একটি নতুন প্রেরণ সার্ভলেট শিম তৈরির পরিবর্তে আপনি কোনও পোস্ট প্রসেসরে পতাকাটি উল্টাতে পারেন: আপনার ক্লাস বিয়ানপোস্টপ্রসেসর প্রয়োগ করে {... `পাবলিক অবজেক্ট পোস্টপ্রসেসবাইফ্রান্সইনাইজেশন (অবজেক্ট বিন, স্ট্রিং বিননম) নিক্ষেপ ception যদি (বিয়ান উদাহরণস্বরূপ ডিসপ্যাচার সার্ভলেট) {// অন্যথায় আমাদের ব্যতিক্রম হ্যান্ডলারটি ((ডিসপ্যাচারসারভাইলেট) শিম) কিক করার আগে একটি 404 পান T সেট থ্রো এক্সসেপশনআইফনো হ্যান্ডলারফাউন্ড (সত্য); } শিম রিটার্ন; } সর্বজনীন অবজেক্ট পোস্টপ্রোসেসএফটারআইনিটাইজেশন (অবজেক্ট বিন, স্ট্রিং বিন নাম) বিনসেক্সেক্সেশন নিক্ষেপ করে {রিটার বিট; }
ওয়াডেজ

1
আমার এই সমস্যা আছে তবে ডিসপ্যাচার সার্ভলেটটি কাস্টমাইজ করা আমার পক্ষে কাজ করে না। এই অতিরিক্ত শিম এবং কনফিগার ব্যবহারের জন্য বুটের জন্য কি কোনও অতিরিক্ত যাদু প্রয়োজন?
ইয়ানগিলহাম

3
@ ইয়ানগিলহ্যাম আমিও এটি স্প্রিং বুট 1.2 1.2 এর সাথে কাজ করতে পাই না। এমনকি ক্লাসে @ExceptionHandlerরাখার সময় আমি কোনও পদ্ধতি কল করি না @ControllerAdviceযদিও তারা ক্লাসে রাখলে সঠিকভাবে কাজ করে @RestController@EnableWebMvcচালু থাকে @ControllerAdviceএবং @Configurationশ্রেণী (আমি প্রতি সমন্বয় পরীক্ষিত)। কোন ধারণা বা কাজের উদাহরণ? // @ অ্যান্ডি উইলকিনসন
ফ্রাভাবে

1
যে কেউ এই প্রশ্ন ও উত্তর লেখা সংশ্লিষ্ট SpringBoot ইস্যু কটাক্ষপাত থাকা উচিত GitHub
FrVaBe

1
নিশ্চিত না @agpt আমার একটি অভ্যন্তরীণ প্রকল্প রয়েছে যা আমি ১.৩.০-এ চলে যেতে পারি এবং আমার সেটআপে কী প্রভাব পড়ে তা দেখতে পাচ্ছি এবং আমি কী খুঁজে পাই তা আপনাকে জানাতে পারি।
ogradyjd

41

স্প্রিং বুট সহ 1.4+ সহজ ব্যতিক্রম হ্যান্ডলিংয়ের জন্য নতুন শীতল ক্লাস যুক্ত করা হয়েছিল যা বয়লারপ্লেট কোডটি সরিয়ে ফেলতে সহায়তা করে।

@RestControllerAdviceব্যতিক্রম হ্যান্ডলিংয়ের জন্য একটি নতুন সরবরাহ করা হয়, এটি @ControllerAdviceএবং এর সংমিশ্রণ @ResponseBody। এই নতুন টীকাটি ব্যবহার করার সময় আপনি পদ্ধতিটি @ResponseBodyচালু করতে পারেন @ExceptionHandler

অর্থাত

@RestControllerAdvice
public class GlobalControllerExceptionHandler {

    @ExceptionHandler(value = { Exception.class })
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiErrorResponse unknownException(Exception ex, WebRequest req) {
        return new ApiErrorResponse(...);
    }
}

404 ত্রুটিগুলি @EnableWebMvcঅ্যাডোটেশন যুক্ত করার জন্য এবং অ্যাপ্লিকেশনটিতে নিম্নলিখিতগুলি হ্যান্ডল করার জন্য যথেষ্ট was
spring.mvc.throw-exception-if-no-handler-found=true

আপনি এখানে উত্সগুলি খুঁজে পেতে এবং খেলতে পারেন:
https://github.com/magiccrafter/spring-boot-exception-handling


7
এটি সত্যিই সহায়ক, আপনাকে ধন্যবাদ। তবে কেন পাই না যে আমাদের `বসন্ত.এমভিসি.থ্রো-ব্যতিক্রম-যদি-না-হ্যান্ডলার-সন্ধান = সত্য` সহ we @EnableWebMvc to দরকার ` আমার প্রত্যাশাটি @RestControllerAdviceঅতিরিক্ত কনফিগারেশন ছাড়াই সমস্ত ব্যতিক্রম হ্যান্ডেল করা ছিল । আমি এখানে কি মিস করছি?
ফিস্করা

28

আমি মনে করি ResponseEntityExceptionHandlerআপনার প্রয়োজনীয়তা পূরণ করে HTTP 400 এর জন্য কোডের একটি নমুনা টুকরা:

@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {

  @ResponseStatus(value = HttpStatus.BAD_REQUEST)
  @ExceptionHandler({HttpMessageNotReadableException.class, MethodArgumentNotValidException.class,
      HttpRequestMethodNotSupportedException.class})
  public ResponseEntity<Object> badRequest(HttpServletRequest req, Exception exception) {
    // ...
  }
}

আপনি এই পোস্টটি চেক করতে পারেন


6
আমি এই কোডটি আগে দেখেছি এবং এটি প্রয়োগের পরে, ক্লাসটি নিয়ামক অনুরোধকরণ পদ্ধতিতে উত্থাপিত ব্যতিক্রমগুলি ধরেছিল। এটি এখনও 404 ত্রুটিগুলি ধরতে পারে না, যা রিসোর্সএইচটিপিআরকুয়েস্টহ্যান্ডলিআরডেলরেক্সেস্ট পদ্ধতিতে পরিচালিত হচ্ছে, অথবা, যদি ডিপপ্যাচারসर्ভালেট.নো হ্যান্ডলারফাউন্ডে @EnableWebMvc টিকা ব্যবহার করা হয়। আমরা 404s সহ যে কোনও ত্রুটি হ্যান্ডেল করতে চাই, তবে স্প্রিং বুটের সর্বশেষতম সংস্করণটি কীভাবে এটি করা যায় তা অবিশ্বাস্যরকম মনে হয়।
ogradyjd

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

নিয়ামক পরামর্শ একটি ভাল পদ্ধতির, কিন্তু সর্বদা মনে রাখবেন যে ব্যতিক্রমগুলি ব্যতিক্রমী ক্ষেত্রে তাদের অবশ্যই প্রবাহের অংশ নয়!
জর্জেটোভার

17

যদিও এটি একটি পুরানো প্রশ্ন, আমি এই সম্পর্কে আমার মতামত জানাতে চাই। আমি আশা করি, এটি আপনার কারও পক্ষে সহায়ক হবে।

আমি বর্তমানে একটি REST এপিআই তৈরি করছি যা স্প্রিং বুটটি 1.5.2.REREASE এর সাথে স্প্রিং ফ্রেমওয়ার্ক 4.3.7.RELEASE ব্যবহার করে A আমি জাভা কনফিগার পদ্ধতি ব্যবহার করি (এক্সএমএল কনফিগারেশনের বিপরীতে)। এছাড়াও, আমার প্রকল্পটি @RestControllerAdviceটীকাটি ব্যবহার করে একটি বৈশ্বিক ব্যতিক্রম হ্যান্ডলিং প্রক্রিয়া ব্যবহার করে (পরে নীচে দেখুন)।

আমার প্রকল্পটি আপনার মতো একই প্রয়োজনীয়তা রয়েছে: আমি চাই যে আমার আরএসটি HTTP 404 Not Foundএপিআই ক্লায়েন্টের কাছে এইচটিটিপি প্রতিক্রিয়াতে জেএসওএন পেওলোড সহ একটি জেএসএন পেওলোডের সাথে ফিরে আসুক যখন এটি উপস্থিত নেই এমন কোনও ইউআরএলকে অনুরোধ প্রেরণের চেষ্টা করে। আমার ক্ষেত্রে, জেএসএন পে-লোডটি এর মতো দেখাচ্ছে (যা স্প্রিং বুটের ডিফল্ট, বিটিডব্লিউ থেকে স্পষ্টভাবে পৃথক রয়েছে):

{
    "code": 1000,
    "message": "No handler found for your request.",
    "timestamp": "2017-11-20T02:40:57.628Z"
}

আমি শেষ পর্যন্ত এটি কাজ করে। সংক্ষিপ্তভাবে আপনার যা করতে হবে তা এখানে গুরুত্বপূর্ণ:

  • নিশ্চিত হয়ে নিন যে NoHandlerFoundExceptionএপিআই ক্লায়েন্টরা ইউআরএলএস কল করে যার জন্য কোনও হ্যান্ডলার পদ্ধতি বিদ্যমান নেই (নীচের পদক্ষেপ 1 দেখুন) the
  • একটি কাস্টম ত্রুটি শ্রেণি তৈরি করুন (আমার ক্ষেত্রে ApiError) যা এপিআই ক্লায়েন্টকে ফিরিয়ে আনতে হবে এমন সমস্ত ডেটা রয়েছে (পদক্ষেপ 2 দেখুন)।
  • একটি ব্যতিক্রম হ্যান্ডলার তৈরি করুন যা এর উপর প্রতিক্রিয়া জানায় NoHandlerFoundException এবং API ক্লায়েন্টকে যথাযথ ত্রুটি বার্তা দেয় (পদক্ষেপ 3 দেখুন)।
  • এটির জন্য একটি পরীক্ষা লিখুন এবং নিশ্চিত হয়ে নিন, এটি কার্যকর হয় (4 ধাপ দেখুন)।

ঠিক আছে, এখন বিবরণে:

পদক্ষেপ 1: অ্যাপ্লিকেশন.প্রেটিটি কনফিগার করুন

প্রকল্পের application.propertiesফাইলে আমাকে নিম্নলিখিত দুটি কনফিগারেশন সেটিংস যুক্ত করতে হয়েছিল :

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

এটি নিশ্চিত করে যে, কোনও NoHandlerFoundExceptionক্ষেত্রে কোনও ক্লায়েন্ট কোনও ইউআরএল অ্যাক্সেস করার চেষ্টা করে যার জন্য কোনও নিয়ন্ত্রণকারী পদ্ধতি উপস্থিত নেই যা অনুরোধটি পরিচালনা করতে সক্ষম হবে the

পদক্ষেপ 2: এপিআই ত্রুটিগুলির জন্য একটি শ্রেণি তৈরি করুন

ইউজেন পারাশাইভের ব্লগে এই নিবন্ধে পরামর্শ মতো একটি শ্রেণি তৈরি করেছি । এই শ্রেণিটি একটি API ত্রুটি উপস্থাপন করে। এই তথ্যটি কোনও ত্রুটির ক্ষেত্রে এইচটিটিপি প্রতিক্রিয়া সংস্থায় ক্লায়েন্টকে প্রেরণ করা হয়।

public class ApiError {

    private int code;
    private String message;
    private Instant timestamp;

    public ApiError(int code, String message) {
        this.code = code;
        this.message = message;
        this.timestamp = Instant.now();
    }

    public ApiError(int code, String message, Instant timestamp) {
        this.code = code;
        this.message = message;
        this.timestamp = timestamp;
    }

    // Getters and setters here...
}

পদক্ষেপ 3: একটি গ্লোবাল ব্যতিক্রম হ্যান্ডলার তৈরি / কনফিগার করুন

আমি ব্যতিক্রমগুলি হ্যান্ডেল করতে নিম্নলিখিত ক্লাসটি ব্যবহার করি (সরলতার জন্য, আমি আমদানির বিবৃতি, লগিং কোড এবং কিছু অন্যান্য, কোডের অ-প্রাসঙ্গিক টুকরো সরিয়ে ফেলেছি):

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ApiError noHandlerFoundException(
            NoHandlerFoundException ex) {

        int code = 1000;
        String message = "No handler found for your request.";
        return new ApiError(code, message);
    }

    // More exception handlers here ...
}

পদক্ষেপ 4: একটি পরীক্ষা লিখুন

আমি নিশ্চিত করতে চাই, এপিআই সর্বদা কলিং ক্লায়েন্টকে সঠিক ত্রুটি বার্তা প্রদান করে, এমনকি ব্যর্থতার ক্ষেত্রেও। সুতরাং, আমি এই মত একটি পরীক্ষা লিখেছি:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SprintBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@ActiveProfiles("dev")
public class GlobalExceptionHandlerIntegrationTest {

    public static final String ISO8601_DATE_REGEX =
        "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$";

    @Autowired
    private MockMvc mockMvc;

    @Test
    @WithMockUser(roles = "DEVICE_SCAN_HOSTS")
    public void invalidUrl_returnsHttp404() throws Exception {
        RequestBuilder requestBuilder = getGetRequestBuilder("/does-not-exist");
        mockMvc.perform(requestBuilder)
            .andExpect(status().isNotFound())
            .andExpect(jsonPath("$.code", is(1000)))
            .andExpect(jsonPath("$.message", is("No handler found for your request.")))
            .andExpect(jsonPath("$.timestamp", RegexMatcher.matchesRegex(ISO8601_DATE_REGEX)));
    }

    private RequestBuilder getGetRequestBuilder(String url) {
        return MockMvcRequestBuilders
            .get(url)
            .accept(MediaType.APPLICATION_JSON);
    }

@ActiveProfiles("dev")টীকা দূরে রাখা যেতে পারে। আমি বিভিন্ন প্রোফাইলের সাথে কাজ করার সময়ই এটি ব্যবহার করি। RegexMatcherএকটি কাস্টম হয় Hamcrest মিলকারীর আমি ভাল হাতল টাইমস্ট্যাম্প ক্ষেত্র ব্যবহার করুন। এখানে কোডটি (আমি এটি এখানে খুঁজে পেয়েছি ):

public class RegexMatcher extends TypeSafeMatcher<String> {

    private final String regex;

    public RegexMatcher(final String regex) {
        this.regex = regex;
    }

    @Override
    public void describeTo(final Description description) {
        description.appendText("matches regular expression=`" + regex + "`");
    }

    @Override
    public boolean matchesSafely(final String string) {
        return string.matches(regex);
    }

    // Matcher method you can call on this matcher class
    public static RegexMatcher matchesRegex(final String string) {
        return new RegexMatcher(regex);
    }
}

আমার পক্ষ থেকে আরও কিছু নোট:

  • স্ট্যাকওভারফ্লোতে অন্য অনেক পোস্টে লোকেরা @EnableWebMvcটীকাটি সেট করার পরামর্শ দেয় । আমার ক্ষেত্রে এটি প্রয়োজনীয় ছিল না।
  • এই পদ্ধতির মকএমভিসি (উপরে পরীক্ষা দেখুন) এর সাথে ভাল কাজ করে।

এটি আমার জন্য সমস্যার সমাধান। কেবল যোগ করার জন্য, আমি @ রেস্টকন্ট্রোলার অ্যাডভাইস টিকাটি অনুপস্থিত ছিলাম তাই আমি যোগ করেছি @ কন্ট্রোলার অ্যাডওয়াইস টীকাটি যাতে এটি সমস্ত পরিচালনা করে এবং সেই কৌশলটি করে।
PGMacDesign

13

এই কোড সম্পর্কে কি? আমি 404 ত্রুটিগুলি ধরার জন্য ফ্যালব্যাক অনুরোধ ম্যাপিং ব্যবহার করি।

@Controller
@ControllerAdvice
public class ExceptionHandlerController {

    @ExceptionHandler(Exception.class)
    public ModelAndView exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception ex) {
        //If exception has a ResponseStatus annotation then use its response code
        ResponseStatus responseStatusAnnotation = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);

        return buildModelAndViewErrorPage(request, response, ex, responseStatusAnnotation != null ? responseStatusAnnotation.value() : HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @RequestMapping("*")
    public ModelAndView fallbackHandler(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return buildModelAndViewErrorPage(request, response, null, HttpStatus.NOT_FOUND);
    }

    private ModelAndView buildModelAndViewErrorPage(HttpServletRequest request, HttpServletResponse response, Exception ex, HttpStatus httpStatus) {
        response.setStatus(httpStatus.value());

        ModelAndView mav = new ModelAndView("error.html");
        if (ex != null) {
            mav.addObject("title", ex);
        }
        mav.addObject("content", request.getRequestURL());
        return mav;
    }

}

6

ডিফল্টরূপে স্প্রিং বুট ত্রুটির বিশদ সহ জসনকে দেয়।

curl -v localhost:8080/greet | json_pp
[...]
< HTTP/1.1 400 Bad Request
[...]
{
   "timestamp" : 1413313361387,
   "exception" : "org.springframework.web.bind.MissingServletRequestParameterException",
   "status" : 400,
   "error" : "Bad Request",
   "path" : "/greet",
   "message" : "Required String parameter 'name' is not present"
}

এটি সমস্ত ধরণের অনুরোধ ম্যাপিং ত্রুটির জন্যও কাজ করে। এই নিবন্ধটি দেখুন http://www.jayway.com/2014/10/19/spring-boot-error-responses/

আপনি যদি তৈরি করতে চান তবে এটি NoSQL এ লগ করুন। আপনি @ কন্ট্রোলার অ্যাডভাইস তৈরি করতে পারেন যেখানে আপনি এটি লগইন করবেন এবং তারপরে ব্যতিক্রমটি আবার ছুঁড়ে ফেলতে পারেন। ডকুমেন্টেশনের উদাহরণ রয়েছে https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc


যখন অস্তিত্বহীন ম্যাপিংয়ের জন্য অনুরোধটি পাওয়া যায় তখন আপনি ব্যতিক্রম না ছড়িয়ে এমভিসির সাথে পুনঃনির্দেশ করা ডিফল্ট ডিসপ্যাচার সার্ভেলিটিকে হার্ডকোড করা হয় - যদি না আপনি উপরের পোস্টে যেমন পতাকাটি সেট না করেন।
ogradyjd

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

6

ক্রস-কাটিয়া উদ্বেগ সমাধানের মাধ্যমে রেস্টফলএপির সাথে ব্যতিক্রম পরিচালনা করতে রিস্টকন্ট্রোলার অ্যাডভাইস হ'ল স্প্রিং ফ্রেমওয়ার্ক ৪.৩ এর একটি নতুন বৈশিষ্ট্য:

 package com.khan.vaquar.exception;

import javax.servlet.http.HttpServletRequest;

import org.owasp.esapi.errors.IntrusionException;
import org.owasp.esapi.errors.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.khan.vaquar.domain.ErrorResponse;

/**
 * Handles exceptions raised through requests to spring controllers.
 **/
@RestControllerAdvice
public class RestExceptionHandler {

    private static final String TOKEN_ID = "tokenId";

    private static final Logger log = LoggerFactory.getLogger(RestExceptionHandler.class);

    /**
     * Handles InstructionExceptions from the rest controller.
     * 
     * @param e IntrusionException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IntrusionException.class)
    public ErrorResponse handleIntrusionException(HttpServletRequest request, IntrusionException e) {       
        log.warn(e.getLogMessage(), e);
        return this.handleValidationException(request, new ValidationException(e.getUserMessage(), e.getLogMessage()));
    }

    /**
     * Handles ValidationExceptions from the rest controller.
     * 
     * @param e ValidationException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = ValidationException.class)
    public ErrorResponse handleValidationException(HttpServletRequest request, ValidationException e) {     
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);

        if (e.getUserMessage().contains("Token ID")) {
            tokenId = "<OMITTED>";
        }

        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(),
                                    e.getUserMessage());
    }

    /**
     * Handles JsonProcessingExceptions from the rest controller.
     * 
     * @param e JsonProcessingException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = JsonProcessingException.class)
    public ErrorResponse handleJsonProcessingException(HttpServletRequest request, JsonProcessingException e) {     
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(),
                                    e.getOriginalMessage());
    }

    /**
     * Handles IllegalArgumentExceptions from the rest controller.
     * 
     * @param e IllegalArgumentException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IllegalArgumentException.class)
    public ErrorResponse handleIllegalArgumentException(HttpServletRequest request, IllegalArgumentException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(), 
                                    e.getMessage());
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = UnsupportedOperationException.class)
    public ErrorResponse handleUnsupportedOperationException(HttpServletRequest request, UnsupportedOperationException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(), 
                                    e.getMessage());
    }

    /**
     * Handles MissingServletRequestParameterExceptions from the rest controller.
     * 
     * @param e MissingServletRequestParameterException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public ErrorResponse handleMissingServletRequestParameterException( HttpServletRequest request, 
                                                                        MissingServletRequestParameterException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(), 
                                    e.getMessage());
    }

    /**
     * Handles NoHandlerFoundExceptions from the rest controller.
     * 
     * @param e NoHandlerFoundException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(value = NoHandlerFoundException.class)
    public ErrorResponse handleNoHandlerFoundException(HttpServletRequest request, NoHandlerFoundException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.NOT_FOUND.value(), 
                                    e.getClass().getSimpleName(), 
                                    "The resource " + e.getRequestURL() + " is unavailable");
    }

    /**
     * Handles all remaining exceptions from the rest controller.
     * 
     * This acts as a catch-all for any exceptions not handled by previous exception handlers.
     * 
     * @param e Exception
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = Exception.class)
    public ErrorResponse handleException(HttpServletRequest request, Exception e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.error(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.INTERNAL_SERVER_ERROR.value(), 
                                    e.getClass().getSimpleName(), 
                                    "An internal error occurred");
    }   

}

3

আরআরইএসটি নিয়ন্ত্রণকারীদের জন্য, আমি ব্যবহারের পরামর্শ দেব Zalando Problem Spring Web

https://github.com/zalando/problem-spring-web

যদি স্প্রিং বুটটি কিছু স্বতঃ-কনফিগারেশন এম্বেড করার লক্ষ্যে থাকে তবে এই পাঠাগারটি ব্যতিক্রম হ্যান্ডলিংয়ের জন্য আরও বেশি কিছু করে। আপনাকে কেবল নির্ভরতা যুক্ত করতে হবে:

<dependency>
    <groupId>org.zalando</groupId>
    <artifactId>problem-spring-web</artifactId>
    <version>LATEST</version>
</dependency>

এবং তারপরে আপনার ব্যতিক্রমগুলির জন্য এক বা একাধিক পরামর্শের বৈশিষ্ট্যগুলি সংজ্ঞায়িত করুন (বা ডিফল্ট হিসাবে প্রদত্ত সেগুলি ব্যবহার করুন)

public interface NotAcceptableAdviceTrait extends AdviceTrait {

    @ExceptionHandler
    default ResponseEntity<Problem> handleMediaTypeNotAcceptable(
            final HttpMediaTypeNotAcceptableException exception,
            final NativeWebRequest request) {
        return Responses.create(Status.NOT_ACCEPTABLE, exception, request);
    }

}

তারপরে আপনি ব্যতিক্রম হ্যান্ডলিংয়ের জন্য নিয়ামক পরামর্শটি হিসাবে সংজ্ঞায়িত করতে পারেন:

@ControllerAdvice
class ExceptionHandling implements MethodNotAllowedAdviceTrait, NotAcceptableAdviceTrait {

}

2

HTTP স্থিতি কোড অনুযায়ী প্রতিক্রিয়া জানাতে চান এমন লোকদের জন্য, আপনি উপায়টি ব্যবহার করতে পারেন ErrorController:

@Controller
public class CustomErrorController extends BasicErrorController {

    public CustomErrorController(ServerProperties serverProperties) {
        super(new DefaultErrorAttributes(), serverProperties.getError());
    }

    @Override
    public ResponseEntity error(HttpServletRequest request) {
        HttpStatus status = getStatus(request);
        if (status.equals(HttpStatus.INTERNAL_SERVER_ERROR)){
            return ResponseEntity.status(status).body(ResponseBean.SERVER_ERROR);
        }else if (status.equals(HttpStatus.BAD_REQUEST)){
            return ResponseEntity.status(status).body(ResponseBean.BAD_REQUEST);
        }
        return super.error(request);
    }
}

ResponseBeanএখানে প্রতিক্রিয়ার জন্য আমার কাস্টম pojo হয়।


0

সঙ্গে সমাধান dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);এবং @EnableWebMvc @ControllerAdvice স্প্রিং বুট 1.3.1 সঙ্গে আমার জন্য কাজ, যখন 1.2.7 কাজ করা হয় নি

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