Trigger 404 di pengontrol Spring-MVC?


193

Bagaimana cara mendapatkan musim semi pengontrol 3.0 untuk memicu 404?

Saya memiliki pengontrol dengan @RequestMapping(value = "/**", method = RequestMethod.GET)dan untuk beberapa URL mengakses pengontrol, saya ingin wadah muncul dengan 404.

Jawaban:


326

Sejak Spring 3.0 Anda juga dapat melemparkan Pengecualian yang dinyatakan dengan @ResponseStatusanotasi:

@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
Menarik. Bisakah Anda menentukan HttpStatus mana yang akan digunakan di situs melempar (yaitu tidak dikompilasi ke dalam kelas Exception)?
matt b

1
@ mattb: Saya pikir intinya @ResponseStatusadalah bahwa Anda mendefinisikan sejumlah besar kelas pengecualian yang diketik dengan baik, masing-masing dengan kelasnya sendiri @ResponseStatus. Dengan begitu, Anda memisahkan kode pengontrol dari detail kode status HTTP.
skaffman

9
Apakah ini dapat diperpanjang untuk mendukung pengembalian badan yang berisi deskripsi lebih lanjut tentang kesalahan?
Tom

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

6
Jika Anda menggunakan pengecualian ResourceNotFound ini hanya untuk kontrol aliran, maka mungkin ide yang baik untuk menimpanya ResourceNotFound.fillInStackTrace()dengan implementasi kosong.
Ralph


36

Tulis ulang tanda tangan metode Anda sehingga diterima HttpServletResponse sebagai parameter, sehingga Anda dapat meneleponsetStatus(int) .

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


8
Ini adalah satu-satunya jawaban yang benar jika seseorang mencari cara untuk memberi tahu pemohon http bahwa mereka membuat kesalahan sementara tidak membanjiri tim prod ops dengan banyak pengecualian yang tidak dapat mereka perbaiki.
Alex R

4
setStatus(int)javadoc menyatakan sebagai berikut: Jika metode ini digunakan untuk mengatur kode kesalahan, maka mekanisme halaman kesalahan penampung tidak akan dipicu. Jika ada kesalahan dan penelepon ingin meminta halaman kesalahan yang ditentukan dalam aplikasi web, maka sendErrorharus digunakan sebagai gantinya.
Philippe Gioseffi

@AlexR Pengecualian yang ditangani tidak boleh membanjiri tim ops. Jika ya, penebangan sedang dilakukan secara tidak benar.
Raedwald

25

Saya ingin menyebutkan bahwa ada pengecualian (tidak hanya) untuk 404 secara default disediakan oleh Spring. Lihat dokumentasi Spring untuk detailnya. Jadi, jika Anda tidak membutuhkan pengecualian Anda sendiri, Anda bisa melakukan ini:

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

    ...

  }

11
Tampaknya ini dimaksudkan untuk kasus tertentu - ketika Spring tidak dapat menemukan pawang. Kasus yang dimaksud adalah ketika Spring dapat menemukan penangan, tetapi pengguna ingin mengembalikan 404 karena alasan lain.
Roy Truelove

2
Saya menggunakannya ketika pemetaan ulr saya untuk metode handler dinamis. Ketika entitas tidak ada berdasarkan @PathVariabletidak ada penanganan permintaan dari sudut pandang saya. Apakah menurut Anda lebih baik / bersih menggunakan Pengecualian Anda sendiri yang dijelaskan dengan @ResponseStatus(value = HttpStatus.NOT_FOUND) ?
michal.kreuzman

1
Dalam kasus Anda kedengarannya baik, tetapi saya tidak tahu bahwa saya akan merekomendasikan pengecualian yang ditemukan di tautan yang Anda berikan untuk menangani semua kasus di mana pengecualian diperlukan - terkadang Anda harus membuatnya sendiri.
Roy Truelove

Nah, Spring memberikan satu pengecualian dan satu hanya untuk 404. Mereka seharusnya menamainya 404Exception atau membuat satu. Tapi seperti sekarang, saya pikir tidak apa-apa untuk melempar ini setiap kali Anda membutuhkan 404.
autra

Nah, secara teknis tidak masalah - Anda akan mengirim 404 tajuk status. Tetapi pesan kesalahan otomatis - konten respons - adalah "Tidak ada metode penanganan permintaan dengan nama ..." yang mungkin bukan sesuatu yang ingin Anda tampilkan kepada pengguna.
Olli

24

Sejak Spring 3.0.2 Anda dapat mengembalikan ResponseEntity <T> sebagai hasil dari metode pengontrol:

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

(ResponseEntity <T> lebih fleksibel daripada penjelasan @ResponseBody - lihat pertanyaan lain )


2
fleksibel tentu saja tetapi mengalahkan manfaat pemrograman deklaratif
rohanagarwal

3
Jika Anda menggunakan Sentry atau sejenisnya di PROD, dan tidak ingin mengirim spam dengan kesalahan yang bukan kesalahan aktual, solusi ini jauh lebih baik dibandingkan dengan yang menggunakan pengecualian untuk situasi yang tidak luar biasa ini.
Tobias Hermann

Jangan lupa bagaimana mengisi tubuh (dengan objek Anda yang sebenarnya). generik "Objek" contoh: Objek returnItemBody = Objek baru (); return ResponseEntity.status (HttpStatus.OK) .body (returnItemBody);
granadaCoder

16

Anda dapat menggunakan @ControllerAdvice untuk menangani Pengecualian Anda, Perilaku default kelas penjelasan @ControllerAdvice akan membantu semua Pengendali yang dikenal.

sehingga akan dipanggil ketika Kontroler yang Anda miliki memiliki 404 kesalahan.

seperti berikut ini:

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

dan petakan kesalahan respons 404 ini di web.xml Anda, seperti berikut ini:

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

Semoga Itu Membantu.


2
Anda telah memetakan pengecualian tipe Exception (dan subclasses) dengan kode status 404. Apakah Anda pernah berpikir ada kesalahan server internal? Bagaimana Anda berencana menangani mereka yang ada di GlobalControllerExceptionHandler Anda?
rohanagarwal

Ini TIDAK berfungsi untuk pengendali REST, mengembalikan respons kosong.
rustyx

10

Jika metode pengontrol Anda untuk sesuatu seperti penanganan file maka ResponseEntitysangat berguna:

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

9

Meskipun jawaban yang ditandai sudah benar, ada cara untuk mencapainya tanpa kecuali. Layanan ini mengembalikan Optional<T>objek yang dicari dan ini dipetakan ke HttpStatus.OKjika ditemukan dan ke 404 jika kosong.

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

}

Saya suka pendekatan ini secara umum, tetapi menggunakan Opsional terkadang menjadi anti-pola. Dan menjadi rumit ketika mengembalikan koleksi.
jfzr

7

Saya sarankan untuk melempar HttpClientErrorException , seperti ini

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

Anda harus ingat bahwa ini dapat dilakukan hanya sebelum sesuatu ditulis ke servlet output stream.


4
Pengecualian itu dilontarkan oleh klien HTTP Spring. Spring MVC tampaknya tidak mengenali pengecualian itu. Apa versi Spring yang Anda gunakan? Apakah Anda mendapatkan 404 dengan pengecualian itu?
Eduardo

1
Ini menyebabkan Spring Boot kembali: 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

Ini merupakan pengecualian untuk klien HTTP, bukan untuk controller. Jadi menggunakannya dalam konteks yang ditentukan tidak pantas.
Alexey

3

Ini agak terlambat, tetapi jika Anda menggunakan Spring Data REST maka sudah ada org.springframework.data.rest.webmvc.ResourceNotFoundException Ini juga menggunakan @ResponseStatusanotasi. Tidak perlu lagi membuat pengecualian runtime khusus.


2

Juga jika Anda ingin mengembalikan status 404 dari pengontrol Anda, Anda hanya perlu melakukan ini

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

Dengan melakukan ini, Anda akan menerima kesalahan 404 jika Anda ingin mengembalikan 404 dari controller Anda.


0

Cukup Anda dapat menggunakan web.xml untuk menambahkan kode kesalahan dan 404 halaman kesalahan. Tetapi pastikan halaman kesalahan 404 tidak boleh ditemukan di bawah WEB-INF.

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

Ini adalah cara paling sederhana untuk melakukannya tetapi ini memiliki beberapa keterbatasan. Misalkan jika Anda ingin menambahkan gaya yang sama untuk halaman ini yang Anda tambahkan halaman lain. Dengan cara ini Anda tidak bisa melakukan itu. Anda harus menggunakan@ResponseStatus(value = HttpStatus.NOT_FOUND)


Ini cara untuk melakukannya tetapi pertimbangkan dengan itu HttpServletResponse#sendError(HttpServletResponse.SC_NOT_FOUND); return null;dari kode pengontrol. Sekarang dari luar responnya tidak berbeda dengan 404 normal yang tidak mengenai controller apa pun.
Darryl Miles

ini tidak memicu 404, itu hanya menangani itu jika terjadi
Alex R

0

Konfigurasikan web.xml dengan pengaturan

<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>

Buat pengontrol baru

   /**
     * 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

Karena itu selalu baik untuk memiliki setidaknya sepuluh cara melakukan hal yang sama:

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);
    }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.