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.

NA.
sumber

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(); 
        }
    }
}
axtavt
sumber
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

matt b
sumber
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);

    ...

  }
michal.kreuzman
sumber
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 )

Lu55
sumber
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.


sumber
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);
        }
    }
}
Muntah
sumber
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); 
    }

}
pengguna1121883
sumber
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.

mmatczuk
sumber
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.

pilot
sumber
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.

AbdusSalam
sumber
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)

Rajith Delantha
sumber
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;
        }
}
Atish Narlawar
sumber
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);
    }
}
Jason
sumber