Eventos
Campeonato Mundial de Visualização de Dados do Power BI
14 de fev., 16 - 31 de mar., 16
Com 4 chances de participar, você pode ganhar um pacote de conferência e chegar à Grande Final AO VIVO em Las Vegas
Saiba maisNão há mais suporte para esse navegador.
Atualize o Microsoft Edge para aproveitar os recursos, o suporte técnico e as atualizações de segurança mais recentes.
Por Fiyaz Hasan
Aviso
Os recursos descritos neste artigo estão obsoletos desde o ASP.NET Core 3.0. Um mecanismo de integração de estruturas SPA mais simples está disponível no pacote NuGet Microsoft.AspNetCore.SpaServices.Extensions. Para obter mais informações, consulte [Comunicado] Microsoft.AspNetCore.SpaServices e Microsoft.AspNetCore.NodeServices obsoletos.
Um SPA (Aplicativo de Página Única) é um tipo popular de aplicativo Web devido à sua experiência de usuário avançada inerente. A integração de estruturas ou bibliotecas SPA do lado do cliente, como Angular ou React, com estruturas do lado do servidor, como ASP.NET Core, pode ser difícil. Os Serviços JavaScript foram desenvolvidos para reduzir o atrito no processo de integração. Eles permitem uma operação contínua entre as diferentes pilhas de tecnologia de cliente e servidor.
Os Serviços JavaScript são uma coleção de tecnologias do lado do cliente para ASP.NET Core. Sua meta é tornar o ASP.NET Core a plataforma preferencial do lado do servidor dos desenvolvedores para a criação de SPAs.
Os Serviços JavaScript consistem em dois pacotes NuGet distintos:
Esses pacotes são úteis nos seguintes cenários:
Este artigo foca, principalmente, no uso do pacote SpaServices.
O SpaServices foi criado para tornar o ASP.NET Core a plataforma preferencial do lado do servidor dos desenvolvedores para a criação de SPAs. O SpaServices não é necessário para desenvolver SPAs com ASP.NET Core e não bloqueia os desenvolvedores em uma estrutura de cliente específica.
O SpaServices fornece uma infraestrutura útil, como:
Coletivamente, esses componentes de infraestrutura aprimoram o fluxo de trabalho de desenvolvimento e a experiência de runtime. Os componentes podem ser adotados individualmente.
Para trabalhar com o SpaServices, instale o seguinte:
Node.js (versão 6 ou posterior) com npm
Para verificar se esses componentes estão instalados e podem ser encontrados, execute o seguinte na linha de comando:
node -v && npm -v
Se estiver implantando em um site do Azure, nenhuma ação é necessária, poisNode.js está instalado e disponível nos ambientes do servidor.
SDK 2.0 ou posterior do .NET Core
Pacote NuGet Microsoft.AspNetCore.SpaServices
Um aplicativo universal (também conhecido como isomórfico) é um aplicativo JavaScript capaz de ser executado no servidor e no cliente. Angular, React e outras estruturas populares fornecem uma plataforma universal para esse estilo de desenvolvimento de aplicativos. A ideia é primeiro renderizar os componentes da estrutura no servidor por meio de Node.jse, em seguida, delegar a execução adicional ao cliente.
Os Auxiliares de Marcas do ASP.NET Core fornecidos pelo SpaServices simplificam a implementação da pré-renderização do lado do servidor invocando as funções JavaScript no servidor.
Instale o pacote npm aspnet-prerendering:
npm i -S aspnet-prerendering
Os Auxiliares de Marcas são descobertos por meio do registro de namespace no arquivo _ViewImports.cshtml
do projeto:
@using SpaServicesSampleApp
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
@addTagHelper "*, Microsoft.AspNetCore.SpaServices"
Esses Auxiliares de Marcas abstraem a complexidade da comunicação direta com APIs de baixo nível, aproveitando uma sintaxe semelhante a HTML dentro da exibição Razor:
<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
O Helper de Tag asp-prerender-module
, usado no exemplo de código anterior, executa ClientApp/dist/main-server.js
no servidor por meio de Node.js. Para fins de clareza, o arquivo main-server.js
é um artefato da tarefa de transpilação TypeScript para JavaScript no processo de build do Webpack. O Webpack define um alias de ponto de entrada de main-server
; e, a passagem do grafo de dependência para esse alias começa no arquivo ClientApp/boot-server.ts
:
entry: { 'main-server': './ClientApp/boot-server.ts' },
No exemplo angular a seguir, o arquivo ClientApp/boot-server.ts
utiliza a função createServerRenderer
e RenderResult
tipo do pacote npm aspnet-prerendering
para configurar a renderização do servidor por meio de Node.js. A marcação HTML destinada à renderização do lado do servidor é passada para uma chamada de função resolve, que é encapsulada em um objeto JavaScript Promise
fortemente tipado. A significância do objeto Promise
é que ele fornece de forma assíncrona a marcação HTML para a página para injeção no elemento de espaço reservado do DOM.
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
export default createServerRenderer(params => {
const providers = [
{ provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } },
{ provide: 'ORIGIN_URL', useValue: params.origin }
];
return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
const appRef = moduleRef.injector.get(ApplicationRef);
const state = moduleRef.injector.get(PlatformState);
const zone = moduleRef.injector.get(NgZone);
return new Promise<RenderResult>((resolve, reject) => {
zone.onError.subscribe(errorInfo => reject(errorInfo));
appRef.isStable.first(isStable => isStable).subscribe(() => {
// Because 'onStable' fires before 'onError', we have to delay slightly before
// completing the request in case there's an error to report
setImmediate(() => {
resolve({
html: state.renderToString()
});
moduleRef.destroy();
});
});
});
});
});
Quando combinado com o Auxiliar de Marcas asp-prerender-module
, o Auxiliar de Marcas asp-prerender-data
pode ser usado para passar informações contextuais da exibição Razor para o JavaScript do lado do servidor. Por exemplo, a marcação a seguir passa os dados do usuário para o módulo main-server
:
<app asp-prerender-module="ClientApp/dist/main-server"
asp-prerender-data='new {
UserName = "John Doe"
}'>Loading...</app>
O argumento UserName
recebido é serializado usando o serializador JSON interno e é armazenado no objeto params.data
. No exemplo de Angular a seguir, os dados são usados para construir uma saudação personalizada dentro de um elemento h1
:
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
export default createServerRenderer(params => {
const providers = [
{ provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } },
{ provide: 'ORIGIN_URL', useValue: params.origin }
];
return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
const appRef = moduleRef.injector.get(ApplicationRef);
const state = moduleRef.injector.get(PlatformState);
const zone = moduleRef.injector.get(NgZone);
return new Promise<RenderResult>((resolve, reject) => {
const result = `<h1>Hello, ${params.data.userName}</h1>`;
zone.onError.subscribe(errorInfo => reject(errorInfo));
appRef.isStable.first(isStable => isStable).subscribe(() => {
// Because 'onStable' fires before 'onError', we have to delay slightly before
// completing the request in case there's an error to report
setImmediate(() => {
resolve({
html: result
});
moduleRef.destroy();
});
});
});
});
});
Os nomes de propriedade passados em Auxiliares de Marcas são representados com notação PascalCase. Contraste isso com JavaScript, em que os mesmos nomes de propriedade são representados com camelCase. A configuração de serialização JSON padrão é responsável por essa diferença.
Para expandir o exemplo de código anterior, os dados podem ser passados do servidor para a exibição hidratando a propriedade globals
fornecida para a função resolve
:
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
export default createServerRenderer(params => {
const providers = [
{ provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } },
{ provide: 'ORIGIN_URL', useValue: params.origin }
];
return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
const appRef = moduleRef.injector.get(ApplicationRef);
const state = moduleRef.injector.get(PlatformState);
const zone = moduleRef.injector.get(NgZone);
return new Promise<RenderResult>((resolve, reject) => {
const result = `<h1>Hello, ${params.data.userName}</h1>`;
zone.onError.subscribe(errorInfo => reject(errorInfo));
appRef.isStable.first(isStable => isStable).subscribe(() => {
// Because 'onStable' fires before 'onError', we have to delay slightly before
// completing the request in case there's an error to report
setImmediate(() => {
resolve({
html: result,
globals: {
postList: [
'Introduction to ASP.NET Core',
'Making apps with Angular and ASP.NET Core'
]
}
});
moduleRef.destroy();
});
});
});
});
});
A matriz postList
definida dentro do objeto globals
é anexada ao objeto global window
do navegador. Essa variável de içamento para o escopo global elimina a duplicação de esforço, especialmente porque se refere ao carregamento dos mesmos dados no servidor e novamente no cliente.
O Middleware de Desenvolvimento do Webpack apresenta um fluxo de trabalho de desenvolvimento simplificado pelo qual o Webpack cria recursos sob demanda. O middleware compila e atende automaticamente aos recursos do lado do cliente quando uma página é recarregada no navegador. A abordagem alternativa é invocar manualmente o Webpack por meio do script de build npm do projeto quando uma dependência de terceiros ou o código personalizado é alterado. Um script de build npm no arquivo package.json
é exibido no exemplo a seguir:
"build": "npm run build:vendor && npm run build:custom",
Instale o pacote npm aspnet-webpack:
npm i -D aspnet-webpack
O Middleware de Desenvolvimento do Webpack é registrado no pipeline de solicitação HTTP por meio do seguinte código no método Startup.cs
do arquivo Configure
:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
// Call UseWebpackDevMiddleware before UseStaticFiles
app.UseStaticFiles();
O método de extensão UseWebpackDevMiddleware
deve ser chamado antes de registrar a hospedagem de arquivo estático por meio do método de extensão UseStaticFiles
. Por motivos de segurança, registre o middleware somente quando o aplicativo for executado no modo de desenvolvimento.
A propriedade webpack.config.js
do arquivo output.publicPath
instrui o middleware a inspecionar a pasta dist
para alterações:
module.exports = (env) => {
output: {
filename: '[name].js',
publicPath: '/dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix
},
Pense no recurso HMR (Substituição de Módulo Frequente) do Webpack como uma evolução do Middleware de Desenvolvimento do Webpack. O HMR apresenta os mesmos benefícios, mas simplifica ainda mais o fluxo de trabalho de desenvolvimento atualizando automaticamente o conteúdo da página depois de compilar as alterações. Não confunda isso com uma atualização do navegador, o que interferiria no estado atual na memória e na sessão de depuração do SPA. Há um link dinâmico entre o serviço do Middleware de Desenvolvimento do Webpack e o navegador, o que significa que as alterações são enviadas por push para o navegador.
Instale o pacote npm webpack-hot-middleware:
npm i -D webpack-hot-middleware
O componente HMR deve ser registrado no pipeline de solicitação HTTP do MVC no método Configure
:
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
HotModuleReplacement = true
});
Como foi true com o Middleware de Desenvolvimento do Webpack, o método de extensão UseWebpackDevMiddleware
deve ser chamado antes do método de extensão UseStaticFiles
. Por motivos de segurança, registre o middleware somente quando o aplicativo for executado no modo de desenvolvimento.
O arquivo webpack.config.js
deve definir uma matriz plugins
, mesmo que ela seja deixada vazia:
module.exports = (env) => {
plugins: [new CheckerPlugin()]
Depois de carregar o aplicativo no navegador, a guia Console das ferramentas de desenvolvedor fornece a confirmação da ativação do HMR:
Na maioria dos SPAs baseados em ASP.NET Core, o roteamento do lado do cliente geralmente é desejado, além do roteamento do lado do servidor. Os sistemas de roteamento SPA e MVC podem funcionar de forma independente sem interferência. No entanto, há um caso de borda que apresenta desafios: identificar respostas para HTTP 404.
Considere o cenário no qual uma rota sem extensão de /some/page
é usada. Suponha que o padrão da solicitação não corresponda a uma rota do lado do servidor, mas sim a uma rota do lado do cliente. Agora considere uma solicitação de entrada para /images/user-512.png
, que geralmente espera encontrar um arquivo de imagem no servidor. Se esse caminho de recurso solicitado não corresponder a nenhuma rota do lado do servidor ou arquivo estático, é improvável que o aplicativo do lado do cliente o manipule, geralmente retornando um código de status HTTP 404.
Instale o pacote npm de roteamento do lado do cliente. Usando Angular como exemplo:
npm i -S @angular/router
Um método de extensão chamado MapSpaFallbackRoute
é usado no método Configure
:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
As rotas são avaliadas na ordem em que estão configuradas. Consequentemente, a rota default
no exemplo de código anterior é usada primeiro para padrões correspondentes.
Os Serviços JavaScript fornecem modelos de aplicativo pré-configurados. O SpaServices é usado nesses modelos em conjunto com diferentes estruturas e bibliotecas, como Angular, React e Redux.
Esses modelos podem ser instalados por meio da CLI do .NET executando o seguinte comando:
dotnet new --install Microsoft.AspNetCore.SpaTemplates::*
Uma lista de modelos SPA disponíveis é exibida:
Modelos | Nome curto | Idioma | Marcações |
---|---|---|---|
ASP.NET Core MVC com Angular | angular | [C#] | Web/MVC/SPA |
ASP.NET Core MVC com React.js | react | [C#] | Web/MVC/SPA |
MVC ASP.NET Core com React.js e Redux | reactredux | [C#] | Web/MVC/SPA |
Para criar um novo projeto usando um dos modelos de SPA, inclua o Nome Curto do modelo no comando dotnet new. O comando a seguir cria um aplicativo Angular com ASP.NET Core MVC configurado para o lado do servidor:
dotnet new angular
Existem dois modos de configuração de runtime primários:
O ASP.NET Core usa uma variável de ambiente chamada ASPNETCORE_ENVIRONMENT
para armazenar o modo de configuração. Para obter mais informações, confira Configurar o ambiente.
Restaure os pacotes NuGet e npm necessários executando o seguinte comando na raiz do projeto:
dotnet restore && npm i
Crie e execute o aplicativo:
dotnet run
O aplicativo começa no localhost de acordo com o modo de configuração de runtime. O acesso a http://localhost:5000
no navegador exibe a página de aterrissagem.
Abra o arquivo .csproj
gerado pelo comando dotnet new. Os pacotes NuGet e npm necessários são restaurados automaticamente após a abertura do projeto. Esse processo de restauração pode levar até alguns minutos e o aplicativo está pronto para ser executado quando for concluído. Clique no botão de execução verde ou pressione Ctrl + F5
e o navegador será aberto na página de aterrissagem do aplicativo. O aplicativo começa no localhost de acordo com o modo de configuração de runtime.
Os modelos do SpaServices são pré-configurados para executar testes do lado do cliente usando Karma e Jasmine. Jasmine é uma estrutura de teste de unidade popular para JavaScript, enquanto Karma é um executor de teste para esses testes. O Karma está configurado para trabalhar com o Middleware de Desenvolvimento do Webpack de modo que o desenvolvedor não seja obrigado a parar e executar o teste sempre que forem feitas alterações. Seja o código em execução no caso de teste ou o caso de teste em si, o teste será executado automaticamente.
Usando o aplicativo Angular como exemplo, dois casos de teste de Jasmine já são fornecidos para o CounterComponent
no arquivo counter.component.spec.ts
:
it('should display a title', async(() => {
const titleText = fixture.nativeElement.querySelector('h1').textContent;
expect(titleText).toEqual('Counter');
}));
it('should start with count 0, then increments by 1 when clicked', async(() => {
const countElement = fixture.nativeElement.querySelector('strong');
expect(countElement.textContent).toEqual('0');
const incrementButton = fixture.nativeElement.querySelector('button');
incrementButton.click();
fixture.detectChanges();
expect(countElement.textContent).toEqual('1');
}));
Abra o prompt de comando no diretório ClientApp. Execute o comando a seguir:
npm test
O script inicia o executor de teste Karma, que lê as configurações definidas no arquivo karma.conf.js
. Entre outras configurações, o karma.conf.js
identifica os arquivos de teste a serem executados por meio de sua matriz files
:
module.exports = function (config) {
config.set({
files: [
'../../wwwroot/dist/vendor.js',
'./boot-tests.ts'
],
Consulte este problema do GitHub para obter mais informações sobre como publicar no Azure.
Pode ser difícil combinar os ativos gerados do lado do cliente e os artefatos de ASP.NET Core publicados em um pacote pronto para implantação. Felizmente, o SpaServices orquestra todo esse processo de publicação com um destino personalizado do MSBuild nomeado RunWebpack
:
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />
<Exec Command="node node_modules/webpack/bin/webpack.js --env.prod" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="wwwroot\dist\**; ClientApp\dist\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
O destino do MSBuild tem as seguintes responsabilidades:
O destino do MSBuild é invocado ao executar:
dotnet publish -c Release
Comentários do ASP.NET Core
O ASP.NET Core é um projeto código aberto. Selecione um link para fornecer comentários:
Eventos
Campeonato Mundial de Visualização de Dados do Power BI
14 de fev., 16 - 31 de mar., 16
Com 4 chances de participar, você pode ganhar um pacote de conferência e chegar à Grande Final AO VIVO em Las Vegas
Saiba maisTreinamento
Módulo
Publish an ASP.NET Core app - Training
Learn how to publish an ASP.NET Core app for deployment to a web server or cloud service.
Certificação
Microsoft Certified: Azure Developer Associate - Certifications
Crie soluções de ponta a ponta no Microsoft Azure para criar Funções do Azure, implementar e gerenciar aplicativos Web, desenvolver soluções utilizando o Armazenamento do Microsoft Azure e muito mais.