Há vários sites onde podemos encurtar nossos URLs, como tinyURL.com, is.gd, dwarfURL.com, bit.ly e outros.
Recebi uma chamada de um amigo que tem vários artigos distribuidos com URLs encurtados e ele estava com medo de caso os sites que hospedam o redirecionamento parassem, seus artigos distribuidos estariam perdidos.
Então ele me perguntou se eu podia criar um site de URL Shortener para seu uso privado e para ser hospedado em seu servidor junto com seus websites.
Decidi disponibilizar o código para a comunidade para aqueles que pensam da mesma forma.
Nosso ambiente é Windows Server 2003, Microsoft SQL 2008 e ColdFusion 8.
Domain Name
Primeiro, voce precisa registrar um domain name bem curto e criar seu website. Suponho que voce tenha registrado o domínio zzz.com e configurou os registros DNS.
- Crie uma pasta para seu site no raiz do seu web, ex: c:\inetpub\wwwroot\zzz
- Adicione o site no IIS
ISAPI Rewrite
Para redirecionar o URL curto para o destino, voce precisa configurar o ISAPI URL Rewrite.
- Se voce tiver IIS 5 ou 6
- Voce deve baixar a DLL e o arquivo de configuração (.ini) AQUI.
- Salve o IsapiRewrite4.dll e o arquivo de configuração em uma pasta fora do seu raiz. Eu salvei em “c:\ISAPI\urlShortener\”.
O arquivo de configuraç?o é bem simples e contem a seguinte regra:RewriteRule ^/([^/.\?]+)$ /index.cfm?go=$1
- Se voce tiver o IIS 7+ (Windows Server 2008 ou Windows 7)
- Voce precisa baixar a ferramenta de ISAPI Rewrite (http://www.iis.net/download/URLRewrite)
- Clique no link x86 ou x64 (64-bit) no menu à direita, caso voce não queira instalar o Microsoft Werb Platform Installer.
- Execute a instalação e o seu IIS terá a ferramenta “Rewrite” para todos os seus sites.
- Configure o ISAPI:
- Abra o IIS Manager e selecione o seu web site ou pasta virtual (caso Windows 7)
- No bloco de icons IIS, clique em “URL Rewrite”
- A janela URL Rewrite abrirá, então clique em “Add Rule(s)” no menu à direita.
- No Rule Template, selecione “User-friendly URL” e clique OK.
- Entre o seguinte no campo URL:
http://zzz.com/index.cfm?go=xyz123
(naturalmente, substitua o zzz.com pelo seu domínio)- O IIS mostrará o URL curto e o pattern nos campos abaixo. Apenas clique OK.
- Agora a janela URL Rewrite mostrará a regra recem criada.
- Voce precisa baixar a ferramenta de ISAPI Rewrite (http://www.iis.net/download/URLRewrite)
Database
Próximo passo: Configurar a base de dados:
- Crie uma base de dados e uma tabela (eu nomeei a base “urlShortener” e a tabela “urls”) (baixe os scripts AQUI) (códigos SP1 e SP2)
- Crie duas stored procedures: dbo.setShortUrl e dbo.getShortURL (baixe os scripts AQUI)
- A stored procedure setShortUrl checa se o URL detino e o código passado já existem, e se não existem, então salva o novo destino e código. (código SP3)
- A stored procedure getShortUrl recupera o URL destino a partir de um código fornecido (short code). (código SP4)
ColdFusion Data Source
Adicione o datasource “urlShortener” no Administrador do ColdFusion. Vou assumir que voce sabe fazê-lo.
CFML
Agora vamos trabalhar no projeto no CFEclipse ou CFBuilder.
- Crie o “Application.cfc ” (código CF1)
- Crie o componente “shortener.cfc” (código CF2)
- Crie o “index.cfm” (código CF3)
Voce pode baixar o projeto completo AQUI.
Espero que este artigo lhe sirva. Caso voce ache algum erro, por favor reporte, ou caso faça melhorias no projeto, entre em contato. Seus comentários são valiosos.
Obrigado.
Ricardo Parente
Códigos:
Código S1: Criar Database urlShortener
USE [master] GO CREATE DATABASE [urlShortener] ON PRIMARY ( NAME = N'urlShortener', FILENAME = N'C:\MSSQLDATA\urlShortener.mdf' , SIZE = 2048KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB ) LOG ON ( NAME = N'urlShortener_log', FILENAME = N'C:\MSSQLDATA\urlShortener_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 2048KB ) GO ALTER DATABASE [urlShortener] SET COMPATIBILITY_LEVEL = 100 GO IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled')) begin EXEC [urlShortener].[dbo].[sp_fulltext_database] @action = 'enable' end GO ALTER DATABASE [urlShortener] SET ANSI_NULL_DEFAULT OFF GO ALTER DATABASE [urlShortener] SET ANSI_NULLS OFF GO ALTER DATABASE [urlShortener] SET ANSI_PADDING OFF GO ALTER DATABASE [urlShortener] SET ANSI_WARNINGS OFF GO ALTER DATABASE [urlShortener] SET ARITHABORT OFF GO ALTER DATABASE [urlShortener] SET AUTO_CLOSE OFF GO ALTER DATABASE [urlShortener] SET AUTO_CREATE_STATISTICS ON GO ALTER DATABASE [urlShortener] SET AUTO_SHRINK OFF GO ALTER DATABASE [urlShortener] SET AUTO_UPDATE_STATISTICS ON GO ALTER DATABASE [urlShortener] SET CURSOR_CLOSE_ON_COMMIT OFF GO ALTER DATABASE [urlShortener] SET CURSOR_DEFAULT GLOBAL GO ALTER DATABASE [urlShortener] SET CONCAT_NULL_YIELDS_NULL OFF GO ALTER DATABASE [urlShortener] SET NUMERIC_ROUNDABORT OFF GO ALTER DATABASE [urlShortener] SET QUOTED_IDENTIFIER OFF GO ALTER DATABASE [urlShortener] SET RECURSIVE_TRIGGERS OFF GO ALTER DATABASE [urlShortener] SET DISABLE_BROKER GO ALTER DATABASE [urlShortener] SET AUTO_UPDATE_STATISTICS_ASYNC OFF GO ALTER DATABASE [urlShortener] SET DATE_CORRELATION_OPTIMIZATION OFF GO ALTER DATABASE [urlShortener] SET TRUSTWORTHY OFF GO ALTER DATABASE [urlShortener] SET ALLOW_SNAPSHOT_ISOLATION OFF GO ALTER DATABASE [urlShortener] SET PARAMETERIZATION SIMPLE GO ALTER DATABASE [urlShortener] SET READ_COMMITTED_SNAPSHOT OFF GO ALTER DATABASE [urlShortener] SET HONOR_BROKER_PRIORITY OFF GO ALTER DATABASE [urlShortener] SET READ_WRITE GO ALTER DATABASE [urlShortener] SET RECOVERY SIMPLE GO ALTER DATABASE [urlShortener] SET MULTI_USER GO ALTER DATABASE [urlShortener] SET PAGE_VERIFY CHECKSUM GO ALTER DATABASE [urlShortener] SET DB_CHAINING OFF GO
Código S2: Criar Table urls
USE [urlShortener] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[urls]( [ID] [uniqueidentifier] NOT NULL, [shortURL] [varchar](25) NULL, [targetURL] [varchar](255) NULL, [hitCount] [int] NULL, [dateCreated] [datetime] NULL, [isActive] [bit] NULL, CONSTRAINT [PK_urls] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO SET ANSI_PADDING OFF GO ALTER TABLE [dbo].[urls] ADD CONSTRAINT [DF_urls_ID] DEFAULT (newid()) FOR [ID] GO ALTER TABLE [dbo].[urls] ADD CONSTRAINT [DF_urls_hitCount] DEFAULT ((0)) FOR [hitCount] GO ALTER TABLE [dbo].[urls] ADD CONSTRAINT [DF_urls_dateCreated] DEFAULT (getdate()) FOR [dateCreated] GO ALTER TABLE [dbo].[urls] ADD CONSTRAINT [DF_urls_isActive] DEFAULT ((1)) FOR [isActive] GO
Código S3: Stored Procedure setShortURL
USE [urlShortener] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author: Ricardo Parente -- Create date: 2010-10-09 -- Description: I try to add a new URL -- ============================================= CREATE PROCEDURE [dbo].[setShortUrl] @arg_shortURL varchar(25) , @arg_targetURL varchar(255) AS BEGIN SET NOCOUNT ON; -- check if targetURL already exists IF (NOT EXISTS ( SELECT TOP 1 shortURL FROM urls WHERE targetURL = @arg_targetURL)) BEGIN -- since targetURL does not exist, let's check if the -- shortURL already exists IF (NOT EXISTS ( SELECT TOP 1 shortURL FROM urls WHERE shortURL = @arg_shortURL)) BEGIN -- since the shortURL and targetURL do not exist, -- let's add them INSERT INTO urls ( shortURL , targetURL ) VALUES ( @arg_shortURL , @arg_targetURL ) -- return the newly created shortURL SELECT @arg_shortURL AS shortURL END ELSE -- shortURL already exists and it is not for the --given target URL, so return blank SELECT NULL AS shortURL END ELSE -- targetURL already exists, so return its shortURL SELECT TOP 1 shortURL FROM urls WHERE targetURL = @arg_targetURL END GO
Código S4: Stored Procedure getShortURL
USE [urlShortener] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author: Ricardo Parente -- Create date: 2010-10-10 -- Description: I retrieve one record -- ============================================= CREATE PROCEDURE [dbo].[getShortURL] @arg_shortURL varchar(25) AS BEGIN SET NOCOUNT ON; UPDATE urls SET hitCount = hitCount + 1 WHERE shortURL = @arg_shortURL SELECT TOP 1 targetURL FROM urls WHERE shortURL = @arg_shortURL END
Código CF1: Aplication.cfc
<cfcomponent output="false"> <cfscript> this.name = "urlShortener"; this.applicationTimeout = createTimeSpan(0,6,0,0); this.sessionManagement = false; this.clientManagement = false; </cfscript> <cffunction name="onApplicationStart" returntype="boolean" output="false"> <cfscript> application.mainDSN = "urlShortener"; application.emailFrom = "[email protected]"; application.emailAdmin = "[email protected]"; application.totChars = 6; </cfscript> <cfreturn True/> </cffunction> <cffunction name="onApplicationEnd" output="false"> <cfargument name="applicationScope" required="true" /> </cffunction> <cffunction name="onRequestStart"> <cfargument type="String" name="targetPage" required="true" /> <cfparam name="url.appInit" type="string" default="false" /> <cfparam name="url.serverInit" type="string" default="false" /> <cfscript> if (url.appInit eq true or url.serverInit eq true) onApplicationStart(); </cfscript> </cffunction> </cfcomponent>
Código CF2: shortener.cfc
<cfcomponent output="false"> <cfscript> variables.aChars = listToArray("a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9", " "); </cfscript> <cffunction name="setShort" access="remote" output="false" returntype="Any"> <cfargument name="targetURL" type="string" required="true" /> <cfset var myShort = "" /> <cfset var totChars = application.totChars /> <cfset var shortURL = "" /> <!--- try up to 10 times to avoid duplicate shortURL ---> <cfloop from="1" to="10" index="x"> <cfset myShort = createShort() /> <cfset shortURL = saveShort(myShort,arguments.targetURL) /> <cfif len(trim(shortURL))> <cfbreak /> </cfif> </cfloop> <cfif not len(trim(shortURL))> <!--- try again with more characters ---> <cfset totChars = totChars + 1 /> <cfloop from="1" to="10" index="x"> <cfset myShort = createShort(totChars) /> <cfset shortURL = saveShort(myShort,arguments.targetURL) /> <cfif len(trim(shortURL))> <cfbreak /> </cfif> </cfloop> </cfif> <cfreturn shortURL /> </cffunction> <cffunction name="saveShort" access="private" output="false" returntype="Any"> <cfargument name="shortURL" type="string" required="true" /> <cfargument name="targetURL" type="string" required="true" /> <cfset var myShortURL = "" /> <cftry> <!--- this procedure will save the url and return the shorURL generated ---> <cfstoredproc procedure="dbo.setShortURL" datasource="#application.mainDSN#"> <cfprocparam cfsqltype="CF_SQL_VARCHAR" value="#arguments.shortURL#" /> <cfprocparam cfsqltype="CF_SQL_VARCHAR" value="#arguments.targetURL#" /> <cfprocresult name="qShort" /> </cfstoredproc> <cfset myShortURL = qShort.shortURL /> <cfcatch> <cfmail from="#application.emailFrom#" To="#application.emailAdmin#" subject="Error on SetShort function" Type="html"> <h1>Error trying to set short URL</h1> <cfdump var="#cfcatch#"> </cfmail> <cfset myShortURL = "" /> </cfcatch> </cftry> <cfreturn myShortURL /> </cffunction> <cffunction name="createShort" access="private" output="false" returntype="Any"> <cfargument name="totChars" type="numeric" required="false" default="#application.totChars#" /> <cfscript> var shortUrl = ""; for (i=1; i lte arguments.totChars; i=i+1) { shortUrl = shortUrl & variables.aChars[randRange(1,arrayLen(variables.aChars))]; } return shortUrl; </cfscript> </cffunction> <!--- function set to remote to be called as webservice ---> <cffunction name="getShortURL" access="remote" output="false" returntype="Any"> <cfargument name="targetURL" type="string" required="true" /> <cfreturn setShort(arguments.targetURL) /> </cffunction> </cfcomponent>
Código CF3: index.cfm
<cfparam name="url.site" type="string" default="" /> <cfparam name="url.go" type="string" default="" /> <!--- let's check if we are in development server or localhost ---> <cfif listFindNoCase("localhost,127.0.0.1", cgi.host_name)> <cfset baseHost = "http://localhost/" /> <cfelse> <cfset baseHost = "http://lk3.us/" /> </cfif> <!--- if a target URL was passed, we need to create the short URL ---> <cfif len(trim(url.site))> <cfset short = createObject("component", "shorten") /> <cfif left(url.site, 7) neq 'http://'> <cfset url.site = "http://" & url.site /> </cfif> <cfset shortURL = short.setShort(url.site) /> <cfif len(trim(shortURL))> <cfset shortURL = baseHost & shortURL /> <cfoutput> The short URL for: #url.site# is: <br/> <a href="#shortURL#">#shortURL#</a> </cfoutput> <!--- if any error occurred during the creation of the short URL ---> <cfelse> <cfoutput> Sorry! We are experiencing problems now. Please try again later ! </cfoutput> </cfif> <cfabort /> <!--- check if a short URL was passed, then get the target and jump to it ---> <cfelseif len(trim(url.go))> <cftry> <cfstoredproc procedure="dbo.getShortURL" datasource="#application.mainDSN#"> <cfprocparam cfsqltype="CF_SQL_VARCHAR" value="#url.go#" /> <cfprocresult name="get" /> </cfstoredproc> <cfcatch> <cfoutput> Sorry ! Short URL unavailable !<br/> #cfcatch.message#<br/> #cfcatch.detail# </cfoutput> <cfabort /> </cfcatch> </cftry> <!--- jump to the target ---> <cfheader statuscode="301" statustext="Moved Permanently" /> <cfheader name="Location" value="#get.targetURL#" /> <cfelse> Please verify the query string. It should have either "site" or "go" parameters. </cfif>