【问题标题】:How do I prevent my Rails controller method from returning nil to my view?如何防止我的 Rails 控制器方法返回 nil 到我的视图?
【发布时间】:2019-06-06 17:49:20
【问题描述】:

我有一个简单的应用程序,它调用 API 并返回天气数据。用户可以搜索城市并返回当前温度。但是我有一个问题,当搜索字段为空或无法识别的城市时,我收到错误 undefined method[]' for nil:NilClass`。这是我的代码:

forecasts_controller.rb

class ForecastsController < ApplicationController
  def current_weather
    @token = Rails.application.credentials.openweather_key
    @city = params[:q]
    if @city == nil
      @forecast = ""
    else
      @forecast = OpenWeatherApi.new(@city, @token).my_location_forecast
    end
  end
end

服务/open_weather_api.rb

class OpenWeatherApi
  include HTTParty
  base_uri "http://api.openweathermap.org"

  def initialize(city, appid)
    @options = { query: { q: city, APPID: appid } }
  end

  def my_location_forecast
    self.class.get("/data/2.5/weather", @options)
  end
end

current_weather.html.erb

<%= form_tag(current_weather_forecasts_path, method: :get) do %>
  <%= text_field_tag(:q) %>
  <%= submit_tag("Search") %>
<% end %><br>

<p>Current temperature: <%= @forecast['main']['temp'].to_i - 273 %>°C</p>

显然代码['main']['temp'].to_i - 273 不能在nil 上调用,但是当表单中没有传递任何内容或API 无法识别城市时,如何防止@forecast 成为nil

【问题讨论】:

  • 为什么不在&lt;% if @forecast.present? %&gt; ... &lt;% end %&gt; 中换行呢?此外,默认 @forecast"" 不是惯用的,最好将其定义为 nil (或者根本不定义它,因为无论如何默认情况下实例变量都是 nil)。

标签: ruby-on-rails ruby httparty openweathermap


【解决方案1】:

你可以试试……

if @city.nil?
  @forecast = {}

为了确保@forecast 始终以哈希的形式响应,然后在您的视图中,您可以使用dig,即使对于不存在的节点,您也可以深入了解哈希...

<p>Current temperature: <%= @forecast.dig('main', 'temp').to_i - 273 %>°C</p>

但可能更好

<% if @forecast.present? %>
  <p>Current temperature: <%= @forecast.dig('main', 'temp').to_i - 273 %>°C</p>
<% else %>
  <p>You need to select a city!</p>
<% end %>

【讨论】:

  • 谢谢!这样做的问题是它在第一次加载页面时显示You need to select a city!
  • 如果输入了 API 无法识别的城市,它也会返回 Current temperature: -273°C
  • 查看API在无法识别或空白城市的情况下返回什么。它应该足以让您将错误段落包装在 if 块中。
【解决方案2】:

您可以在每种方法之前简单地使用安全导航运算符 (&amp;) 来防止这种情况发生。

<p>Current temperature: <%= @forecast['main']['temp']&.to_i - 273 %>°C</p>

请参阅this question and answers 了解更多信息。

基本上,它可以防止undefined method for nil:NilClass 发生。如果一个值为空/nil。

【讨论】:

  • 除了未定义的方法是指方括号,而不是to_i
  • @SteveTurczyn 啊,是的,你是对的。我不确定如何将其应用于@forcast。它会直接在对象之前还是之后?在这种情况下甚至可能吗?
  • 是的,你可以try 用于rails 中的括号...[:a, :b, :c].try(:[], 1) 或者在这种情况下@forecast.try(:[], 'main') 和链接将是@forecast.try(:[], 'main').try(:[], 'temp') 但一个有趣的奇怪是nil.to_i 返回@ 987654332@ 但nil.try(:to_i) 返回nil
  • 我已经搜索了几天以正确使用它。我只需要 & 符号,而不是 '.to_i' 部分。 ' 喜欢&lt;p&gt;Current city: &lt;%= @forecast['main']['city']&amp; %&gt;°&lt;/p&gt;
  • @Chnikki .to_i 是针对 OP 问题的“整数”。在方法前使用&amp;,以便在从空参数拉取时允许它通过。
【解决方案3】:

利用最后一条评论,您可以将此逻辑包装在视图对象中并作为选项执行以下操作:

class ForecastsController < ApplicationController
  def current_weather
    @city = params[:q]

    @temperature = ForecastView.new(city).temperature
  end
end

class ForecastView
  DESCRIPTIVE_NAME_HERE = 273

  def initialize(city)
    @city = city
    @token = Rails.application.credentials.openweather_key
  end

  def temperature
    forecast.dig('main', 'temp').to_i - DESCRIPTIVE_NAME_HERE
  end

  private

  attr_reader :city, :token

  def forecast
    return {} if city.blank?

    OpenWeatherApi.new(city, token).my_location_forecast
  end
end

<p>Current temperature: <%= @temperature %>°C</p>

【讨论】:

    【解决方案4】:

    你可以试试

    <p>Current temperature: <%= @forecast['main']['temp'].to_i - 273 if @forcast['main']['temp'].present? %>°C</p>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多