रेल में 404 पर पुनर्निर्देशित कैसे करें?


482

मैं रेल में एक 404 पृष्ठ 'नकली' करना चाहता हूं। PHP में, मैं सिर्फ एक हेडर भेजूंगा जिसमें त्रुटि कोड होगा:

header("HTTP/1.0 404 Not Found");

यह रेल के साथ कैसे किया जाता है?

जवाबों:


1049

अपने आप को 404 रेंडर न करें, इसका कोई कारण नहीं है; रेल में यह कार्यक्षमता पहले से ही निर्मित है। यदि आप 404 पृष्ठ दिखाना चाहते हैं , तो इस तरह से एक render_404विधि (या not_foundजैसा कि मैंने इसे बुलाया था) बनाएँ ApplicationController:

def not_found
  raise ActionController::RoutingError.new('Not Found')
end

रेल भी संभालती है AbstractController::ActionNotFound, और ActiveRecord::RecordNotFoundउसी तरह।

इससे दो चीजें बेहतर होती हैं:

1) यह 'में निर्मित रेल का उपयोग करता है rescue_from 404 पेज को रेंडर करने के लिए हैंडलर , और 2) यह आपके कोड के निष्पादन में बाधा डालता है, जिससे आप अच्छे काम कर सकते हैं:

  user = User.find_by_email(params[:email]) or not_found
  user.do_something!

बदसूरत सशर्त बयान लिखने के लिए बिना।

एक बोनस के रूप में, यह परीक्षणों में संभालना भी आसान है। उदाहरण के लिए, rspec एकीकरण परीक्षण में:

# RSpec 1

lambda {
  visit '/something/you/want/to/404'
}.should raise_error(ActionController::RoutingError)

# RSpec 2+

expect {
  get '/something/you/want/to/404'
}.to raise_error(ActionController::RoutingError)

और न्यूनतम:

assert_raises(ActionController::RoutingError) do 
  get '/something/you/want/to/404'
end

या Rails से अधिक जानकारी का संदर्भ दें 404 एक नियंत्रक कार्रवाई से नहीं मिला


3
इसे स्वयं करने का एक कारण है। यदि आपका आवेदन रूट से सभी मार्गों का अपहरण कर लेता है। यह खराब डिज़ाइन है, लेकिन कभी-कभी अन-अवॉइड करता है।
13

7
यह दृष्टिकोण आपको ActiveRecord बैंग फ़ंडर्स (ढूँढें !, find_by _..., इत्यादि) आदि का भी उपयोग करने देता है, जो सभी ActiveRecord :: RecordNotFound अपवाद को बढ़ाते हैं यदि कोई रिकॉर्ड नहीं मिलता है (रेस्क्यू_रोम हैंडलर को ट्रिगर करता है)।
गजविस

2
यह मेरे लिए ५०४ नहीं, बल्कि ५०० आंतरिक सर्वर त्रुटि उठाता है। मुझे क्या याद आ रही है?
ग्लेन

3
लगता ActionController::RecordNotFoundहै कि बेहतर विकल्प है?
पीटर एर्लिच

4
कोड ने बहुत काम किया, लेकिन परीक्षण तब तक नहीं हुआ जब तक मुझे एहसास नहीं हुआ कि मैं RSpec 2 का उपयोग कर रहा हूं, जिसमें अलग-अलग वाक्यविन्यास हैं: expect { visit '/something/you/want/to/404' }.to raise_error(ActionController::RoutingError)/ stackoverflow.com/a/1722839/993890 के
rantanttb

243

HTTP 404 स्थिति

एक 404 हेडर वापस करने के लिए, बस :statusरेंडर विधि के लिए विकल्प का उपयोग करें ।

def action
  # here the code

  render :status => 404
end

यदि आप मानक 404 पृष्ठ को रेंडर करना चाहते हैं तो आप एक विधि में सुविधा को निकाल सकते हैं।

def render_404
  respond_to do |format|
    format.html { render :file => "#{Rails.root}/public/404", :layout => false, :status => :not_found }
    format.xml  { head :not_found }
    format.any  { head :not_found }
  end
end

और इसे अपनी कार्रवाई में कॉल करें

def action
  # here the code

  render_404
end

यदि आप चाहते हैं कि कार्रवाई त्रुटि पृष्ठ को प्रस्तुत करने और बंद करने के लिए हो, तो बस वापसी विवरण का उपयोग करें।

def action
  render_404 and return if params[:something].blank?

  # here the code that will never be executed
end

ActiveRecord और HTTP 404

यह भी याद रखें कि रेल कुछ ActiveRecord त्रुटियों को बचाती है, जैसे कि ActiveRecord::RecordNotFound कि 404 त्रुटि पृष्ठ प्रदर्शित करना।

इसका मतलब है कि आपको इस क्रिया को स्वयं करने की आवश्यकता नहीं है

def show
  user = User.find(params[:id])
end

User.findActiveRecord::RecordNotFoundजब उपयोगकर्ता मौजूद नहीं है तो उठाता है। यह एक बहुत शक्तिशाली विशेषता है। निम्नलिखित कोड को देखें

def show
  user = User.find_by_email(params[:email]) or raise("not found")
  # ...
end

आप चेक को रेल को सौंपकर इसे सरल बना सकते हैं। बस धमाकेदार संस्करण का उपयोग करें।

def show
  user = User.find_by_email!(params[:email])
  # ...
end

9
इस समाधान के साथ एक बड़ी समस्या है; यह अभी भी टेम्पलेट में कोड चलाएगा। इसलिए यदि आपके पास एक सरल, आरामदायक संरचना है और कोई व्यक्ति ऐसी आईडी में प्रवेश करता है जो अस्तित्व में नहीं है, तो आपका टेम्पलेट उस वस्तु की तलाश करेगा जो मौजूद नहीं है।
जूल्ट

5
जैसा कि पहले उल्लेख किया गया है, यह सही उत्तर नहीं है। स्टीवन की कोशिश करो।
पाब्लो मैराम्बियो

बेहतर अभ्यास को प्रतिबिंबित करने के लिए चयनित उत्तर को बदल दिया। टिप्पणियों के धन्यवाद मित्रों!
युवल कर्मि

1
मैंने अधिक उदाहरण और ActiveRecord के बारे में एक नोट के साथ उत्तर को अपडेट किया।
सिमोन कारलेटी

1
धमाके का संस्करण कोड निष्पादन को रोक देता है, इसलिए यह अधिक प्रभावी समाधान IMHO है।
गुई विएरा

60

स्टीवन सोरोका द्वारा प्रस्तुत नया चयनित जवाब करीब है, लेकिन पूरा नहीं है। परीक्षण खुद इस तथ्य को छुपाता है कि यह एक सच्चा 404 नहीं लौटा रहा है - यह 200 की स्थिति लौटा रहा है - "सफलता"। मूल उत्तर करीब था, लेकिन लेआउट को प्रस्तुत करने का प्रयास किया जैसे कि कोई विफलता नहीं हुई थी। यह सब कुछ ठीक करता है:

render :text => 'Not Found', :status => '404'

यहाँ मेरा एक विशिष्ट परीक्षण सेट है, जिसके लिए मुझे आरएसपीईसी और शोता माचिस का उपयोग करके 404 लौटाने की उम्मीद है:

describe "user view" do
  before do
    get :show, :id => 'nonsense'
  end

  it { should_not assign_to :user }

  it { should respond_with :not_found }
  it { should respond_with_content_type :html }

  it { should_not render_template :show }
  it { should_not render_with_layout }

  it { should_not set_the_flash }
end

इस स्वस्थ व्यामोह ने मुझे सामग्री-प्रकार के बेमेल को देखने की अनुमति दी जब बाकी सब कुछ पेचीदा लग रहा था :) मैं इन सभी तत्वों की जांच करता हूं: असाइन किए गए चर, प्रतिक्रिया कोड, प्रतिक्रिया सामग्री प्रकार, टेम्पलेट प्रदान, लेआउट प्रदान, फ्लैश संदेश।

मैं कड़ाई से html ... कभी-कभी अनुप्रयोगों पर सामग्री प्रकार की जाँच छोड़ दूँगा। सब के बाद, "एक संदेहवादी सभी दराज की जांच करता है" :)

http://dilbert.com/strips/comic/1998-01-20/

FYI करें: मैं उन चीजों के लिए परीक्षण की अनुशंसा नहीं करता, जो नियंत्रक में हो रही हैं, अर्थात "should_raise"। आप जिस चीज की परवाह करते हैं वह आउटपुट है। ऊपर दिए गए मेरे परीक्षणों ने मुझे विभिन्न समाधानों की कोशिश करने की अनुमति दी, और परीक्षण एक ही रहते हैं कि क्या समाधान एक अपवाद, विशेष प्रतिपादन आदि उठा रहा है।


3
वास्तव में इस जवाब को पसंद करते हैं, विशेष रूप से आउटपुट के परीक्षण के संबंध में और नियंत्रक में कहे जाने वाले तरीकों के बारे में नहीं ...
xentek

रेल में अंतर्निहित 404 स्थिति है render :text => 'Not Found', :status => :not_found:।
लास बंक

1
@JaimeBellmyer - मुझे यकीन है कि जब आप एक तैनात (यानी मंचन / प्रहसन) वातावरण में रहते हैं तो यह 200 वापस नहीं आता है। मैं कई अनुप्रयोगों में ऐसा करता हूं और यह स्वीकृत समाधान में वर्णित के रूप में काम करता है। शायद आप जिसका उल्लेख कर रहे हैं वह यह है कि यह 200 का रिटर्न देता है जब यह विकास में डिबग स्क्रीन को प्रस्तुत करता है जहां आपके पास संभवतः config.consider_all_requests_localआपकी environments/development.rbफ़ाइल में सही करने के लिए सेट पैरामीटर है । यदि आप कोई त्रुटि उठाते हैं, जैसा कि स्वीकृत समाधान में वर्णित है, मंचन / उत्पादन में, आपको निश्चित रूप से एक 404 मिलेगा, 200 नहीं।
जावीद जामे जूल 30'14

18

आप रेंडर फ़ाइल का उपयोग भी कर सकते हैं:

render file: "#{Rails.root}/public/404.html", layout: false, status: 404

आप लेआउट का उपयोग करना चुन सकते हैं या नहीं।

एक अन्य विकल्प इसे नियंत्रित करने के लिए अपवाद का उपयोग करना है:

raise ActiveRecord::RecordNotFound, "Record not found."

13

चयनित उत्तर रेल 3.1+ में काम नहीं करता है क्योंकि त्रुटि हैंडलर को एक मिडलवेयर में ले जाया गया था ( जीथब मुद्दा देखें) )।

यहाँ समाधान है जो मैंने पाया है कि मैं बहुत खुश हूँ।

इन ApplicationController:

  unless Rails.application.config.consider_all_requests_local
    rescue_from Exception, with: :handle_exception
  end

  def not_found
    raise ActionController::RoutingError.new('Not Found')
  end

  def handle_exception(exception=nil)
    if exception
      logger = Logger.new(STDOUT)
      logger.debug "Exception Message: #{exception.message} \n"
      logger.debug "Exception Class: #{exception.class} \n"
      logger.debug "Exception Backtrace: \n"
      logger.debug exception.backtrace.join("\n")
      if [ActionController::RoutingError, ActionController::UnknownController, ActionController::UnknownAction].include?(exception.class)
        return render_404
      else
        return render_500
      end
    end
  end

  def render_404
    respond_to do |format|
      format.html { render template: 'errors/not_found', layout: 'layouts/application', status: 404 }
      format.all { render nothing: true, status: 404 }
    end
  end

  def render_500
    respond_to do |format|
      format.html { render template: 'errors/internal_server_error', layout: 'layouts/application', status: 500 }
      format.all { render nothing: true, status: 500}
    end
  end

और इसमें application.rb:

config.after_initialize do |app|
  app.routes.append{ match '*a', :to => 'application#not_found' } unless config.consider_all_requests_local
end

और मेरे संसाधनों में (शो, एडिट, अपडेट, डिलीट):

@resource = Resource.find(params[:id]) or not_found

यह निश्चित रूप से सुधार किया जा सकता है, लेकिन कम से कम, मेरे पास कोर रेल कार्यों को ओवरराइड किए बिना not_found और internal_error के लिए अलग-अलग विचार हैं।


3
यह एक बहुत अच्छा समाधान है; हालाँकि, आपको || not_foundभाग की आवश्यकता नहीं है , बस कॉल करें find!(धमाके की सूचना दें) और यह ActiveRecord को फेंक देगा :: RecordNotFound जब संसाधन पुनर्प्राप्त नहीं किया जा सकता है। इसके अलावा, ActiveRecord :: अगर स्थिति में सरणी में RecordNotFound जोड़ें।
मेराक पोहोडा

1
मैं बचाव StandardErrorऔर नहीं Exception, सिर्फ मामले में होगा। वास्तव में मैं मानक 500 स्थैतिक पृष्ठ छोड़ दूंगा और कस्टम का उपयोग नहीं render_500करूंगा, जिसका अर्थ है कि मैं स्पष्ट रूप rescue_fromसे 404 से संबंधित त्रुटियों का सरणी
रखूंगा

7

ये आपकी मदद करेंगे ...

आवेदन नियंत्रक

class ApplicationController < ActionController::Base
  protect_from_forgery
  unless Rails.application.config.consider_all_requests_local             
    rescue_from ActionController::RoutingError, ActionController::UnknownController, ::AbstractController::ActionNotFound, ActiveRecord::RecordNotFound, with: lambda { |exception| render_error 404, exception }
  end

  private
    def render_error(status, exception)
      Rails.logger.error status.to_s + " " + exception.message.to_s
      Rails.logger.error exception.backtrace.join("\n") 
      respond_to do |format|
        format.html { render template: "errors/error_#{status}",status: status }
        format.all { render nothing: true, status: status }
      end
    end
end

त्रुटियों को नियंत्रित करता है

class ErrorsController < ApplicationController
  def error_404
    @not_found_path = params[:not_found]
  end
end

विचारों / त्रुटियों / error_404.html.haml

.site
  .services-page 
    .error-template
      %h1
        Oops!
      %h2
        404 Not Found
      .error-details
        Sorry, an error has occured, Requested page not found!
        You tried to access '#{@not_found_path}', which is not a valid page.
      .error-actions
        %a.button_simple_orange.btn.btn-primary.btn-lg{href: root_path}
          %span.glyphicon.glyphicon-home
          Take Me Home

3
<%= render file: 'public/404', status: 404, formats: [:html] %>

इसे केवल उस पृष्ठ पर जोड़ें जिसे आप 404 त्रुटि पृष्ठ पर रेंडर करना चाहते हैं और आप कर रहे हैं।


1

मैं किसी भी लॉग इन करने वाले उपयोगकर्ता के लिए एक 'सामान्य' 404 फेंकना चाहता था जो कि कोई व्यवस्थापक नहीं है, इसलिए मैंने कुछ इस तरह से रेलिंग में लिखना समाप्त कर दिया:

class AdminController < ApplicationController
  before_action :blackhole_admin

  private

  def blackhole_admin
    return if current_user.admin?

    raise ActionController::RoutingError, 'Not Found'
  rescue ActionController::RoutingError
    render file: "#{Rails.root}/public/404", layout: false, status: :not_found
  end
end


0

त्रुटि से निपटने के परीक्षण के लिए, आप कुछ इस तरह से कर सकते हैं:

feature ErrorHandling do
  before do
    Rails.application.config.consider_all_requests_local = false
    Rails.application.config.action_dispatch.show_exceptions = true
  end

  scenario 'renders not_found template' do
    visit '/blah'
    expect(page).to have_content "The page you were looking for doesn't exist."
  end
end

0

यदि आप विभिन्न तरीकों से 404s को संभालना चाहते हैं, तो उन्हें अपने नियंत्रकों में पकड़ने पर विचार करें। यह आपको विभिन्न उपयोगकर्ता समूहों द्वारा उत्पन्न 404 की संख्या को ट्रैक करने जैसी चीजों को करने की अनुमति देगा, उपयोगकर्ताओं के साथ बातचीत करने के लिए यह पता लगाने में सहायता करेगा कि क्या गलत हुआ / उपयोगकर्ता अनुभव के किस हिस्से को ए / बी परीक्षण करना चाहिए, आदि।

मैंने यहाँ ApplicationController में आधार तर्क रखा है, लेकिन इसे केवल एक नियंत्रक के लिए विशेष तर्क रखने के लिए अधिक विशिष्ट नियंत्रकों में भी रखा जा सकता है।

कारण है कि अगर मैं ENV ['RESCUE_404'] के साथ प्रयोग कर रहा हूं, तो मैं AR :: RecordNotFound के अलगाव में परीक्षण कर सकता हूं। परीक्षणों में, मैं इस ईएनवी संस्करण को झूठे में सेट कर सकता हूं, और मेरे बचाव_फ्रेम में आग नहीं लगेगी। इस तरह मैं सशर्त 404 तर्क से अलग होने का परीक्षण कर सकता हूं।

class ApplicationController < ActionController::Base

  rescue_from ActiveRecord::RecordNotFound, with: :conditional_404_redirect if ENV['RESCUE_404']

private

  def conditional_404_redirect
    track_404(@current_user)
    if @current_user.present?
      redirect_to_user_home          
    else
      redirect_to_front
    end
  end

end
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.