Introdução
Passados mais epende 13 anos sobre o lançamento do ASP.NET, a Microsoft decidiu começar do zero… ou quase. Com efeito, com o ASP.NET 5 vamos assistir a uma revolução no desenvolvimento para a web, que irá levar a uma reaprendizagem de todo o processo de desenvolvimento.
Um Bocado de História
Quando o ASP.NET foi lançado, em 2002, a designação Web Forms era quase um pleonasmo: basicamente, não havia outro modelo de desenvolvimento para a web na framework .NET. A ideia da Microsoft era reproduzir, tanto quanto possível, em ambiente web o modelo de desenvolvimento já existente para o Windows:
- Designer visual de páginas e controlos (drag and drop);
- Modelo de eventos;
- Manutenção automática de estado.
Em grande parte, estes objectivos foram bem-sucedidos: era possível a uma pessoa com relativamente poucos conhecimentos de desenvolvimento produzir aplicações ricas com controlos visuais e acesso a dados de forma rápida. Muitos fabricantes de software começaram a produzir bibliotecas de controlos e frameworks de desenvolvimento sobre Web Forms, permitindo a construção facilitada de ricas interfaces gráficas com funcionalidades avançadas, quase ao nível do que é possível encontrar em aplicações Windows Forms e Windows Presentation Foundation (WPF). A boa integração dos vários produtos da Microsoft com o Visual Studio deixou muita gente satisfeita durante bastante tempo. O SharePoint, o servidor colaborativo “de bandeira” da Microsoft, usa Web Forms, pelo que quem desenvolve para SharePoint tem obrigatoriamente de conhecer a framework.
Algumas versões notáveis do ASP.NET:
- 1.0: versão inicial (Visual Studio .NET, Visual Studio 2003);
- 2: introdução do mecanismo de providers (fornecedores de funcionalidades); disponibilização das ASP.NET AJAX Extensions; adaptadores de CSS configuráveis (Visual Studio 2005);
- 3.5: integração das ASP.NET AJAX Extensions; novos controlos ListView e DataPager; LinqDataSource e EntityDataSource; routing; disponibilização do modelo MVC (Visual Studio 2008);
- 4.0: páginas assíncronas; integração dos controlos gráficos ASP.NET Chart Controls; transformações do Web.config; mais fornecedores de funcionalidades (codificação de respostas, cache); novo modelo de validação de pedidos; inclusão do jQuery e do Modernizr; suporte a Content Delivery Networks (CDNs); view state opcional por página e controlo; geração de identificadores no lado do cliente configurável; possibilidade de endereçar diferentes versões do .NET; geração de HTML optimizado; disponibilização do ASP.NET Dynamic Data (Visual Studio 2010);
- 4.5: binding model para controlos de exibição e inserção de dados; empacotamento (bundling) e minificação integrada; optimizações na garbage collection; suporte a mais funcionalidades do HTML 5; suporte a WebSockets; integração da biblioteca Microsoft AntiXSS; integração do WebAPI; integração do SignalR; integração de formas de autenticação sociais, como Facebook, Twitter, etc (Visual Studio 2012, Visual Studio 2013).
Entretanto, sobre Web Forms foram sido construídas outras frameworks: XML Web Services e Web Services Enhancements (para a versão 1.0), ASP.NET AJAX Extensions (2), ASP.NET Provider Model (2), Dynamic Data (3.5), Routing (3.5), SignalR (4.5), para citar apenas algumas, e a própria base foi evoluindo, tornando-se mais extensível e completa, com um mecanismo que permite trocar grande parte das funcionalidades nativas. A versão mais recente é a 4.5.2.
Críticas ao Modelo Web Forms
Grande parte das críticas ao modelo Web Forms assenta essencialmente nos seguintes aspectos:
- A web é complexa e o modelo Web Forms esconde essa complexidade (manutenção de estado, actualizações parciais, etc);
- A utilização do designer visual para produzir funcionalidades conceptualmente complexas pode levar a uma má separação de código, onde a própria página chega a conter código SQL ou LINQ;
- A existência do view state e a dependência que certos controlos têm dessa funcionalidade, que por vezes faz com que as páginas fiquem extremamente “pesadas”, contendo grandes quantidades de dados que, de forma invisível, atrasam a sua submissão;
- A dependência de uma biblioteca JavaScript embutida para algumas das suas funcionalidades (ASP.NET AJAX Library);
- A complexidade/pouca qualidade do código gerado automaticamente pelos seus controlos: até há bem pouco tempo estes geravam elementos TABLE e outro conteúdo HTML que actualmente é considerado má prática;
- O modelo “same-page” por omissão leva a que uma só página contenha muita lógica, porque a página faz submissões para si própria e tem de considerar várias acções possíveis aquando do processamento dos eventos;
- O ciclo de vida de uma página e dos seus controlos é complexo; há certas coisas que têm de ser feitas em (ou até) determinado evento e por vezes é difícil sincronizar as dependências entre os vários controlos e a própria página (“event hell”);
- Monolítico e pesado; as novas versões apenas são distribuídas com a própria framework .NET, o que não acontece tão frequentemente como isso; além disso, para usar uma qualquer funcionalidade, temos de trazer várias dependências atrás;
- Apesar da adição tardia de routing, as páginas Web Forms não são geralmente amigas de Search Engine Optimization (SEO), dependendo de URLs relativamente crípticos (ex:
/Categorias.aspx?ID=b1aee664-aa95-44bb-a0c8-45a567b56919
, por oposição a/Categoria/Smartphone
).
ASP.NET MVC
O modelo de desenvolvimento MVC foi lançado em 2009 com o objectivo de fornecer uma alternativa “oficial” a quem não gostasse do Web Forms. Como grandes vantagens, propunha:
- A familiaridade de um design pattern, Model-View-Controller (MVC), usado noutras linguagens e frameworks de desenvolvimento para a web (Java, PHP);
- Uma distinção clara entre as várias camadas de desenvolvimento, conducente a uma melhor separação de responsabilidades, que eventualmente poderá levar a código mais fácil de manter e evoluir;
- O regresso ao HTTP e HTML: o programador passa a ter de considerar questões como os verbos HTTP, os URLs e a gerar ele próprio o HTML dos conteúdos, o que lhe dá mais controlo; a necessidade de produzir HTML levou a uma maior utilização de frameworks JavaScript;
- O modelo de rotas leva a URLs mais “amigos” de REST e de SEO, tópicos “quentes” actualmente;
- Mais facilidade em testar o código por meio de testes unitários;
- A disponibilização de actualizações “out-of-band”, ou seja, não coincidentes com as actualizações da framework .NET, levando a que possam ser mais frequentes;
- Elevada extensibilidade: abraçando conceitos modernos, tais como Dependency Injection (DI) e Inversion of Control (IoC), é possível substituir ou complementar grande parte dos seus mecanismos internos (validação, autenticação, autorização, logging, etc). Praticamente todas as funcionalidades são extensíveis.
O modelo MVC tornou-se muito popular. Apesar de ainda não poder rivalizar com o Web Forms nalguns aspectos – falta de designer visual, menor capacidade de reutilização de controlos – rapidamente se tornou a framework de escolha para muitos programadores com tecnologias Microsoft, descontentes com o modelo anterior. A própria Microsoft pareceu empurrar nessa direcção, incluindo até o apoio a projectos construídos sobre MVC, externos (Orchard, por exemplo) ou internos (Web API, Web Pages, Razor) e à especificação OWIN. Como prova dessa evolução, podemos ver que passou da versão 1, em 2009, para a versão 5 em 2013, com vários lançamentos pelo meio, estando actualmente na 5.2.3.
OWIN e Open Source
Numa tentativa de “democratizar” o ASP.NET, levando-o a outros sistemas operativos, juntamente com o resto da família .NET, a Microsoft libertou grande parte do código fonte como open source, sob o licenciamento Microsoft Public License (MS-PL). Adicionalmente, tem vindo a trabalhar numa especificação que define o fluxo de processamento de um pedido pelo servidor e como o código .NET se pode integrar com um servidor HTTP que não exclusivamente o IIS: é o standard Open Web Integration for .NET (OWIN). O problema é que OWIN, neste momento, não se integra verdadeiramente com Web Forms, embora seja possível usá-los em conjunto, de forma a suportar componentes que dependam de OWIN num cenário Web Forms, mas a framework Web Forms propriamente dita não o usa, ao contrário da MVC. Todo o desenvolvimento continua a assentar na velha pipeline ASP.NET e nas bibliotecas System.Web.DLL e System.Web.Extensions.DLL.
ASP.NET vNext
Na realidade o que temos em cima da mesa é não uma mas duas versões do ASP.NET:
- 4.6: trata-se da evolução natural da versão actual do ASP.NET; inclui alguns melhoramentos em Web Forms e MVC e integra várias correcções de segurança entretanto lançadas;
- 5: reescrita total do ASP.NET.
Foquemo-nos no ASP.NET 5. Esta nova framework – pois é disto que estamos a falar – vai funcionar sobre o .NET 5. As suas principais características vão ser:
- Totalmente open source sob a licença MIT; serão aceites contributos da comunidade, como já agora acontece;
- Fim do modelo de desenvolvimento Web Forms; não será incluído com o ASP.NET 5 qualquer classe de suporte ao Web Forms;
- C# e VB serão suportados, apesar de inicialmente a Microsoft ter anunciado que não existiria suporte inicial para VB;
- Baseado em OWIN, sendo que a framework “natural” de desenvolvimento será MVC e Razor, mas será alojável em vários servidores, desde o IIS até o novo Kestrel, desenvolvido de raiz para Mac e Linux, passando por correr num processo .NET;
- Por assentar em .NET 5, será implicitamente multi-plataforma, devendo correr em Windows, Mac e Linux, com suporte a versões limitadas do .NET (.NET Core CLR) a pensar na cloud, e podendo usar simultaneamente componentes de várias versões do .NET;
- Será modular e suportado em packages NuGet: as novas versões serão distribuídas “out-of-band” sob a forma de packages NuGet; os programadores poderão escolher apenas aquelas de que necessitam; estas serão actualizadas em ciclos próprios, não ditados pelas novas versões da framework .NET;
- Unificação dos APIs MVC, Web API e Web Pages, para evitar duplicação e fornecer um modelo coerente e coeso, sobre a pipeline especificada pelo OWIN;
- Totalmente extensível por meio de DI e IoC, sendo fornecido um contentor próprio, que pode ser substituído por um mais tradicional (AutoFac, Unity, Ninject, etc);
- Compilação dinâmica: deixa de ser necessário compilar o código explicitamente para experimentar as alterações, estas são detectadas automaticamente e compiladas pelo novo compilador Roslyn;
- Novas ferramentas de linha de comandos com nomes estranhos: DNX, DNVM e DNU;
- Integração com bibliotecas populares de desenvolvimento e gestão de dependências JavaScript, como Bower, Grunt, Gulp e NPM;
- Gestão de dependências de bibliotecas .NET por meio de packages NuGet e suas versões em ficheiros JSON;
- Suporte ao HTTP 2.0, quando a correr sobre o Windows 10 ou superior.
Reconhecendo os problemas actuais – código monolítico, muito agarrado e com muitas dependências, obrigatoriedade de respeitar os ciclos mais lentos de lançamento da framework .NET, grande disparidade e duplicação de funcionalidades entre o MVC, Web API e Web Pages, a Microsoft decidiu começar de raiz e reescrever a framework a partir do zero. O novo ambiente de desenvolvimento integrado (IDE) será o Visual Studio 2015 e irá suportar quer a nova framework baseada em .NET 5 quer as anteriores.
Exemplos
Ao criar um novo projecto web com o Visual Studio 2015 RC somos confrontados com as seguintes opções:
Um projecto ASP.NET 5 consiste numa pasta com um ficheiro project.json
:
Dentro deste, temos algo como:
{ "webroot": "wwwroot", "userSecretsId": "aspnet5-WebApplication1-108e0908-d4ac-4341-920e-ce646e4c2b33", "version": "1.0.0-*", "dependencies": { "Microsoft.AspNet.Mvc": "6.0.0-beta4", "Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-beta4", "Microsoft.AspNet.Diagnostics": "1.0.0-beta4", "Microsoft.AspNet.Diagnostics.Entity": "7.0.0-beta4", "Microsoft.AspNet.Server.IIS": "1.0.0-beta4", "Microsoft.AspNet.Server.WebListener": "1.0.0-beta4", "Microsoft.AspNet.StaticFiles": "1.0.0-beta4", "Microsoft.AspNet.Tooling.Razor": "1.0.0-beta4", "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-beta4", "Microsoft.Framework.ConfigurationModel.UserSecrets": "1.0.0-beta4", "Microsoft.Framework.CodeGenerators.Mvc": "1.0.0-beta4", "Microsoft.Framework.Logging": "1.0.0-beta4", "Microsoft.Framework.Logging.Console": "1.0.0-beta4", "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-beta4" }, "commands": { "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000", "gen": "Microsoft.Framework.CodeGeneration", }, "frameworks": { "dnx451": { }, "dnxcore50": { } }, "exclude": [ "wwwroot", "node_modules", "bower_components" ], "publishExclude": [ "node_modules", "bower_components", "**.xproj", "**.user", "**.vspscc" ], "scripts": { "postrestore": [ "npm install", "bower install" ], "prepare": [ "gulp copy" ] } }
São perceptíveis os seguintes elementos:
- Informação genérica do projecto:
webroot
,userSecretsId
,version
;userSecretsId
contém um identificador único da aplicação usado para localizar um ficheiro de configuração pessoal por utilizador; - Dependências NuGet:
dependencies
; - Comandos:
commands
; neste exemplo, o comandoweb
despoleta o servidor HTTP WebListener; - Frameworks suportadas:
frameworks
; - Pastas e ficheiros excluídos do projecto (por omissão, todos os ficheiros dentro de uma pasta estão incluídos):
exclude
; - Pastas e ficheiros excluídos da publicação:
publishExclude
; - Scripts executáveis:
scripts
. São visíveis elementos do Bower, NPM e Gulp.
Não iremos percorrer todos estes elementos, a maior parte deles é facilmente compreensível. É interessante ver que o ficheiro possui IntelliSense:
Ao adicionar uma dependência, o Visual Studio detecta a alteração e faz o seu download:
Não existem mais os ficheiros Web.config
e Global.asax/Global.asax.cs
: a configuração é agora extensível sendo por omissão suportados ficheiros JSON e as variáveis de ambiente, e a pipeline do projecto é inteiramente OWIN. A classe que por convenção permite configurar a aplicação chama-se Startup
:
public class Startup { public Startup(IHostingEnvironment env) { // Definir a fonte da configuração a partir de um ficheiro e das variáveis de ambiente var configuration = new Configuration() .AddJsonFile("config.json") .AddUserSecrets() .AddEnvironmentVariables(); // Guardar a configuração numa propriedade local this.Configuration = configuration; } public IConfiguration Configuration { get; set; } public void ConfigureServices(IServiceCollection services) { // Adicionar um serviço com scope do pedido HTTP actual services.AddScoped<IService, ServiceImplementation>()); // Adicionar um serviço a ser criado em cada pedido por ele services.AddTransient<IService, ServiceImplementation>(); // Adicionar um serviço singleton services.AddSingleton<IService>(new ServiceImplementation()); // Adicionar MVC ao contentor de IoC e DI services.AddMvc(); // Descomentar para adicionar Web API // services.AddWebApiConventions(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory) { // Configurar o logging loggerfactory.AddConsole(minLevel: LogLevel.Warning); // Se estivermos em ambiente de desenvolvimento, mostrar erros if (env.IsEnvironment("Development")) { app.UseBrowserLink(); app.UseErrorPage(ErrorPageOptions.ShowAll); app.UseDatabaseErrorPage(DatabaseErrorPageOptions.ShowAll); } else { // Caso contrário, redireccionar para este controlador e acção no caso de erros app.UseErrorHandler("/Home/Error"); } // Servir ficheiros estáticos app.UseStaticFiles(); // Configurar rotas MVC app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index" }); // Descomentar para configurar rotas Web API // routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}"); }); } }
Os três métodos chamados por convenção são:
Startup
: permite definir opções de alojamento do serviço HTTP e definições globais;ConfigureServices
: adicionar ou substituir serviços no contentor de IoC e DI, com uma de três longevidades possíveis:- Singleton: apenas existe uma instância, esta é sempre devolvida;
- Scoped: é criada uma instância, caso não exista, por pedido HTTP, após o que, dentro do mesmo pedido, é sempre devolvida a instância criada;
- Transient (default): é criada uma nova instância do serviço sempre que for pedida ao contentor de IoC.
Configure
: outros aspectos da configuração, já usando os serviços definidos (rotas, logging, etc), adicionar middleware à pipeline
Nota: alguns nomes da especificação OWIN foram alterados no ASP.NET 5; o interface IAppBuilder passou para IApplicationBuilder, por exemplo.
O registo de middleware – adicionar funcionalidades à pipeline HTTP – é tipicamente feito no método Configure
:
// Adicionar um componente à pipeline de execução app.UseMiddleware<MeasureMiddleware>();
Um exemplo de um componente OWIN, que vem substituir os módulos e handlers HTTP (IHttpModule e IHttpHandler) será:
public class MeasureMiddleware { private readonly RequestDelegate _next; public MeasureMiddleware(RequestDelegate next) { this._next = next; } public async Task Invoke(HttpContext context) { var sw = Stopwatch.StartNew(); await this._next(context); var ext = Path.GetExtension(context.Request.Path.Value); if ((context.Response.StatusCode == (Int32) HttpStatusCode.OK) && (String.IsNullOrWhiteSpace(ext) == true)) { var milliseconds = sw.ElapsedMilliseconds; await context.Response.WriteAsync($"Processado em {milliseconds} milisegundos"); } } }
Este componente faz uso do padrão Chain of Responsibility para invocar o componente anterior (middleware) na pipeline (recebido no construtor), medindo essa execução e fazendo output do tempo que demorou. Tem acesso ao contexto de execução, incluindo todos os detalhes do pedido, e pode adicionar conteúdo às respostas.
A configuração global num ficheiro JSON (config.json
) fica assim:
{ "AppSettings": { "SiteTitle": "WebApplication1" }, "Data": { "DefaultConnection": { "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet5-WebApplication1-108e0908-d4ac-4341-920e-ce646e4c2b33;Integrated Security=SSPI;MultipleActiveResultSets=true" } } }
No nosso exemplo, estamos a combinar configuração específica do projecto (AddJsonFile
) com variáveis de ambiente do utilizador (AddEnvironmentVariables
) e também com um ficheiro de configuração pessoal do utilizador, o qual não é mantido na mesma pasta do projecto, mas numa pasta específica do utilizador (%AppData%\Roaming\Microsoft\UserSecrets\<nome da aplicação>\secrets.json
, por omissão).
Outra diferença importante é que a raiz do servidor HTTP, a partir da qual são servidos os ficheiros estáticos, está situada numa pasta chamada, por omissão, wwwroot
(configurada no project.json
), e não na pasta do projecto propriamente dito:
Também o Bower e o NPM merecem um tratamento especial, exibindo o Visual Studio uma vista sobre o conteúdo dos ficheiros de configuração bower.json
, package.json
e gulpfile.js
:
As dependências também provêm directamente do ficheiro project.json
, da sua entrada frameworks:
Já os controladores e as vistas, bem como as respectivas rotas, localizações e convenções de acesso, permanecem idênticas aos das versões anteriores do MVC, pelo que não serão cobertos aqui. Destaco apenas três novidades: apenas são suportadas vistas Razor e os controladores são partilhados por MVC e Web API, devendo retornar implementações de IActionResult
, interface que é comum aos resultados de controladores MVC e Web API. A terceira são na verdade duas formas server-side de gerar conteúdos HTML. A primeira chama-se tag helpers, e é uma forma de definir tags cujos atributos e conteúdo, pelo menos parcialmente, são gerados por código .NET. Estes tag helpers são definidos em classes .NET que herdam de TagHelper
:
[TargetElement("square")] public class SquareTagHelper : TagHelper { [HtmlAttributeName("asp-side")] public Int32 ? Side { get; set; } [HtmlAttributeName("asp-background-color")] public String BackgroundColor { get; set; } [HtmlAttributeName("asp-foreground-color")] public String ForegroundColor { get; set; } [HtmlAttributeName("asp-content")] public String Content { get; set; } public override void Process(TagHelperContext context, TagHelperOutput output) { if ((this.Side != null) && (this.BackgroundColor != null)) { //define a tag de output output.TagName = "div"; //adiciona atributos à tag output.Attributes["style"] = $"width: {Side}px; height: {Side}px; color: {ForegroundColor}; background-color: {BackgroundColor}; text-align: center; vertical-align: middle; line-height: {Side}px;"; //adiciona conteúdo output.Content.SetContent(this.Content); } base.Process(context, output); } }
E registados em vistas Razor, por exemplo, _GlobalImport.cshtml
, por forma a estarem acessíveis em todas as vistas:
@using MyNamespace @using MyNamespace.Models @addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers" @addTagHelper "*, MyNamespace"
A instrução addTagHelper
torna disponível todas as tag helpers disponíveis numa assembly .NET. Após este registo, podemos usar a nova tag square
:
<square asp-side="200" asp-foreground-color="white" asp-background-color="blue" asp-content="Hello, World!"/>
Alguns leitores poderão notar semelhanças com o modelo de controlos server-side do ASP.NET Web Forms.
O segundo novo API chama-se view components. Aqui a ideia é ter um mecanismo semelhante às partial views, simplesmente, sem designer, ou seja, todo o conteúdo é produzido por código. Vejamos um exemplo de um view component, uma classe que herda de ViewComponent
:
[ViewComponent(Name = "Sum")] public class AddViewComponent : ViewComponent { public IViewComponentResult Invoke(int a, int b) { var result = a + b; return this.View(result); } }
E eis como o utilizar numa vista Razor:
@Component.Invoke("Sum", 1, 2)
O primeiro parâmetro de Invoke
é o nome do view component, tal como definido pelo atributo ViewComponentAttribute
, caso exista, ou então o nome da classe sem o sufixo ViewComponent
. Seguidamente vão quaisquer parâmetros, que irão ser passados ao método Invoke
. Poderão existir vários overloads deste método, recebendo diferentes parâmetros e é também possível invocar o view component assincronamente.
Com estes dois mecanismos, tag helpers e view components, torna-se mais fácil a invocação de código server-side, e o que não é de somenos importância, a reutilização, já que estes componentes podem existir em assemblies externas para as quais foram adicionadas referências.
Novos Ferramentas da Linha de Comandos
À boleia do .NET 5 vem um conjunto de comandos novo; entre outras novidades, passa a ser possível gerir packages NuGet e correr aplicações ASP.NET sem usar o Visual Studio. Os três novos comandos com os quais teremos de nos familiarizar são, muito resumidamente:
- DNVM: .NET Version Manager, anteriormente chamado KVM; configura a versão do .NET do projecto;
- DNX: .NET Execution Environment, anteriormente K, KLR e KRE; permite configurar o ambiente do .NET em uso pelo projecto, de entre .NET Framework (default), .NET Core (subset optimizado para cloud) e Mono (para Linux e Mac);
- DNU: .NET Development Utilities, antigo KPM; utilitários para gerir as packages NuGet, criar packages e publicar a aplicação.
Estes comandos integram-se com a nova estrutura de projectos .NET (project.json
). Alguns exemplos:
Para o DNU, os comandos mais típicos serão:
dnu restore
: obter todas as packages NuGet especificadas no projecto que estejam em falta;dnu publish
: preparar a aplicação para deployment;dnu wrap
: converter um projecto .csproj em json;dnu build
: compilar o projecto.
Os comandos DNX, DNVM e DNU actuam com base e sobre o projecto actual, que pode ser ASP.NET 5 ou outro .NET 5.
Conclusão
Como o ASP.NET 5, bem como o 4.6, ainda não foram lançados, só nos resta especular. É possível que alguém, ou mesmo a própria Microsoft, venha a implementar Web Forms sobre o ASP.NET 5. Certo parece ser um cada vez maior impulso na direcção do Azure, multi-plataforma e também do open source. Durante algum tempo ainda teremos a família 4.x, com suporte a código legado, mas é de prever que esta seja descontinuada num futuro próximo. Esperemos pela versão final do Visual Studio 2015 e ASP.NET 5, que deverá ser lançada ainda este ano, e, entretanto, estejamos atentos às novidades que vão sendo apresentadas.
Referências
- Site oficial da família ASP.NET: http://www.asp.net
- Introduction to ASP.NET 5: http://docs.asp.net/en/latest/conceptual-overview/aspnet.html
- Updates for ASP.NET 4.6 – Web Forms/ MVC 5/ Web API 2: http://blogs.msdn.com/b/webdev/archive/2015/04/30/updates-for-asp-net-4-6-web-forms-mvc-5-web-api-2.aspx
- New ASP.NET Features and Fixes in Visual Studio 2015 RC: http://blogs.msdn.com/b/webdev/archive/2015/04/29/new-asp-net-features-and-fixes-in-visual-studio-2015-rc.aspx
- The MIT License: https://github.com/dotnet/corefx/blob/master/LICENSE
- Entrada ASP.NET na Wikipedia: http://en.wikipedia.org/wiki/ASP.NET
- Entrada ASP.NET MVC Framework na Wikipedia: http://en.wikipedia.org/wiki/ASP.NET_MVC_Framework
- Entrada Model-view-controller na Wikipedia: http://en.wikipedia.org/wiki/Model–view–controller
- Repositório ASP.NET no GitHub: https://github.com/aspnet/
- ASP.NET vNext: http://www.asp.net/vnext
- OWIN: Open Web Interface for .NET: http://owin.org
- Licenciamento Microsoft Public License (MS-PL): http://opensource.org/licenses/ms-pl.html
- Posts sobre ASP.NET no blogue do autor deste artigo (Development With A Dot): https://weblogs.asp.net/ricardoperes/Tags/ASP.NET
- Orchard: http://orchardproject.net/