Criando um URL Shortener com ColdFusion
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.
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 = "process@zzz.com";
application.emailAdmin = "webmaster@zzz.com";
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>
0 responses to “Criando um URL Shortener com ColdFusion”