How do I safely join relative url segments?

uri join c#
ruby uri
golang url path join
uri join java
ruby format url
addressable gem
rails join path
ruby uri extract

I'm trying to find a robust method of joining partial url path segments together. Is there a quick way to do this?

I tried the following:

puts URI::join('resource/', '/edit', '12?option=test')

I expect:

resource/edit/12?option=test

But I get the error:

`merge': both URI are relative (URI::BadURIError)

I have used File.join() in the past for this but something does not seem right about using the file library for urls.

URI's api is not neccearily great.

URI::join will work only if the first one starts out as an absolute uri with protocol, and the later ones are relative in the right ways... except I try to do that and can't even get that to work.

This at least doesn't error, but why is it skipping the middle component?

 URI::join('http://somewhere.com/resource', './edit', '12?option=test') 

I think maybe URI just kind of sucks. It lacks significant api on instances, such as an instance #join or method to evaluate relative to a base uri, that you'd expect. It's just kinda crappy.

I think you're going to have to write it yourself. Or just use File.join and other File path methods, after testing all the edge cases you can think of to make sure it does what you want/expect.

edit 9 Dec 2016 I figured out the addressable gem does it very nicely.

base = Addressable::URI.parse("http://example.com")
base + "foo.html"
# => #<Addressable::URI:0x3ff9964aabe4 URI:http://example.com/foo.html>

base = Addressable::URI.parse("http://example.com/path/to/file.html")
base + "relative_file.xml"
# => #<Addressable::URI:0x3ff99648bc80 URI:http://example.com/path/to/relative_file.xml>

base = Addressable::URI.parse("https://example.com/path")
base + "//newhost/somewhere.jpg"
# => #<Addressable::URI:0x3ff9960c9ebc URI:https://newhost/somewhere.jpg>

base = Addressable::URI.parse("http://example.com/path/subpath/file.html")
base + "../up-one-level.html"
=> #<Addressable::URI:0x3fe13ec5e928 URI:http://example.com/path/up-one-level.html>

How do I safely join relative url segments?, I'm trying to find a robust method of joining partial url path segments together. Is there a `merge': both URI are relative (URI::BadURIError). the relative URLs should be resolved as shown below. I will need your help testing the examples on multiple browsers. What you need to do is point to the example anchor and compare it to the resolved URL in your browser (most browsers have a feature by which you can see the resolved URL at the bottom of the window/screen when the anchor is active).

The problem is that resource/ is relative to the current directory, but /edit refers to the top level directory due to the leading slash. It's impossible to join the two directories without already knowing for certain that edit contains resource.

If you're looking for purely string operations, simply remove the leading or trailing slashes from all parts, then join them with / as the glue.

Uri.Segments Property (System), This instance represents a relative URI, and this property is valid only for absolute URIs. Examples. The following example creates a Uri instance with 3 segments  How do you enable URL segments? URL segments are not enabled by default. They have to be enabled for each template you want them in: Setup > Templates > [your template] > URLs > Enable URL segments. By default, ProcessWire allows up to 4 stacked URL segments before it'll start throwing 404s.

The way to do it using URI.join is:

URI.join('http://example.com', '/foo/', 'bar')

Pay attention to the trailing slashes. You can find the complete documentation here:

http://www.ruby-doc.org/stdlib-1.9.3/libdoc/uri/rdoc/URI.html#method-c-join

Why is URI.join so counterintuitive?, I recently found myself using `URI.join` to construct certain some redirect a) All but the last segment of the base URI's path component is  Relative URLs . By default, all relative URLs are left unchanged by Hugo, which can be problematic when you want to make your site browsable from a local file system. Setting relativeURLs to true in your site configuration will cause Hugo to rewrite all relative URLs to be relative to the current content.

Have uri as URI::Generic or subclass of thereof

uri.path += '/123' 

Enjoy!

06/25/2016 UPDATE for skeptical folk

require 'uri'
uri = URI('http://ioffe.net/boris')
uri.path += '/123'
p uri

Outputs

 <URI::HTTP:0x2341a58 URL:http://ioffe.net/boris/123>

Run me

Fluent URL Building, Flurl's URL builder is best explained with an example: For path segments, the ? character is encoded, since query strings get special treatment. Flurl also contains some handy static methods, most notably Url.Combine , which is basically  The Segments property returns an array of strings containing the "segments" (substrings) that form the URI's absolute path. The first segment is obtained by parsing the absolute path from its first character until you reach a slash (/) or the end of the path.

Using File.join is not robust since it will use the OS filesystem separator, which in Windows is \ instead of /, losing portability.

As you noticed, URI::join won't combine paths with repeated slashes, so it doesn't fit the part.

Turns out it doesn't require a lot of Ruby code to achieve this:

module GluePath

  def self.join(*paths, separator: '/')
    paths = paths.compact.reject(&:empty?)
    last = paths.length - 1
    paths.each_with_index.map { |path, index|
      _expand(path, index, last, separator)
    }.join
  end

  def self._expand(path, current, last, separator)
    if path.start_with?(separator) && current != 0
      path = path[1..-1]
    end

    unless path.end_with?(separator) || current == last
      path = [path, separator]
    end

    path
  end
end

The algorithm takes care of consecutive slashes, preserves start and end slashes, and ignores nil and empty strings.

puts GluePath::join('resource/', '/edit', '12?option=test')

outputs

resource/edit/12?option=test

parse_url - Manual, parse_url — Разбирает URL и возвращает его компоненты I have coded a function which converts relative URL to absolute URL for a project of mine. This article will examine the properties of the Request object that will provide path and url information related to the application and the current request. First, here are a couple of tables of useful properties on the Request object and an example of the text they return for a given input URL.

PHP: parse_url - Manual, Using remote files · Connection handling · Persistent Database Connections · Safe Mode Unvollständige URLs werden als Parameter akzeptiert, parse_url() versucht, sie Diese Funktion gibt für relative URLs möglicherweise inkorrekte Ergebnisse. foreach ($url_path as $segment) { $base['path'] = join('/', $path); } The url() CSS function is used to include a file. The parameter is an absolute URL, a relative URL, or a data URI. The url() function can be passed as a parameter of another CSS functions, like the attr() function. Depending on the property for which it is a value, the resource sought can be an image, font, or a stylesheet. The url() functional notation is the value for the url data type.

URL Conventions (OData Version 3.0) · OData, Since OData has a uniform composable URL syntax and associated rules Often however a single entity is accessed by composing more path segments to a Each expandItem MUST be evaluated relative to the entity type of the request,​  Flurl takes care of encoding characters in URLs but takes a different approach with path segments than it does with query string values. The assumption is that query string values are highly variable (such as from user input), whereas path segments tend to be more "fixed" and may already be encoded, in which case you don't want to double-encode.

C# Uri and UriBuilder Classes, You can use the Uri class in the C# programming language to represents URIs a much easier way to access parts of the URI and also manipulate and combine URIs. Port = 80 Query = Scheme = http Segments = / UserEscaped = False UserInfo Here: We determine a minimal, relative URI between two absolute URIs. The last thing that every developer should know is the difference between an absolute and relative URL as well as how to turn a relative URL into its absolute form. The first part of that is pretty easy, if a URL contains a scheme (such as http), then it can be considered an absolute URL. Relative URLs are a little bit more complicated.

Comments
  • "something does not seem right about using the file library for urls", that is correct. File.join is sensitive to the OS and will change the character used as the separator, depending on the OS. That would give you bad results.
  • Do not forget to do to_s if you need String version of uri, otherwise you get URI::HTTP object
  • Addresable skips the middle segment as well if there is more than one. e.g. (Addressable::URI.parse("http://example.com") + "abc" + "xyz").to_s generates http://example.com/xyz. Years after my original answer (stackoverflow.com/a/9067748/345034) on this question, I still think File.join is the safest way to join URL segments. :D
  • Well, unless you're not working at the domain root, in which case the leading / on the first part would make a difference.
  • That's easy to force using an empty string as a first parameter.
  • If you need to manage the leading and trailing slashes yourself, what's the point of using this method in the first place?
  • You get a URI::HTTP object in this case. Though you have a valid point, and I generally use File.join('http://example.com', '/foo/', 'bar') when I need a string and it takes care of slashes. PS: I work solely on linux based systems and servers, so I don't face the file separator issue described above.
  • This replaces the entire path component with '/123', it doesn't extend the existing path.
  • Here's what I get: uri = URI("httpx://example.com/mypath/"); uri+= '/123' => #<URI::HTTPS example.com/123>
  • @AneilMallavarapu you should consider that you doing something wrong. In this case you should use url.path instead of url. Please look carefully at the sample I provided, before down-voting a legit answer.
  • Actually, File.join does not use a backslash separator on Windows. It uses a forward slash (/), just as on Linux.
  • In case it helps others, the starts_with? and the ends_with? probably needs to be start_with? and end_with? respectively.