Arquivo da tag: mvc

Criando um sistema básico de Short Url (parte 2)

Criando um sistema básico de Short Url (parte 2)

Criando um sistema básico de Short Url (parte 2)

Depois de vários meses usando o sistema e tendo que atualizar a lista de links manualmente no XML via FTP, decidi que era hora de melhorar o sistema para que incluísse um banco de dados e um meio de incluir, editar e remover links online.

Portanto, melhorei o sistema e hoje está disponível em http://puul.ga

 

O código fonte (open source) está disponível no Github

Foram feitas várias melhorias no sistema, de forma a ser responsivo e fácil de usar. Mas seu visual e utilidades continuam simples. Cria-se um short link (mesmo conceito que bit.ly) que pode ser compartilhado em qualquer lugar, deixando o link curto e fácil de ser digitado.

O projeto continua sendo em C#, MVC, Entity Framework, Html5, Css3 e jQuery (Bootstrap)

Primeiramente, é necessário criar o objeto/classe que irá conter os dados do Link gerado:

using System;
using System.ComponentModel.DataAnnotations;

namespace Shorten_Urls.Models
{
    public class Url
    {
        public int Id { get; set; }
        [RegularExpression(@"^.{8,}$", ErrorMessage = "Minimum 8 characters required")]
        [Required(ErrorMessage = "Required")]
        [StringLength(30, MinimumLength = 8, ErrorMessage = "Invalid")]
        [Display(Name ="Shareable Url")]
        public string Src { get; set; }
        [Url]
        [Display(Name = "Redirects To")]
        public string Redirect { get; set; }
        public string UserId { get; set; }
        [Display(Name = "Created By")]
        public string Username { get; set; }
        //[DataType(DataType.Date)]
        //[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
        [Display(Name = "Created On")]
        public DateTime CreatedOn { get; set; }
        //[DataType(DataType.Date)]
        //[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
        [Display(Name = "Expires")]
        public DateTime? Expires { get; set; }
    }
}

Também é necessário criar o Repositório (ou Data Layer). Esse repositório permite o uso tanto de XML quanto SQL, sendo configurada a opção no

web.config
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Microsoft.AspNet.Identity;
using System.Configuration;
using System.Data.Entity;

namespace Shorten_Urls.Models
{
    public class UrlRepository : IRepository<Url>
    {
        private XDocument _urls;
        private List<Url> urls;
        ApplicationDbContext _context;
        private readonly string DbProvider = ConfigurationManager.AppSettings["DbProvider"];
        private const string SQL = "Sql";
        private const string XML = "Xml";
        public bool needLastId { get { return DbProvider.Equals(XML); } }

        public List<Url> Urls { get { return urls; } }
        public int LastId
        { get { return urls.Last().Id; } }

        public UrlRepository()
        {
            urls = new List<Url>();

            if (DbProvider == XML)
            {
                _urls = MvcApplication.IO.Forwards;

                IEnumerable<XElement> rootDescendants = _urls.Descendants("url");
                foreach (XElement e in rootDescendants)
                    urls.Add(MapXElement(e));
            }
            else if (DbProvider == SQL)
            {
                _context = new ApplicationDbContext();
                urls = _context.Forwarders.ToList();
            }
        }

        public bool Exists(string src)
        {
            var yes = from y in urls where y.Src == src select y;
            if (yes.Count() > 0) return true;
            else return false;
        }

        public IEnumerable<Url> GetAll()
        {
            return urls;
        }

        public void Add(Url url)
        {
            urls.Add(url);
            if (DbProvider == XML)
                MvcApplication.IO.AddForward(url);
            else if (DbProvider == SQL)
            {
                _context.Forwarders.Add(url);
                _context.SaveChanges();
            }
        }
        public Url GetById(int Id)
        {
            Url e = (from url in urls where url.Id == Id select url).Single();
            if (DbProvider == SQL)
            {
                PopulateUsername(e);
            }
            return e;
        }
        public List<Url> GetByUserId(string userId)
        {
            List<Url> list = (from urlList in urls where urlList.UserId == userId select urlList).ToList();
            if (DbProvider == SQL)
            {
                foreach (Url url in list) PopulateUsername(url);
            }
            return list;
        }
        public Url GetBySrc(string src)
        {
            var e = (from url in urls where url.Src.Equals(src) select url);
            if (e.Count() == 0) return null;
            else {
                if (DbProvider == SQL)
                {
                    PopulateUsername(e.Single());
                }
                return e.Single();
            }
        }

        public void Remove(int id)
        {
            Url remove = (from url in urls where url.Id == id select url).Single();
            urls.Remove(remove);
            Remove(remove);
        }

        public void Remove(Url url)
        {
            urls.Remove(url);
            if (DbProvider == XML)
                MvcApplication.IO.RemoveForward(url);
            else if (DbProvider == SQL)
            {
                _context.Forwarders.Remove(url);
                _context.SaveChanges();
            }
        }

        public void Remove(string src)
        {
            Url remove = (from url in urls where url.Src == src select url).Single();
            urls.Remove(remove);
            Remove(remove);
        }

        public void RemoveAll()
        {
            urls.Clear();
        }

        public void RemoveByUserId(string userId)
        {
            Url remove = (from url in urls where url.UserId == userId select url).Single();
            urls.Remove(remove);
            Remove(remove);
        }

        public void Update(Url url)
        {
            Url old = (from urlE in urls where urlE.Id == url.Id select urlE).Single();
            urls.Remove(old);
            urls.Add(url);
            if (DbProvider == XML)
            {

                MvcApplication.IO.RemoveForward(old);
                MvcApplication.IO.AddForward(url);
            }
            else if (DbProvider == SQL)
            {
                _context.Entry(url).State = EntityState.Modified;
                _context.SaveChanges();
            }
        }


        private Url PopulateUsername(int id)
        {
            Url url = (from u in urls where u.Id == id select u).Single();
            string userId = url.UserId == null ? "" : url.UserId;
            UserRepository userRepo = new UserRepository();
            url.Username = userRepo.GetUsername(userId);
            return url;
        }
        private Url PopulateUsername(Url url)
        {
            UserRepository userRepo = new UserRepository();
            string userId = url.UserId == null ? "" : url.UserId;
            url.Username = userRepo.GetUsername(userId);
            return url;
        }
        private Url MapXElement(XElement element)
        {
            DateTime expires = element.Attribute("expires").Value == "" ? new DateTime(2999, 12, 31) : DateTime.Parse(element.Attribute("expires").Value);
            DateTime createdOn = element.Attribute("created").Value == "" ? DateTime.Now : DateTime.Parse(element.Attribute("created").Value);
            string userId = element.Attribute("userid").Value;
            int id = int.Parse(element.Attribute("id").Value);
            Url url = new Url
            {
                Id = id,
                CreatedOn = createdOn,
                Expires = expires,
                UserId = userId,
                Redirect = element.Value.Trim(),
                Src = element.Attribute("src").Value
            };
            return PopulateUsername(url);
        }
    }
}

A parte mais importante do projeto é o Controller que vai servir os Links, e as rotas de leitura dos links. Portanto, o

RouteConfig.cs

também tem que estar correto:

ForwardsController.cs
using Shorten_Urls.Models;
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Script.Serialization;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;

namespace Shorten_Urls.Controllers
{
    public class ForwardsController : Controller
    {
        private UrlRepository _repo;
        private UserRepository _usersRepo;
        private ApplicationUser user;

        public ForwardsController()
        {
            _repo = new UrlRepository();
            _usersRepo = new UserRepository();
        }
        //
        // GET: /Forwards/

        public ActionResult SendToSrc(string src)
        {
            string error404 = "404.html";
            string redirect = "/Home/Index";

            Url url = _repo.GetBySrc(src);
            if (url != null)
            {

                if (!url.Redirect.Equals(string.Empty) && (url.Expires > DateTime.Now || url.Expires == null))
                    redirect = url.Redirect;
                else
                    redirect = error404;
            }
            return Redirect(redirect);
        }
        [Authorize]
        public ActionResult List()
        {

            List<Url> urls;
            if (User != null)
                user = _usersRepo.GetById(User.Identity.GetUserId());

            if (user.UserType == UserTypes.Administrator)
                urls = _repo.Urls;
            else urls = _repo.GetByUserId(user.Id);

            return View(urls);
        }

        //
        // GET: /Forwards/Details/5
        [Authorize]

        public ActionResult Details(int id)
        {
            if (User != null)
                user = _usersRepo.GetById(User.Identity.GetUserId());

            if (id == 0)
                Redirect("/Home/Index");
            Url url = _repo.GetById(id);
            return View(url);
        }

        //
        // GET: /Forwards/Create
        [Authorize]

        public ActionResult Create()
        {
            if (User != null)
                user = _usersRepo.GetById(User.Identity.GetUserId());

            Url url = new Url();
            string random = Helpers.GenerateRandomgUrl();
            while (_repo.Exists(random))
            {
                random = Helpers.GenerateRandomgUrl();
            }
            ViewBag.RandomUrl = random;
            return View(url);
        }

        [HttpGet]
        public string Available(string Src)
        {
            bool exists = _repo.Exists(Src);
            return new JavaScriptSerializer().Serialize(!exists);
        }
        //
        // POST: /Forwards/Create

        [Authorize]
        [HttpPost]
        public ActionResult Create(string Redirect, string Src, DateTime? Expires)
        {
            try
            {
                // TODO: Add insert logic here
                if (User != null)
                    user = _usersRepo.GetById(User.Identity.GetUserId());

                Url newUrl = new Url
                {
                    Redirect = Redirect,
                    Src = Src,
                    CreatedOn = DateTime.Now,
                    Expires = Expires,
                    UserId = user.Id,
                    Username = _usersRepo.GetUsername(user.Id)
                };

                if (_repo.needLastId)
                    newUrl.Id = _repo.LastId + 1;

                _repo.Add(newUrl);
                TempData["Success"] = "Short Url has been created";
                return RedirectToAction("List");
            }
            catch
            {
                TempData["Error"] = "There was an error saving your new Url";
                return View("List");
            }
        }

        //
        // GET: /Forwards/Edit/5
        [Authorize]

        public ActionResult Edit(int id)
        {
            if (User != null)
                user = _usersRepo.GetById(User.Identity.GetUserId());

            Url editUrl = _repo.GetById(id);
            return View(editUrl);
        }

        //
        // POST: /Forwards/Edit/5
        [Authorize]
        [HttpPost]
        public ActionResult Edit(int Id, string Redirect, string Src, DateTime? Expires = null)
        {
            try
            {
                // TODO: Add update logic here
                if (User != null)
                    user = _usersRepo.GetById(User.Identity.GetUserId());

                Url editUrl = _repo.GetById(Id);
                editUrl.Redirect = Redirect;
                editUrl.Src = Src;
                editUrl.UserId = user.Id;
                editUrl.Expires = Expires;
                editUrl.Username = _usersRepo.GetUsername(user.Id);

                _repo.Update(editUrl);
                TempData["Success"] = "Short Url has been updated";
                return RedirectToAction("List");
            }
            catch
            {
                TempData["Error"] = "There was an error saving your Url";
                return View();
            }
        }

        //
        // GET: /Forwards/Delete/5
        [Authorize]
        public ActionResult Delete(int id)
        {
            if (User != null)
                user = _usersRepo.GetById(User.Identity.GetUserId());
            Url url = _repo.GetById(id);
            return View(url);
        }

        [Authorize]
        [HttpPost]
        public ActionResult Delete(int id, bool confirm)
        {
            try
            {
                if (User != null)
                    user = _usersRepo.GetById(User.Identity.GetUserId());

                _repo.Remove(id);
                TempData["Success"] = "Short Url has been deleted";
                return RedirectToAction("List");

            }
            catch
            {
                TempData["Error"] = "There was an error deleting your Url";
                Url url = _repo.GetById(id);
                return View(url);
            }
        }

    }
}
RouteConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace Shorten_Urls
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Forward",
                url: "{src}",
                defaults: new { controller = "Forwards", action = "SendToSrc", src = "" }
                );

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

Ao final, configura-se o

web.config

com as seguintes chaves:

 <appsettings>
    <add key="ForwardsFile" value="~/App_Data/forwards.xml"></add>
    <add key="LenghtOfRandomString" value="8"></add>
    <add key="WebsiteName" value="Puulga"></add>
    <add key="WebsiteUrl" value="http://puul.ga/"></add>
    <add key="Website" value="true"></add>
    <!--email provider section-->
    <add key="siteadmin" value="email@puul.ga"></add>
    <add key="email-subject" value="Email from Puul.ga"></add>
    <add key="email-from" value="email@puul.ga"></add>
    <add key="email-from-name" value="Puul.ga"></add>
    <add key="email-require-auth" value="false"></add>
    <add key="email-username" value="puul.ga"></add>
    <add key="email-password" value="password"></add>
    <add key="email-port" value="3535"></add>
    <add key="useSSL" value="false"></add>
    <add key="email-server" value="localhost"></add>
    <add key="email-html-share" value="~/Email Templates/share.html"></add>
    <add key="email-html-default" value="~/Email Templates/puulga.html"></add>
    <add key="email-html-forgotten" value="~/Email Templates/puulga.html"></add>
    <add key="email-html-confirmation" value="~/Email Templates/puulga.html"></add>
    <add key="email-html-welcome" value="~/Email Templates/puulga.html"></add>
    <!--Database options. possible values: Xml, Sql-->
    <add key="DbProvider" value="Sql"></add>
  </appsettings>

Basicamente, esses são os elementos mais importantes para o funcionamento do projeto. Todo o código fonte está disponível no Github em código aberto.

Visite o site para ver como funciona.

Criando um sistema básico de Short Url

Criando um sistema básico de Short Url

por Carlos Casalicchio

Mídias sociais são parte corriqueira da vida de qualquer usuário de computador, especialmente aqueles que são profissionais na área.

Por isso, tenho o costume de inserir links de várias páginas em minhas assinaturas de email. No entanto, serviços como Yahoo e Outlook (online) restringem a quantidade de caracteres, dificultando a criação de assinaturas completas.

Criando um sistema básico de Short Url

Para resolver esse problema, resolvi criar um Webservice simples que fosse capaz de redirecionar qualquer short link para o url completo da assinatura.

Esse artigo mostra como é fácil criar um serviço de short url usando ASP.NET MVC com C#

Inicialmente, criei a lista de short links num xml mesmo (no momento não senti necessidade de um gerenciador de short links):

Xml

<?xml version="1.0" encoding="utf-8" ?>
<forwards>
  <url src="6uzcQsmW" id="1" created="" expires="" userid="">
    https://www.facebook.com/carlos.casalicchio
  </url>
  <url src="g89Tsdxq" id="1" created="" expires="" userid="">
     </url>
  <url src="h9KyWTfu" id="1" created="" expires="" userid="">
    https://plus.google.com/u/0/+CarlosCasalicchio/posts
  </url>
  <url src="4R7oViGj" id="1" created="" expires="" userid="">
    http://www.linkedin.com/in/ccasalicchio
  </url>
  <url src="udVmFUV8" id="1" created="" expires="" userid="">
    http://www.youtube.com/user/casalicchio
  </url>
  <url src="iTSVtpg3" id="1" created="" expires="" userid="">
    https://www.ccasalicchio.com/
  </url>
  <url src="YcbmDf4s" id="1" created="" expires="" userid="">
    http://www.lastfm.com.br/user/c_casalicchio
  </url>
  <url src="HrRbfS7u" id="1" created="" expires="" userid="">
    https://profile.yahoo.com/COUAKGLAHFWMJCPD73Z4EK5PTA/
  </url>
<url src="sx2GkN7N" id="1" created="" expires="" userid="">
    http://www.mellaniealanny.info/
  </url>
</forwards>

Como é possível ver, já deixei preparado para futuros melhoramentos, mas por enquanto só o source e o url de destino são os dados que interessam.

Depois, criei só um método para a página inicial do site, dentro do ForwardController – afinal, quero os url o mais simples possível

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Xml.Linq;

namespace Short_Links.Controllers
{
    public class ForwardController : Controller
    {
        //
        // GET: /Forward/
        public ActionResult Index(string src)
        {
            var url = from e in MvcApplication.Forwards.Root.Descendants("url") where e.Attribute("src").Value == src select e;
            string redirect = "404.html";
            if (url.DescendantNodes().Count() > 0)
             redirect = url.DescendantNodes().Single().ToString().Trim();

            return Redirect(redirect);
        }

    }
}

O próximo problema é fixar o MVC Routing para aceitar links curtos. Para isso, modifiquei o RouteConfig.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace Short_Links
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                name: "Forward",
                url: "{src}",
                defaults: new { controller = "Forward", action = "Index", src="" }
                );
            //routes.MapRoute(
            //    name: "Default",
            //    url: "{controller}/{action}/{id}",
            //    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            //);
        }
    }
}

Como pode-se ver, os routings originais foram comentados, pois por enquanto o site não tem nenhuma outra função a não ser redirecionar short urls. Mas, para servir páginas normais é preciso ter outros routes.

A Última coisa feita foi definir uma variável global que contenha todos os short urls, que carregue com a aplicação. Dessa forma, evita-se ler o xml toda vez que um novo request entrar. Isso aumenta a performance do redirecionamento.

Note que só isso foi adicionado, o resto do Global.ascx não foi modificado:

using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using System.Xml.Linq;

namespace Short_Links
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode,
    // visit http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {
        public static XDocument Forwards {get;set;} 

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            //Load xml with all forwards
            string path =
                Server.MapPath("~/App_Data/forwards.xml");
            Forwards = XDocument.Load(path);
        }
    }
}

Mas o que fazer se alguém tentar acessar o site sem um short link? Para mitigar esse problema, criei uma página 404.html simples, que informa o usuário que ele não submeteu nenhum short link:

Html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Url Not Found</title>
    
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%3E%0A%20%20%20%20%20%20%20%20body%20%7Bwidth%3A100%25%3Btext-align%3Acenter%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20background%3A%20url('%2FImages%2Fgrumpy-cat-04.jpg')%20no-repeat%20top%20right%20%23000%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20background-size%3A%20100%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20font-family%3A'Segoe%20UI'%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A30px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20margin%3A0%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20padding%3A0%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20.not-found-body%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3Aabsolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20bottom%3A0%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3A100%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%3Aleft%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3A%23000%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20background-color%3A%23fff%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20opacity%3A0.6%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20box-shadow%3A%200%20-2px%202px%20%23000%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20text-shadow%3A%200%200%202px%20%23000%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20text-transform%3Auppercase%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20.not-found-body%20span%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A12px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;style&gt;" title="&lt;style&gt;" />

</head>

<body>
    
<div class="not-found-body">

<h3>Damn you 404!</h3>

    <span>You did not insert any short urls to be redirected...</span></div>

</body>
</html>

Em código é só isso! Subi o site publicado para o provedor e voilá, agora tenho um serviço de short urls.

Baixe o código fonte do projeto Short Links.

Outras sugestões são:

  • Compre um domínio curto. No meu caso, estou usando o http://puul.ga que foi comprado através do http://dot.tk
  • Meu provedor de hosting é o http://www.godaddy.com e apontei o DNS do domínio para o Godaddy.
  • Se for usar imagens, coloque-as numa pasta de nome curto – neste projeto nomeei a pasta como ‘i’ e deixei todas as imagens com nomes curtos também. Assim, se for referenciar suas imagens em outro lugar, o link também fica curto, como por exemplo https://puul.ga/i/face.png .

Servidor Portátil .NET

Servidor Portátil .NET

É possível rodar sites WebForms e MVC em um servidor portátil .NET

Esse servidor implementa melhorias em um projeto antigo chamado Cassini++  que permitia rodar sites .NET 2.0 sem o uso do IIS.

No entanto, o projeto foi abandonado e há alguns substitutos, como o UltiDev, que permitem que se rode instancias .NET sem o uso de servidores completos.

O projeto Portable ASP.NET Web Server é aberto e permite rodar sem nenhuma instalação, ou seja, é possível rodar sites direto de um flash drive (pendrive), CDs, DVDs, etc.

Também permite salvar configurações e editar as configurações padrão, para que inicie automaticamente, quando instanciado.

Online Servidor Portátil .NET offline Servidor Portátil .NET

Faça seu download  aqui