Kích hoạt 404 trong bộ điều khiển Spring-MVC?


193

Làm cách nào để có bộ điều khiển Spring 3.0 để kích hoạt 404?

Tôi có một bộ điều khiển với @RequestMapping(value = "/**", method = RequestMethod.GET)và đối với một số URL truy cập vào bộ điều khiển, tôi muốn bộ chứa đi kèm với 404.

Câu trả lời:


326

Kể từ Mùa xuân 3.0, bạn cũng có thể ném Ngoại lệ được khai báo với @ResponseStatuschú thích:

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
    ...
}

@Controller
public class SomeController {
    @RequestMapping.....
    public void handleCall() {
        if (isFound()) {
            // whatever
        }
        else {
            throw new ResourceNotFoundException(); 
        }
    }
}

2
Hấp dẫn. Bạn có thể chỉ định sử dụng httpStatus nào tại trang web ném (tức là không được biên dịch vào lớp Exception) không?
matt b

1
@mattb: Tôi nghĩ rằng vấn đề @ResponseStatuslà bạn xác định một nhóm các lớp ngoại lệ được đánh máy mạnh, được đặt tên tốt, mỗi lớp có một lớp riêng @ResponseStatus. Bằng cách đó, bạn tách mã điều khiển của mình khỏi chi tiết mã trạng thái HTTP.
skaffman

9
Điều này có thể được mở rộng để hỗ trợ trả lại một phần chứa mô tả thêm về lỗi không?
Tom

7
@ Tom:@ResponseStatus(value = HttpStatus.NOT_FOUND, reason="Your reason")
Nailgun

6
Nếu bạn chỉ sử dụng ngoại lệ ResourceNotFound này để kiểm soát luồng, thì có lẽ nên ghi đè ResourceNotFound.fillInStackTrace()bằng một triển khai trống.
Ralph


36

Viết lại chữ ký phương thức của bạn để nó chấp nhận HttpServletResponsenhư một tham số, để bạn có thể gọi setStatus(int)nó.

http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-argument


8
Đây là câu trả lời đúng duy nhất nếu ai đó đang tìm cách nói với người yêu cầu http rằng họ đã mắc lỗi trong khi không làm ngập đội ngũ prod ops với một loạt các trường hợp ngoại lệ mà họ không thể sửa chữa.
Alex R

4
setStatus(int)javadoc tuyên bố như sau: Nếu phương pháp này được sử dụng để đặt mã lỗi, thì cơ chế trang lỗi của bộ chứa sẽ không được kích hoạt. Nếu có lỗi và người gọi muốn gọi một trang lỗi được xác định trong ứng dụng web, thì sendErrorphải sử dụng thay thế.
Philippe Gioseffi

@AlexR Các ngoại lệ được xử lý không nên tràn ngập nhóm ops. Nếu có, đăng nhập đang được thực hiện không chính xác.
Raedwald

25

Tôi muốn đề cập rằng có ngoại lệ (không chỉ) cho 404 theo mặc định được cung cấp bởi Spring. Xem tài liệu mùa xuân để biết chi tiết. Vì vậy, nếu bạn không cần ngoại lệ của riêng mình, bạn chỉ cần làm điều này:

 @RequestMapping(value = "/**", method = RequestMethod.GET)
 public ModelAndView show() throws NoSuchRequestHandlingMethodException {
    if(something == null)
         throw new NoSuchRequestHandlingMethodException("show", YourClass.class);

    ...

  }

11
Điều này có nghĩa là cho một trường hợp cụ thể - khi Spring không thể tìm thấy một trình xử lý. Trường hợp trong câu hỏi là khi Spring có thể tìm thấy một trình xử lý, nhưng người dùng muốn trả lại 404 vì một lý do khác.
Roy Truelove

2
Tôi đang sử dụng nó khi ánh xạ ulr của tôi cho phương thức xử lý là động. Khi thực thể không tồn tại dựa trên @PathVariablekhông có xử lý yêu cầu theo quan điểm của tôi. Bạn có nghĩ rằng sẽ tốt hơn / sạch hơn khi sử dụng chú thích Ngoại lệ của riêng bạn @ResponseStatus(value = HttpStatus.NOT_FOUND) không?
michal.kreuzman

1
Trong trường hợp của bạn nghe có vẻ ổn, nhưng tôi không biết rằng tôi khuyên bạn nên đề xuất các ngoại lệ được tìm thấy trong liên kết bạn cung cấp để xử lý tất cả các trường hợp cần ngoại lệ - đôi khi bạn nên tự làm.
Roy Truelove

Chà, Spring đã cung cấp một ngoại lệ và một ngoại lệ duy nhất cho 404. Họ nên đặt tên là 404Exception hoặc tạo một ngoại lệ. Nhưng như bây giờ, tôi nghĩ sẽ ổn khi ném cái này bất cứ khi nào bạn cần 404.
autra

Chà, về mặt kỹ thuật thì không sao - bạn sẽ gửi tiêu đề trạng thái 404. Nhưng thông báo lỗi tự động - nội dung phản hồi - là "Không có phương thức xử lý yêu cầu có tên ..." có lẽ không phải là thứ bạn muốn hiển thị cho người dùng.
Olli

24

Kể từ Spring 3.0.2, bạn có thể trả về FeedbackEntity <T> do phương thức của bộ điều khiển:

@RequestMapping.....
public ResponseEntity<Object> handleCall() {
    if (isFound()) {
        // do what you want
        return new ResponseEntity<>(HttpStatus.OK);
    }
    else {
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
}

(FeedbackEntity <T> linh hoạt hơn chú thích @ResponseBody - xem câu hỏi khác )


2
linh hoạt của khóa học nhưng đánh bại những lợi ích của lập trình khai báo
rohanagarwal

3
Nếu bạn đang sử dụng Sentry hoặc tương tự trong SẢN PHẨM và không muốn spam nó với các lỗi không có lỗi thực tế, thì giải pháp này tốt hơn nhiều so với cách sử dụng ngoại lệ cho tình huống không đặc biệt này.
Tobias Hermann

Đừng quên làm thế nào để đưa vào cơ thể (với đối tượng thực tế của bạn). ví dụ chung về "Đối tượng": Object returnItemBody = new Object (); return FeedbackEntity.status (HttpStatus.OK) .body (returnItemBody);
granadaCoder

16

bạn có thể sử dụng @ControllAdvice để xử lý Ngoại lệ của mình, Hành vi mặc định mà lớp chú thích @ControllAdvice sẽ hỗ trợ tất cả các Bộ điều khiển đã biết.

vì vậy nó sẽ được gọi khi bất kỳ Trình điều khiển nào bạn gặp lỗi 404.

như sau:

@ControllerAdvice
class GlobalControllerExceptionHandler {
    @ResponseStatus(HttpStatus.NOT_FOUND)  // 404
    @ExceptionHandler(Exception.class)
    public void handleNoTFound() {
        // Nothing to do
    }
}

và ánh xạ lỗi phản hồi 404 này trong tệp webDB của bạn, như sau:

<error-page>
        <error-code>404</error-code>
        <location>/Error404.html</location>
</error-page>

Mong rằng sẽ giúp.


2
bạn đã ánh xạ các ngoại lệ của loại Ngoại lệ (và các lớp con) với mã trạng thái 404. Bạn đã bao giờ nghĩ rằng có lỗi máy chủ nội bộ? Làm thế nào để bạn có kế hoạch để xử lý những người trong GlobalControllExceptionHandler của bạn?
rohanagarwal

Điều này KHÔNG làm việc cho các bộ điều khiển REST, trả về một phản hồi trống.
rustyx

10

Nếu phương thức điều khiển của bạn dành cho một cái gì đó như xử lý tệp thì ResponseEntityrất tiện dụng:

@Controller
public class SomeController {
    @RequestMapping.....
    public ResponseEntity handleCall() {
        if (isFound()) {
            return new ResponseEntity(...);
        }
        else {
            return new ResponseEntity(404);
        }
    }
}

9

Trong khi câu trả lời được đánh dấu là chính xác, có một cách để đạt được điều này mà không có ngoại lệ. Dịch vụ đang trả về Optional<T>đối tượng được tìm kiếm và điều này được ánh xạ tới HttpStatus.OKnếu tìm thấy và thành 404 nếu trống.

@Controller
public class SomeController {

    @RequestMapping.....
    public ResponseEntity<Object> handleCall() {
        return  service.find(param).map(result -> new ResponseEntity<>(result, HttpStatus.OK))
                .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }
}

@Service
public class Service{

    public Optional<Object> find(String param){
        if(!found()){
            return Optional.empty();
        }
        ...
        return Optional.of(data); 
    }

}

Tôi thích cách tiếp cận này nói chung, nhưng sử dụng Tùy chọn đôi khi cuối cùng là một mô hình chống. Và trở nên phức tạp khi trả lại bộ sưu tập.
jfzr

7

Tôi khuyên bạn nên ném httpClientErrorException , như thế này

@RequestMapping(value = "/sample/")
public void sample() {
    if (somethingIsWrong()) {
        throw new HttpClientErrorException(HttpStatus.NOT_FOUND);
    }
}

Bạn phải nhớ rằng điều này chỉ có thể được thực hiện trước khi mọi thứ được ghi vào luồng đầu ra của servlet.


4
Ngoại lệ đó được ném bởi máy khách Spring HTTP. Spring MVC dường như không nhận ra ngoại lệ đó. Phiên bản mùa xuân nào bạn đang sử dụng? Bạn có nhận được 404 với ngoại lệ đó không?
Eduardo

1
Điều này khiến Spring Boot trở lại:Whitelabel Error Page \n .... \n There was an unexpected error (type=Internal Server Error, status=500). \n 404 This is your not found error
slim

Đây là một ngoại lệ cho máy khách HTTP, không phải cho bộ điều khiển. Vì vậy, sử dụng nó trong bối cảnh được chỉ định là không phù hợp.
Alexey

3

Điều này hơi muộn, nhưng nếu bạn đang sử dụng Spring Data REST thì đã có org.springframework.data.rest.webmvc.ResourceNotFoundException Nó cũng sử dụng @ResponseStatuschú thích. Không cần phải tạo một ngoại lệ thời gian chạy tùy chỉnh nữa.


2

Ngoài ra nếu bạn muốn trả về trạng thái 404 từ bộ điều khiển của mình, tất cả những gì bạn cần là làm điều này

@RequestMapping(value = "/somthing", method = RequestMethod.POST)
@ResponseBody
public HttpStatus doSomthing(@RequestBody String employeeId) {
    try{
  return HttpStatus.OK;
    } 
    catch(Exception ex){ 
  return HttpStatus.NOT_FOUND;
    }
}

Bằng cách này, bạn sẽ nhận được một lỗi 404 trong trường hợp khi bạn muốn trả về 404 từ bộ điều khiển của bạn.


0

Đơn giản là bạn có thể sử dụng web.xml để thêm mã lỗi và trang lỗi 404. Nhưng đảm bảo trang lỗi 404 không được định vị trong WEB-INF.

<error-page>
    <error-code>404</error-code>
    <location>/404.html</location>
</error-page>

Đây là cách đơn giản nhất để làm điều này nhưng điều này có một số hạn chế. Giả sử nếu bạn muốn thêm cùng một kiểu cho trang này mà bạn đã thêm các trang khác. Theo cách này, bạn không thể làm điều đó. Bạn phải sử dụng@ResponseStatus(value = HttpStatus.NOT_FOUND)


Đây là cách để làm điều đó nhưng xem xét với nó HttpServletResponse#sendError(HttpServletResponse.SC_NOT_FOUND); return null;từ mã điều khiển. Bây giờ, từ bên ngoài, phản hồi trông không khác với 404 thông thường không gặp bất kỳ bộ điều khiển nào.
Darryl Miles

điều này không kích hoạt 404, nó chỉ xử lý nếu xảy ra
Alex R

0

Định cấu hình tệp web.xml bằng cài đặt

<error-page>
    <error-code>500</error-code>
    <location>/error/500</location>
</error-page>

<error-page>
    <error-code>404</error-code>
    <location>/error/404</location>
</error-page>

Tạo bộ điều khiển mới

   /**
     * Error Controller. handles the calls for 404, 500 and 401 HTTP Status codes.
     */
    @Controller
    @RequestMapping(value = ErrorController.ERROR_URL, produces = MediaType.APPLICATION_XHTML_XML_VALUE)
    public class ErrorController {


        /**
         * The constant ERROR_URL.
         */
        public static final String ERROR_URL = "/error";


        /**
         * The constant TILE_ERROR.
         */
        public static final String TILE_ERROR = "error.page";


        /**
         * Page Not Found.
         *
         * @return Home Page
         */
        @RequestMapping(value = "/404", produces = MediaType.APPLICATION_XHTML_XML_VALUE)
        public ModelAndView notFound() {

            ModelAndView model = new ModelAndView(TILE_ERROR);
            model.addObject("message", "The page you requested could not be found. This location may not be current.");

            return model;
        }

        /**
         * Error page.
         *
         * @return the model and view
         */
        @RequestMapping(value = "/500", produces = MediaType.APPLICATION_XHTML_XML_VALUE)
        public ModelAndView errorPage() {
            ModelAndView model = new ModelAndView(TILE_ERROR);
            model.addObject("message", "The page you requested could not be found. This location may not be current, due to the recent site redesign.");

            return model;
        }
}

0

Bởi vì thật tốt khi có ít nhất mười cách để làm điều tương tự:

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class Something {
    @RequestMapping("/path")
    public ModelAndView somethingPath() {
        return new ModelAndView("/", HttpStatus.NOT_FOUND);
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.