Reputation: 106
I have a post model in my rails project,
First, I think that exposing the real db id is NOT good idea, am I right?
If I'm right, I hope to generate a unique progressively increasing number, use it in url, just link below line:
https://hostname.com/posts/
unique_number
I searched in SO, from below link, SecureRandom
can generate random token, but it can't generate progressively increasing number.
Best way to create unique token in Rails?
So, how to generate progressively increasing number before creating new record.
Thanks a lot.
Upvotes: 0
Views: 1870
Reputation: 37409
Depending on the frequency of posts created, you can use Time from epoch for a (almost) unique number which is progressively increasing, either in seconds:
Time.now.to_i
or in milliseconds:
(Time.now.to_f * 1000).to_i
Caveats:
Upvotes: 3
Reputation: 27207
First, I think that exposing the real db id is NOT good idea, am I right?
It depends.
If your object IDs come from a predictable sequence, that means someone using your application can guess likely valid IDs, and add them to URLs, POST requests etc.
If the objects are accessed read-only and publicly, then there is no problem at all just exposing the database as it was generated.
If you need to secure access, there is more than one way to do so. If you have secure authorisation in your web application, and can tell whether a current user should be able to see an object or not, then you already have sufficient security. Creating unguessable object ids (e.g. uuids) does not really help you. Using some other sequence to "hide" the id does not really add any security and could be a lot of work. Although you could go a step further and generate friendly URLs derived from other content (title), and that would be a similar effort, that is generally not a security concern.
You should only worry about exposing your DB ids directly if that would give a direct link via the API where unauthorised users could guess other IDs and access or change things they were not supposed to. If you cannot restrict access direct via the ID due to other constraints in the system, the answer is not to generate some other direct, predictable index to the same object (which will have the same problems with people being able to guess it), but do one of:
1) provide indirect access - e.g. user can only choose from objects that you have pre-selected for them, and you assign IDs to those choices instead, and only accept those choice_ids via your API, never the database ids of the objects being chosen.
2) generate unguessable IDs, possibly temporary, like SecureRandom.uuid
Upvotes: 1
Reputation: 36860
You could (if it's satisfactory) create a unique number that's somewhere between 1 and 10_000 after the last number generated.
So that the 'to_param' can work properly, your token should be a String.
Migrate to create the column
add_column :posts, :unique_number, :string
Then modify the model...
class Post < ActiveRecord::Base
before_create :new_unique_number
def new_unique_number
last_number = Post.order('unique_number DESC').first.unique_number || '1'
self.unique_number = (last_number.to_i + rand(1, 10_000)).to_s
end
#.....
end
Upvotes: 3
Reputation: 7937
If you use a Post
model, I suppose we speak of blog articles or something ?
The question is : do you really need something incremental, or just something unique ?
If what matters is that your id is unique, you can use anything you want as id, not just an integer.
For example, let say your Post model has a validation on title uniqueness. You can decide its title will be used in urls :
class Post
validates_uniqueness :title
def to_param
title
end
end
The #to_param
method allows to decide which attribute will be used for url generation :
post = Post.create( title: 'my_post' )
post_path( post ) # => /posts/my_post
Note that if you want to use a human string like title, you probably will want to sanitize it in a before save callback and set it in an other field, like "url_title" (to avoid spaces and ugly looking characters in urls).
In controller, you do just as usual, getting the id in params[:id]
:
class PostsController < ApplicationController
before_action :find_post
def show
end
private
def find_post
@post = Post.where( title: params[ :id ] ).first or redirect_to '/'
end
end
Upvotes: 1