"Incorrect string value" 尝试将 UTF-8 从 Rails 插入 MySQL 时

"Incorrect string value" when trying to insert UTF-8 into MySQL from Rails

调试我的 Rails 应用程序时,我在日志文件中发现以下消息:

(0.1ms)  ROLLBACK
Completed 500 Internal Server Error in 25ms (ActiveRecord: 4.2ms)
ActiveRecord::StatementInvalid (Mysql2::Error: Incorrect string value: '\xF0\x9F\x98\x89 u...' for column 'description' at row 1: INSERT INTO `course` (`title`, `description`) VALUES ('sometitle', '<p>Description containing  and stuff</p>')

这似乎是因为我的数据库是 MySQL with not-quite-utf-8:

CREATE TABLE `course` (
  `id` int NOT NULL AUTO_INCREMENT,
  `title` varchar(250) DEFAULT NULL,
  `description` text,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2080 DEFAULT CHARSET=utf8;

根据 the answers to this question CHARSET=utf8 只能处理 3 字节字符,不能处理 4 字节字符。

表情符号需要四个字节 - 请参阅日志文件中的 \xF0\x9F\x98\x89。

我对转换整个数据库持谨慎态度。我宁愿禁止使用表情符号和其他 4 字节字符 - 它们在我的网站上真的没有必要。

在 Rails 中执行此操作的最佳方法是什么?

基于 regular expressions from these answers 我实现了一个验证器:

# file /lib/three_byte_validator.rb
# forbid characters that use more than three byte
class ThreeByteValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    if value =~ /[\u{10000}-\u{10FFFF}]/
      record.errors.add attribute, (options[:message] || 'Keine Emoticons, keine UTF-8 Zeichen mit 4 Byte')
    end
  end
end

现在我可以在最先出现问题的模型上使用这个验证器了:

class Course < ApplicationRecord
  validates :title, length: { in: 3..100 }, three_byte: true
  validates :description, length: { minimum: 50 }, three_byte: true

以及其他型号:

class Person < ApplicationRecord
  validates :firstname, :lastname, :country, :city, :address, three_byte: true

在 MySQL 中,眨眼脸(和大多数其他表情符号)需要 utf8mb4 而不是 utf8