Faraday: A validação de URI falha quando contém caracteres reservados

Criado em 4 dez. 2020  ·  3Comentários  ·  Fonte: lostisland/faraday

Informação básica

  • Versão Faraday: 1.1.0
  • Versão Ruby: 2.7.1

Descrição do problema

Um dos provedores de serviço que estou usando tem URLs como este: https://service.com/service:search . É assim que configuramos o Faraday:

class Provider::Client
 ...
 delegate :post, :patch, :delete, :put, to: :client

 def client
  Faraday.new(url: 'https://service.com/') do |faraday|
    faraday.headers = headers.merge(content_type: content_type)
    faraday.response(:json, content_type: CONTENT_TYPE)
    faraday.basic_auth(USER, PASSWORD)
    faraday.adapter(Faraday.default_adapter)
  end
 end
end

e quando eu o invoco

data = Provider::Client.post(
  "service:search?limit=#{LIMIT}&offset=#{offset}", search_params
)

mas quando faço isso com o URL em questão, recebo o seguinte:

URI::InvalidURIError: query conflicts with opaque
from /Users/luiz/.rbenv/versions/2.7.1/lib/ruby/2.7.0/uri/generic.rb:832:in `query='

Presumo que seja algo relacionado ao adaptador net / http, pois é o adaptador padrão, então fiz a mesma chamada manualmente

require "uri"
require "net/http"

url = URI("https://service.com/service:search?limit=50&offset=400")

https = Net::HTTP.new(url.host, url.port);
https.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Accept"] = "custom_header"
request["Authorization"] = "Basic xxx="
request["Content-Type"] = "application/json"
request.body = "long_json"
response = https.request(request)

puts response.read_body

e isso realmente funciona, então eu suspeito que algo em Faraday está validando este URL de uma maneira diferente de net / http

bug good first issue

Comentários muito úteis

@luizkowalski perfeito, sim, é exatamente o que eu quis dizer com

No entanto, isso não funciona em todos os casos, pois pode ser necessário anexar um caminho relativo à base que não pode começar com /.

Como faraday assume que a URL fornecida é absoluta se começa com / e remove o caminho fornecido para a base.
Que bom que você já encontrou a solução alternativa, mas isso ainda deve ser corrigido adequadamente para que a solução alternativa não seja mais necessária

Todos 3 comentários

Olá @luizkowalski , a exceção vem de URI e é gerada nas primeiras linhas do método query= :

def query=(v)
  return <strong i="9">@query</strong> = nil unless v
  raise InvalidURIError, "query conflicts with opaque" if <strong i="10">@opaque</strong>
  ...
end

Faraday faz alguma mágica interna no url do pedido, dividindo-o e recombinando-o para ajudar em algumas transformações comuns, e presumo que em algum momento isso pode fazer com que @opaque seja definido graças à presença de : no URL.
Rastreei o problema até esta linha: https://github.com/lostisland/faraday/blob/c26df87b8653db4f270e3bcdc7a15bcdd2dd5cae/lib/faraday/connection.rb#L525
Usando seu código de exemplo, base seria #<URI::HTTPS https://service.com/> e url seria service:search?limit=50&offset=400 .
Agora, curiosamente, se o segundo parâmetro do método + para URI contém um : , parece que o resultado é bastante inesperado!

2.7.1 > url
 => "service:search?limit=50&offset=400" 
2.7.1 > base
 => #<URI::HTTPS https://service.com/> 
2.7.1 > base + url
 => #<URI::Generic service:search?limit=50&offset=400>

Por algum motivo, base é removido e apenas o url é deixado.
A correção é muito fácil neste caso, é suficiente apenas acrescentar / ao url e as coisas funcionam conforme o esperado:

data = Provider::Client.post(
  "/service:search?limit=#{LIMIT}&offset=#{offset}", search_params
)

No entanto, isso não funciona em todos os casos, pois pode ser necessário anexar um caminho relativo ao base que não pode começar com / .
Experimente antes de / e deixe-me saber se funciona, mas deixarei este problema em aberto para dar uma olhada e encontrar uma maneira possível de corrigir essa linha de código em Faraday para que funciona neste caso.
Se você quiser tentar você mesmo, ficaria feliz em revisar um PR 😃

@iMacTia funcionou agora. O URL base é: https://service.com/api . Quando adicionei / a service:search , obtive um erro e notei que /api tinha sumido do URL base, então adicionei /api/service:search e agora está tudo bem

@luizkowalski perfeito, sim, é exatamente o que eu quis dizer com

No entanto, isso não funciona em todos os casos, pois pode ser necessário anexar um caminho relativo à base que não pode começar com /.

Como faraday assume que a URL fornecida é absoluta se começa com / e remove o caminho fornecido para a base.
Que bom que você já encontrou a solução alternativa, mas isso ainda deve ser corrigido adequadamente para que a solução alternativa não seja mais necessária

Esta página foi útil?
0 / 5 - 0 avaliações