ฉันสงสัยว่าฉันจะตรวจสอบ URL ใน Rails ให้ดีที่สุดได้อย่างไร ฉันกำลังคิดว่าจะใช้นิพจน์ทั่วไป แต่ไม่แน่ใจว่านี่เป็นแนวทางปฏิบัติที่ดีที่สุดหรือไม่
และถ้าฉันจะใช้ regex มีใครแนะนำให้ฉันได้ไหม ฉันยังใหม่กับ Regex
ฉันสงสัยว่าฉันจะตรวจสอบ URL ใน Rails ให้ดีที่สุดได้อย่างไร ฉันกำลังคิดว่าจะใช้นิพจน์ทั่วไป แต่ไม่แน่ใจว่านี่เป็นแนวทางปฏิบัติที่ดีที่สุดหรือไม่
และถ้าฉันจะใช้ regex มีใครแนะนำให้ฉันได้ไหม ฉันยังใหม่กับ Regex
คำตอบ:
การตรวจสอบ URL เป็นงานที่ยุ่งยาก ยังเป็นคำขอที่กว้างมาก
คุณต้องการทำอะไรกันแน่? คุณต้องการตรวจสอบรูปแบบของ URL การมีอยู่หรืออะไร มีความเป็นไปได้หลายประการขึ้นอยู่กับสิ่งที่คุณต้องการทำ
นิพจน์ทั่วไปสามารถตรวจสอบความถูกต้องของรูปแบบของ URL แต่แม้แต่นิพจน์ทั่วไปที่ซับซ้อนก็ไม่สามารถมั่นใจได้ว่าคุณกำลังจัดการกับ URL ที่ถูกต้อง
ตัวอย่างเช่นหากคุณใช้นิพจน์ทั่วไปง่ายๆก็อาจปฏิเสธโฮสต์ต่อไปนี้
http://invalid##host.com
แต่จะอนุญาต
http://invalid-host.foo
นั่นคือโฮสต์ที่ถูกต้อง แต่ไม่ใช่โดเมนที่ถูกต้องหากคุณพิจารณา TLD ที่มีอยู่ แน่นอนวิธีแก้ปัญหาจะใช้ได้ถ้าคุณต้องการตรวจสอบชื่อโฮสต์ไม่ใช่โดเมนเนื่องจากชื่อต่อไปนี้เป็นชื่อโฮสต์ที่ถูกต้อง
http://host.foo
เช่นเดียวกับสิ่งต่อไปนี้
http://localhost
ตอนนี้ให้ฉันแก้ปัญหาบางอย่าง
หากคุณต้องการตรวจสอบโดเมนคุณต้องลืมเกี่ยวกับนิพจน์ทั่วไป ทางออกที่ดีที่สุดในขณะนี้คือ Public Suffix List ซึ่งเป็นรายการที่ Mozilla ดูแล ฉันสร้างห้องสมุดทับทิมที่จะแยกและตรวจสอบโดเมนกับสาธารณะรายการต่อท้ายและก็เรียกว่าPublicSuffix
หากคุณต้องการตรวจสอบความถูกต้องของรูปแบบของ URI / URL คุณอาจต้องการใช้นิพจน์ทั่วไป แทนที่จะค้นหาอย่างใดอย่างหนึ่งให้ใช้URI.parse
วิธีRuby ในตัว
require 'uri'
def valid_url?(uri)
uri = URI.parse(uri) && !uri.host.nil?
rescue URI::InvalidURIError
false
end
คุณยังสามารถตัดสินใจที่จะกำหนดให้มีข้อ จำกัด มากขึ้น ตัวอย่างเช่นหากคุณต้องการให้ URL เป็น HTTP / HTTPS URL คุณสามารถทำให้การตรวจสอบถูกต้องมากขึ้น
require 'uri'
def valid_url?(url)
uri = URI.parse(url)
uri.is_a?(URI::HTTP) && !uri.host.nil?
rescue URI::InvalidURIError
false
end
แน่นอนว่ามีการปรับปรุงมากมายที่คุณสามารถนำไปใช้กับวิธีนี้รวมถึงการตรวจสอบเส้นทางหรือโครงร่าง
สุดท้าย แต่ไม่ท้ายสุดคุณสามารถรวมรหัสนี้ไว้ในโปรแกรมตรวจสอบความถูกต้อง:
class HttpUrlValidator < ActiveModel::EachValidator
def self.compliant?(value)
uri = URI.parse(value)
uri.is_a?(URI::HTTP) && !uri.host.nil?
rescue URI::InvalidURIError
false
end
def validate_each(record, attribute, value)
unless value.present? && self.class.compliant?(value)
record.errors.add(attribute, "is not a valid HTTP URL")
end
end
end
# in the model
validates :example_attribute, http_url: true
URI::HTTPS
https uris (เช่นURI.parse("https://yo.com").class => URI::HTTPS
URI::HTTPS
สืบทอดจากที่เป็นเหตุผลว่าทำไมผมใช้URI:HTTP
kind_of?
URI.parse('http://invalid-host.foo')
คืนค่าจริงเนื่องจาก URI เป็น URL ที่ถูกต้อง โปรดทราบว่า.foo
ตอนนี้ TLD ถูกต้องแล้ว iana.org/domains/root/db/foo.html
ฉันใช้ซับในแบบจำลองของฉัน:
validates :url, format: URI::regexp(%w[http https])
ฉันคิดว่าดีพอและใช้งานง่าย ยิ่งไปกว่านั้นควรจะเทียบเท่าในทางทฤษฎีกับวิธีการของ Simone เนื่องจากใช้ regexp เดียวกันภายใน
'http://'
ตรงกับรูปแบบด้านบน ดู:URI::regexp(%w(http https)) =~ 'http://'
http:fake
ก็ใช้ได้เช่นกัน
ตามแนวคิดของ Simone คุณสามารถสร้างโปรแกรมตรวจสอบความถูกต้องของคุณเองได้อย่างง่ายดาย
class UrlValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return if value.blank?
begin
uri = URI.parse(value)
resp = uri.kind_of?(URI::HTTP)
rescue URI::InvalidURIError
resp = false
end
unless resp == true
record.errors[attribute] << (options[:message] || "is not an url")
end
end
end
แล้วใช้
validates :url, :presence => true, :url => true
ในแบบจำลองของคุณ
URI("http:").kind_of?(URI::HTTP) #=> true
นอกจากนี้ยังมีอัญมณี validate_url (ซึ่งเป็นเพียงกระดาษห่อหุ้มที่ดีสำหรับAddressable::URI.parse
การแก้ปัญหา)
เพียงแค่เพิ่ม
gem 'validate_url'
ให้กับคุณGemfile
แล้วในแบบจำลองที่คุณทำได้
validates :click_through_url, url: true
คำถามนี้มีคำตอบแล้ว แต่สิ่งที่ฉันเสนอวิธีแก้ปัญหาที่ฉันใช้
regexp ทำงานได้ดีกับ URL ทั้งหมดที่ฉันพบ วิธี setter คือการดูแลหากไม่มีการกล่าวถึงโปรโตคอล (สมมติว่า http: //)
และสุดท้ายเราจะพยายามดึงข้อมูลหน้า บางทีฉันควรยอมรับการเปลี่ยนเส้นทางไม่ใช่แค่ HTTP 200 OK เท่านั้น
# app/models/my_model.rb
validates :website, :allow_blank => true, :uri => { :format => /(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$)/ix }
def website= url_str
unless url_str.blank?
unless url_str.split(':')[0] == 'http' || url_str.split(':')[0] == 'https'
url_str = "http://" + url_str
end
end
write_attribute :website, url_str
end
และ...
# app/validators/uri_vaidator.rb
require 'net/http'
# Thanks Ilya! http://www.igvita.com/2006/09/07/validating-url-in-ruby-on-rails/
# Original credits: http://blog.inquirylabs.com/2006/04/13/simple-uri-validation/
# HTTP Codes: http://www.ruby-doc.org/stdlib/libdoc/net/http/rdoc/classes/Net/HTTPResponse.html
class UriValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
raise(ArgumentError, "A regular expression must be supplied as the :format option of the options hash") unless options[:format].nil? or options[:format].is_a?(Regexp)
configuration = { :message => I18n.t('errors.events.invalid_url'), :format => URI::regexp(%w(http https)) }
configuration.update(options)
if value =~ configuration[:format]
begin # check header response
case Net::HTTP.get_response(URI.parse(value))
when Net::HTTPSuccess then true
else object.errors.add(attribute, configuration[:message]) and false
end
rescue # Recover on DNS failures..
object.errors.add(attribute, configuration[:message]) and false
end
else
object.errors.add(attribute, configuration[:message]) and false
end
end
end
คุณยังสามารถลองใช้valid_url gem ซึ่งอนุญาต URL ที่ไม่มีสกีมตรวจสอบโซนโดเมนและชื่อโฮสต์ ip
เพิ่มลงใน Gemfile ของคุณ:
gem 'valid_url'
จากนั้นในรุ่น:
class WebSite < ActiveRecord::Base
validates :url, :url => true
end
แค่ 2 เซ็นต์ของฉัน:
before_validation :format_website
validate :website_validator
private
def format_website
self.website = "http://#{self.website}" unless self.website[/^https?/]
end
def website_validator
errors[:website] << I18n.t("activerecord.errors.messages.invalid") unless website_valid?
end
def website_valid?
!!website.match(/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-=\?]*)*\/?$/)
end
แก้ไข: เปลี่ยน regex เพื่อให้ตรงกับ URL พารามิเตอร์
http://test.com/fdsfsdf?a=b
วิธีแก้ปัญหาที่เหมาะกับฉันคือ:
validates_format_of :url, :with => /\A(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w\.-]*)*\/?\Z/i
ฉันพยายามใช้ตัวอย่างที่คุณแนบมา แต่ฉันรองรับ url ดังนี้:
สังเกตการใช้ A และ Z เพราะถ้าคุณใช้ ^ และ $ คุณจะเห็นการรักษาความปลอดภัยคำเตือนนี้จาก Rails validators
Valid ones:
'www.crowdint.com'
'crowdint.com'
'http://crowdint.com'
'http://www.crowdint.com'
Invalid ones:
'http://www.crowdint. com'
'http://fake'
'http:fake'
"https://portal.example.com/portal/#"
. ใน Ruby 2.1.6 การประเมินค้าง
เมื่อเร็ว ๆ นี้ฉันพบปัญหาเดียวกัน (ฉันต้องการตรวจสอบความถูกต้องของ URL ในแอป Rails) แต่ฉันต้องรับมือกับข้อกำหนดเพิ่มเติมของยูนิโคด URL (เช่นhttp://кц.рф
) ...
ฉันค้นคว้าวิธีแก้ปัญหาสองสามข้อและพบสิ่งต่อไปนี้:
URI.parse
สิ่งแรกและชี้ให้เห็นมากที่สุดคือการใช้ ตรวจสอบคำตอบโดย Simone Carletti สำหรับรายละเอียด ใช้งานได้ แต่ไม่ใช่สำหรับ URL แบบยูนิโคดURI.parse
แต่ใช้addressable
อัญมณีแทนURI
stdlib แนวทางนี้มีรายละเอียดที่นี่: http://rawsyntax.com/blog/url-validation-in-rails-3-and-ruby-in-general/Addressable::URI.parse('http:///').scheme # => "http"
หรือAddressable::URI.parse('Съешь [же] ещё этих мягких французских булок да выпей чаю')
เป็นที่ยอมรับอย่างสมบูรณ์จากมุมมองของ Addressable :(
นี่เป็นรุ่นที่ปรับปรุงของตรวจสอบโพสต์โดยเดวิดเจมส์ มันได้รับการตีพิมพ์โดยเบนจามิน Fleischer ในขณะที่ฉันผลักดันการปรับปรุงทางแยกซึ่งสามารถพบได้ที่นี่
require 'addressable/uri'
# Source: http://gist.github.com/bf4/5320847
# Accepts options[:message] and options[:allowed_protocols]
# spec/validators/uri_validator_spec.rb
class UriValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
uri = parse_uri(value)
if !uri
record.errors[attribute] << generic_failure_message
elsif !allowed_protocols.include?(uri.scheme)
record.errors[attribute] << "must begin with #{allowed_protocols_humanized}"
end
end
private
def generic_failure_message
options[:message] || "is an invalid URL"
end
def allowed_protocols_humanized
allowed_protocols.to_sentence(:two_words_connector => ' or ')
end
def allowed_protocols
@allowed_protocols ||= [(options[:allowed_protocols] || ['http', 'https'])].flatten
end
def parse_uri(value)
uri = Addressable::URI.parse(value)
uri.scheme && uri.host && uri
rescue URI::InvalidURIError, Addressable::URI::InvalidURIError, TypeError
end
end
...
require 'spec_helper'
# Source: http://gist.github.com/bf4/5320847
# spec/validators/uri_validator_spec.rb
describe UriValidator do
subject do
Class.new do
include ActiveModel::Validations
attr_accessor :url
validates :url, uri: true
end.new
end
it "should be valid for a valid http url" do
subject.url = 'http://www.google.com'
subject.valid?
subject.errors.full_messages.should == []
end
['http://google', 'http://.com', 'http://ftp://ftp.google.com', 'http://ssh://google.com'].each do |invalid_url|
it "#{invalid_url.inspect} is a invalid http url" do
subject.url = invalid_url
subject.valid?
subject.errors.full_messages.should == []
end
end
['http:/www.google.com','<>hi'].each do |invalid_url|
it "#{invalid_url.inspect} is an invalid url" do
subject.url = invalid_url
subject.valid?
subject.errors.should have_key(:url)
subject.errors[:url].should include("is an invalid URL")
end
end
['www.google.com','google.com'].each do |invalid_url|
it "#{invalid_url.inspect} is an invalid url" do
subject.url = invalid_url
subject.valid?
subject.errors.should have_key(:url)
subject.errors[:url].should include("is an invalid URL")
end
end
['ftp://ftp.google.com','ssh://google.com'].each do |invalid_url|
it "#{invalid_url.inspect} is an invalid url" do
subject.url = invalid_url
subject.valid?
subject.errors.should have_key(:url)
subject.errors[:url].should include("must begin with http or https")
end
end
end
โปรดสังเกตว่ายังมี HTTP URI แปลก ๆ ที่แยกวิเคราะห์เป็นที่อยู่ที่ถูกต้อง
http://google
http://.com
http://ftp://ftp.google.com
http://ssh://google.com
นี่คือปัญหาสำหรับaddressable
อัญมณีที่ครอบคลุมตัวอย่าง
ผมใช้การเปลี่ยนแปลงเล็กน้อยในการแก้ปัญหาดังกล่าวข้างต้น Lafeber ไม่อนุญาตให้ใช้จุดที่ติดต่อกันในชื่อโฮสต์ (เช่นอินสแตนซ์www.many...dots.com
):
%r"\A(https?://)?[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]{2,6}(/.*)?\Z"i
URI.parse
ดูเหมือนว่าจะกำหนดคำนำหน้าแบบแผนซึ่งในบางกรณีอาจไม่ใช่สิ่งที่คุณต้องการ (เช่นหากคุณต้องการอนุญาตให้ผู้ใช้ของคุณสะกด URL ในรูปแบบต่างๆได้อย่างรวดเร็วเช่นtwitter.com/username
)
ฉันใช้อัญมณี 'activevalidators'และใช้งานได้ดี (ไม่ใช่เฉพาะการตรวจสอบ URL)
คุณสามารถหาได้ที่นี่
ทุกอย่างได้รับการบันทึกไว้แล้ว แต่โดยทั่วไปเมื่อเพิ่มอัญมณีแล้วคุณจะต้องเพิ่มสองสามบรรทัดต่อไปนี้ในตัวเริ่มต้นพูดว่า: /config/environment/initializers/active_validators_activation.rb
# Activate all the validators
ActiveValidators.activate(:all)
(หมายเหตุ: คุณสามารถแทนที่: all by: url หรือ: อะไรก็ได้หากคุณต้องการตรวจสอบความถูกต้องของค่าบางประเภท)
แล้วย้อนกลับไปในโมเดลของคุณประมาณนี้
class Url < ActiveRecord::Base
validates :url, :presence => true, :url => true
end
ตอนนี้รีสตาร์ทเซิร์ฟเวอร์และนั่นควรจะเป็น
หากคุณต้องการการตรวจสอบอย่างง่ายและข้อความแสดงข้อผิดพลาดที่กำหนดเอง:
validates :some_field_expecting_url_value,
format: {
with: URI.regexp(%w[http https]),
message: 'is not a valid URL'
}
คุณสามารถตรวจสอบหลาย URL โดยใช้สิ่งต่อไปนี้
validates_format_of [:field1, :field2], with: URI.regexp(['http', 'https']), allow_nil: true
https://github.com/perfectline/validates_urlเป็นอัญมณีที่ดีและเรียบง่ายที่จะทำทุกอย่างให้กับคุณ
เมื่อเร็ว ๆ นี้ฉันมีปัญหาเดียวกันนี้และฉันพบวิธีแก้ปัญหาสำหรับ URL ที่ถูกต้อง
validates_format_of :url, :with => URI::regexp(%w(http https))
validate :validate_url
def validate_url
unless self.url.blank?
begin
source = URI.parse(self.url)
resp = Net::HTTP.get_response(source)
rescue URI::InvalidURIError
errors.add(:url,'is Invalid')
rescue SocketError
errors.add(:url,'is Invalid')
end
end
ส่วนแรกของเมธอด validate_url เพียงพอที่จะตรวจสอบความถูกต้องของรูปแบบ URL ส่วนที่สองจะทำให้แน่ใจว่ามี url อยู่โดยการส่งคำขอ
ฉันชอบที่จะจับคู่โมดูล URI เพื่อเพิ่มที่ถูกต้อง? วิธี
ภายใน config/initializers/uri.rb
module URI
def self.valid?(url)
uri = URI.parse(url)
uri.is_a?(URI::HTTP) && !uri.host.nil?
rescue URI::InvalidURIError
false
end
end
และเป็นโมดูล
module UrlValidator
extend ActiveSupport::Concern
included do
validates :url, presence: true, uniqueness: true
validate :url_format
end
def url_format
begin
errors.add(:url, "Invalid url") unless URI(self.url).is_a?(URI::HTTP)
rescue URI::InvalidURIError
errors.add(:url, "Invalid url")
end
end
end
จากนั้นinclude UrlValidator
ในรุ่นใดก็ได้ที่คุณต้องการตรวจสอบความถูกต้องของ url รวมเพียงสำหรับตัวเลือก
การตรวจสอบความถูกต้องของ URL ไม่สามารถจัดการได้ง่ายๆโดยใช้นิพจน์ทั่วไปเนื่องจากจำนวนเว็บไซต์เพิ่มขึ้นเรื่อย ๆ และมีแผนการตั้งชื่อโดเมนใหม่ ๆ
ในกรณีของฉันฉันเพียงแค่เขียนโปรแกรมตรวจสอบความถูกต้องที่กำหนดเองเพื่อตรวจสอบการตอบสนองที่ประสบความสำเร็จ
class UrlValidator < ActiveModel::Validator
def validate(record)
begin
url = URI.parse(record.path)
response = Net::HTTP.get(url)
true if response.is_a?(Net::HTTPSuccess)
rescue StandardError => error
record.errors[:path] << 'Web address is invalid'
false
end
end
end
ฉันกำลังตรวจสอบpath
คุณสมบัติของโมเดลของฉันโดยใช้record.path
. ฉันยังส่งข้อผิดพลาดไปยังชื่อแอตทริบิวต์ที่เกี่ยวข้องโดยใช้record.errors[:path]
.
คุณสามารถแทนที่ด้วยชื่อแอตทริบิวต์ใดก็ได้
จากนั้นฉันก็เรียกตัวตรวจสอบความถูกต้องที่กำหนดเองในโมเดลของฉัน
class Url < ApplicationRecord
# validations
validates_presence_of :path
validates_with UrlValidator
end
คุณสามารถใช้ regex สำหรับสิ่งนี้สำหรับฉันทำงานได้ดีอันนี้:
(^|[\s.:;?\-\]<\(])(ftp|https?:\/\/[-\w;\/?:@&=+$\|\_.!~*\|'()\[\]%#,]+[\w\/#](\(\))?)(?=$|[\s',\|\(\).:;?\-\[\]>\)])