Tutorial: Creación e implementación de una aplicación con un servicio de front-end de ASP.NET Core Web API y un servicio back-end con estadoTutorial: Create and deploy an application with an ASP.NET Core Web API front-end service and a stateful back-end service

Este tutorial es la primera parte de una serie.This tutorial is part one of a series. Aprenda a crear una aplicación de Azure Service Fabric con un front-end de ASP.NET Core Web API y un servicio back-end con estado para almacenar los datos.You will learn how to create an Azure Service Fabric application with an ASP.NET Core Web API front end and a stateful back-end service to store your data. Cuando termine, tendrá una aplicación de votación con un front-end web de ASP.NET Core que guarda los resultados de una votación en un servicio back-end con estado en el clúster.When you're finished, you have a voting application with an ASP.NET Core web front-end that saves voting results in a stateful back-end service in the cluster. Si no desea crear manualmente la aplicación de votación, puede descargar el código fuente de la aplicación terminada y pasar directamente al Tutorial de la aplicación de ejemplo de votación.If you don't want to manually create the voting application, you can download the source code for the completed application and skip ahead to Walk through the voting sample application. Si lo prefiere, también puede ver un vídeo de este tutorial.If you prefer, you can also watch a video walk-through of this tutorial.

Front-end de la API AngularJS+ASP.NET, conexión a un servicio de back-end con estado en Service Fabric

En la primera parte de la serie, se aprende a:In part one of the series, you learn how to:

  • Crear un servicio de ASP.NET Core Web API como un servicio de confianza con estadoCreate an ASP.NET Core Web API service as a stateful reliable service
  • Crear un servicio de ASP.NET Core Web Application como un servicio de confianza sin estadoCreate an ASP.NET Core Web Application service as a stateless web service
  • Usar el proxy inverso para comunicarse con el servicio con estadoUse the reverse proxy to communicate with the stateful service

En esta serie de tutoriales, se aprende a:In this tutorial series you learn how to:

requisitos previosPrerequisites

Antes de empezar este tutorial:Before you begin this tutorial:

Creación de un servicio de ASP.NET Web API como un servicio de confianzaCreate an ASP.NET Web API service as a reliable service

En primer lugar, cree el front-end web de la aplicación de votación mediante ASP.NET Core.First, create the web front-end of the voting application using ASP.NET Core. ASP.NET Core es un marco de desarrollo web ligero multiplataforma, que puede usar para crear modernas interfaces de usuario web y API web.ASP.NET Core is a lightweight, cross-platform web development framework that you can use to create modern web UI and web APIs. Para obtener una descripción completa de cómo se integra ASP.NET Core con Service Fabric, se recomienda fehacientemente leer el artículo ASP.NET Core en Reliable Services de Service Fabric.To get a complete understanding of how ASP.NET Core integrates with Service Fabric, we strongly recommend reading through the ASP.NET Core in Service Fabric Reliable Services article. De momento, puede seguir este tutorial para empezar a trabajar rápidamente.For now, you can follow this tutorial to get started quickly. Para más información sobre ASP.NET Core, vea Documentación de ASP.NET Core.To learn more about ASP.NET Core, see the ASP.NET Core Documentation.

  1. Inicie Visual Studio como administrador.Launch Visual Studio as an administrator.

  2. Cree un proyecto con Archivo->Nuevo->Proyecto.Create a project with File->New->Project.

  3. En el cuadro de diálogo Nuevo proyecto, elija Nube > Aplicación de Service Fabric.In the New Project dialog, choose Cloud > Service Fabric Application.

  4. Asigne el nombre Voting a la aplicación y haga clic en Aceptar.Name the application Voting and click OK.

    Cuadro de diálogo de proyecto nuevo en Visual Studio

  5. En la página Nuevo servicio de Service Fabric, elija ASP.NET Core sin estado, asigne el nombre de VotingWeb al servicio y haga clic en Aceptar.On the New Service Fabric Service page, choose Stateless ASP.NET Core, name your service VotingWeb, then click OK.

    Selección del servicio web ASP.NET en el cuadro de diálogo de nuevo servicio

  6. En la página siguiente se proporciona un conjunto de plantillas de proyecto ASP.NET Core.The next page provides a set of ASP.NET Core project templates. Para este tutorial, elija Aplicación web (controlador de vista de modelos) y haga clic en Aceptar.For this tutorial, choose Web Application (Model-View-Controller), then click OK.

    Elección del tipo de proyecto ASP.NET

    Visual Studio crea una aplicación y un proyecto de servicio, y los muestra en el Explorador de soluciones.Visual Studio creates an application and a service project and displays them in Solution Explorer.

    Explorador de soluciones después de la creación de una aplicación con el servicio ASP.NET Core Web API

Actualización del archivo site.jsUpdate the site.js file

Abra wwwroot/js/site.js.Open wwwroot/js/site.js. Reemplace el contenido por el código de JavaScript siguiente que usan las vistas de inicio y guarde los cambios.Replace its contents with the following JavaScript used by the Home views, then save your changes.

var app = angular.module('VotingApp', ['ui.bootstrap']);
app.run(function () { });

app.controller('VotingAppController', ['$rootScope', '$scope', '$http', '$timeout', function ($rootScope, $scope, $http, $timeout) {

    $scope.refresh = function () {
        $http.get('api/Votes?c=' + new Date().getTime())
            .then(function (data, status) {
                $scope.votes = data;
            }, function (data, status) {
                $scope.votes = undefined;
            });
    };

    $scope.remove = function (item) {
        $http.delete('api/Votes/' + item)
            .then(function (data, status) {
                $scope.refresh();
            })
    };

    $scope.add = function (item) {
        var fd = new FormData();
        fd.append('item', item);
        $http.put('api/Votes/' + item, fd, {
            transformRequest: angular.identity,
            headers: { 'Content-Type': undefined }
        })
            .then(function (data, status) {
                $scope.refresh();
                $scope.item = undefined;
            })
    };
}]);

Actualización del archivo Index.cshtmlUpdate the Index.cshtml file

Abra el archivo Views/Home/Index.cshtml, la vista específica para el controlador de inicio.Open Views/Home/Index.cshtml, the view specific to the Home controller. Reemplace su contenido por el siguiente y guarde los cambios.Replace its contents with the following, then save your changes.

@{
    ViewData["Title"] = "Service Fabric Voting Sample";
}

<div ng-controller="VotingAppController" ng-init="refresh()">
    <div class="container-fluid">
        <div class="row">
            <div class="col-xs-8 col-xs-offset-2 text-center">
                <h2>Service Fabric Voting Sample</h2>
            </div>
        </div>

        <div class="row">
            <div class="col-xs-8 col-xs-offset-2">
                <form class="col-xs-12 center-block">
                    <div class="col-xs-6 form-group">
                        <input id="txtAdd" type="text" class="form-control" placeholder="Add voting option" ng-model="item"/>
                    </div>
                    <button id="btnAdd" class="btn btn-default" ng-click="add(item)">
                        <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
                        Add
                    </button>
                </form>
            </div>
        </div>

        <hr/>

        <div class="row">
            <div class="col-xs-8 col-xs-offset-2">
                <div class="row">
                    <div class="col-xs-4">
                        Click to vote
                    </div>
                </div>
                <div class="row top-buffer" ng-repeat="vote in votes.data">
                    <div class="col-xs-8">
                        <button class="btn btn-success text-left btn-block" ng-click="add(vote.key)">
                            <span class="pull-left">
                                {{vote.key}}
                            </span>
                            <span class="badge pull-right">
                                {{vote.value}} Votes
                            </span>
                        </button>
                    </div>
                    <div class="col-xs-4">
                        <button class="btn btn-danger pull-right btn-block" ng-click="remove(vote.key)">
                            <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
                            Remove
                        </button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

Actualización del archivo _Layout.cshtmlUpdate the _Layout.cshtml file

Abra el archivo Views/Shared/_Layout.cshtml, el diseño predeterminado para la aplicación ASP.NET.Open Views/Shared/_Layout.cshtml, the default layout for the ASP.NET app. Reemplace su contenido por el siguiente y guarde los cambios.Replace its contents with the following, then save your changes.

<!DOCTYPE html>
<html ng-app="VotingApp" xmlns:ng="https://angularjs.org">
<head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>@ViewData["Title"]</title>

    <link href="~/lib/bootstrap/dist/css/bootstrap.css" rel="stylesheet"/>
    <link href="~/css/site.css" rel="stylesheet"/>

</head>
<body>
<div class="container body-content">
    @RenderBody()
</div>

<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.5.0/ui-bootstrap-tpls.js"></script>
<script src="~/js/site.js"></script>

@RenderSection("Scripts", required: false)
</body>
</html>

Actualización del archivo VotingWeb.csUpdate the VotingWeb.cs file

Abra el archivo VotingWeb.cs, que permite crear el elemento WebHost de ASP.NET Core dentro del servicio sin estado con el servidor web de WebListener.Open the VotingWeb.cs file, which creates the ASP.NET Core WebHost inside the stateless service using the WebListener web server.

Agregue la directiva using System.Net.Http; en la parte superior del archivo.Add the using System.Net.Http; directive to the top of the file.

Reemplace la función CreateServiceInstanceListeners() por el código siguiente y guarde los cambios.Replace the CreateServiceInstanceListeners() function with the following code, then save your changes.

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new ServiceInstanceListener[]
    {
        new ServiceInstanceListener(
            serviceContext =>
                new KestrelCommunicationListener(
                    serviceContext,
                    "ServiceEndpoint",
                    (url, listener) =>
                    {
                        ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");

                        return new WebHostBuilder()
                            .UseKestrel()
                            .ConfigureServices(
                                services => services
                                    .AddSingleton<HttpClient>(new HttpClient())
                                    .AddSingleton<FabricClient>(new FabricClient())
                                    .AddSingleton<StatelessServiceContext>(serviceContext))
                            .UseContentRoot(Directory.GetCurrentDirectory())
                            .UseStartup<Startup>()
                            .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                            .UseUrls(url)
                            .Build();
                    }))
    };
}

Agregue también el método GetVotingDataServiceName siguiente debajo de CreateServiceInstanceListeners() y guarde los cambios.Also add the following GetVotingDataServiceName method below CreateServiceInstanceListeners(), then save your changes. GetVotingDataServiceName devuelve el nombre del servicio cuando se sondea.GetVotingDataServiceName returns the service name when polled.

internal static Uri GetVotingDataServiceName(ServiceContext context)
{
    return new Uri($"{context.CodePackageActivationContext.ApplicationName}/VotingData");
}

Incorporación del archivo VotesController.csAdd the VotesController.cs file

Agregue un controlador para definir las acciones de votación.Add a controller, which defines voting actions. Haga clic con el botón derecho en la carpeta Controladores y seleccione Agregar->Nuevo elemento->Visual C#->ASP.NET Core->Clase.Right-click on the Controllers folder, then select Add->New item->Visual C#->ASP.NET Core->Class. Asigne al archivo el nombre VotesController.cs y haga clic en Agregar.Name the file VotesController.cs, then click Add.

Reemplace el contenido del archivo VotesController.cs por el siguiente y guarde los cambios.Replace the VotesController.cs file contents with the following, then save your changes. Más adelante, en Actualización del archivo VotesController.cs, este archivo se modifica para leer y escribir los datos de votación desde el servicio back-end.Later, in Update the VotesController.cs file, this file is modified to read and write voting data from the back-end service. Por ahora, el controlador devuelve datos de cadena estática a la vista.For now, the controller returns static string data to the view.

namespace VotingWeb.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Fabric;
    using System.Fabric.Query;
    using System.Linq;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Text;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Newtonsoft.Json;

    [Produces("application/json")]
    [Route("api/Votes")]
    public class VotesController : Controller
    {
        private readonly HttpClient httpClient;

        public VotesController(HttpClient httpClient)
        {
            this.httpClient = httpClient;
        }

        // GET: api/Votes
        [HttpGet]
        public async Task<IActionResult> Get()
        {
            List<KeyValuePair<string, int>> votes= new List<KeyValuePair<string, int>>();
            votes.Add(new KeyValuePair<string, int>("Pizza", 3));
            votes.Add(new KeyValuePair<string, int>("Ice cream", 4));

            return Json(votes);
        }
     }
}

Configuración del puerto de escuchaConfigure the listening port

Cuando se crea el servicio front-end VotingWeb, Visual Studio selecciona aleatoriamente un puerto en el que el servicio realiza la escucha.When the VotingWeb front-end service is created, Visual Studio randomly selects a port for the service to listen on. Dado que el servicio VotingWeb actúa como front-end para esta aplicación y acepta tráfico externo, queremos enlazar dicho servicio a un puerto fijo y conocido.The VotingWeb service acts as the front-end for this application and accepts external traffic, so let's bind that service to a fixed and well-know port. El manifiesto del servicio declara los puntos de conexión de servicio.The service manifest declares the service endpoints.

En el Explorador de soluciones, abra VotingWeb/PackageRoot/ServiceManifest.xml.In Solution Explorer, open VotingWeb/PackageRoot/ServiceManifest.xml. Busque el elemento Endpoint (Punto de conexión) en la sección Resources (Recursos) y cambie el valor del puerto a 8080.Find the Endpoint element in the Resources section and change the Port value to 8080. Para implementar y ejecutar la aplicación localmente, el puerto de escucha de la aplicación debe estar abierto y disponible en el equipo.To deploy and run the application locally, the application listening port must be open and available on your computer.

<Resources>
    <Endpoints>
      <!-- This endpoint is used by the communication listener to obtain the port on which to 
           listen. Please note that if your service is partitioned, this port is shared with 
           replicas of different partitions that are placed in your code. -->
      <Endpoint Protocol="http" Name="ServiceEndpoint" Type="Input" Port="8080" />
    </Endpoints>
  </Resources>

Actualice también el valor de la propiedad de dirección URL de la aplicación en el proyecto Voting para que un explorador web se abra en el puerto correcto al depurar la aplicación.Also update the Application URL property value in the Voting project so a web browser opens to the correct port when you debug your application. En el Explorador de soluciones, seleccione el proyecto Voting y actualice la propiedad dirección URL de la aplicación a 8080.In Solution Explorer, select the Voting project and update the Application URL property to 8080.

Implementación y ejecución local de la aplicación VotingDeploy and run the Voting application locally

Ya puede continuar y ejecutar la aplicación Voting para depuración.You can now go ahead and run the Voting application for debugging. En Visual Studio, presione F5 para implementar la aplicación en el clúster local de Service Fabric en modo de depuración.In Visual Studio, press F5 to deploy the application to your local Service Fabric cluster in debug mode. La aplicación presentará un error si no abrió Visual Studio anteriormente como administrador.The application will fail if you didn't previously open Visual Studio as administrator.

Nota

La primera vez que ejecute e implemente la aplicación localmente, Visual Studio creará un clúster local de Service Fabric para la depuración.The first time you run and deploy the application locally, Visual Studio creates a local Service Fabric cluster for debugging. Es posible que la creación del clúster tarde un tiempo.Cluster creation may take some time. El estado de creación del clúster se muestra en la ventana de salida de Visual Studio.The cluster creation status is displayed in the Visual Studio output window.

Una vez que la aplicación Voting se implemente en el clúster local de Service Fabric, la aplicación web se abrirá automáticamente en una pestaña del explorador y tendrá un aspecto similar al siguiente:After the Voting application has been deployed to your local Service Fabric cluster, your web app will open in a browser tab automatically and should look like this:

Front-end de ASP.NET Core

Para detener la depuración de la aplicación, vuelva a Visual Studio y presione Mayús+F5.To stop debugging the application, go back to Visual Studio and press Shift+F5.

Incorporación de un servicio back-end con estado a la aplicaciónAdd a stateful back-end service to your application

Ahora que se ejecuta un servicio de ASP.NET Web API en la aplicación, agreguemos un servicio de confianza con estado para almacenar datos en la aplicación.Now that an ASP.NET Web API service is running in the application, go ahead and add a stateful reliable service to store some data in the application.

Service Fabric permite almacenar de forma coherente y confiable su estado justo dentro de su servicio mediante Reliable Collections.Service Fabric allows you to consistently and reliably store your data right inside your service by using reliable collections. Las colecciones de confianza son un conjunto de clases de colecciones de alta disponibilidad y confiabilidad que le resultará familiar a cualquiera que haya usado colecciones de C#.Reliable collections are a set of highly available and reliable collection classes that are familiar to anyone who has used C# collections.

En este tutorial creará un servicio, que almacena un valor de contador en una colección de confianza.In this tutorial, you create a service which stores a counter value in a reliable collection.

  1. En el Explorador de soluciones, haga clic con el botón derecho en Servicios, en el proyecto de aplicación Voting y seleccione Agregar > Nuevo servicio de Service Fabric… .In Solution Explorer, right-click Services within the Voting application project and choose Add -> New Service Fabric Service....

  2. En el cuadro de diálogo Nuevo servicio de Service Fabric, elija ASP.NET Core con estado, asigne el nombre de VotingData al servicio y presione Aceptar.In the New Service Fabric Service dialog, choose Stateful ASP.NET Core, name the service VotingData, then press OK.

    Una vez creado el proyecto de servicio, tendrá dos servicios en la aplicación.Once your service project is created, you have two services in your application. Mientras continúa la creación de la aplicación, puede agregar más servicios de la misma forma.As you continue to build your application, you can add more services in the same way. Cada uno puede tener versiones y actualizaciones independientes.Each can be independently versioned and upgraded.

  3. En la página siguiente se proporciona un conjunto de plantillas de proyecto ASP.NET Core.The next page provides a set of ASP.NET Core project templates. Para este tutorial, elija API.For this tutorial, choose API.

    Visual Studio crea el proyecto de servicio VotingData y lo muestra en el Explorador de soluciones.Visual Studio creates the VotingData service project and displays it in Solution Explorer.

    Explorador de soluciones

Incorporación del archivo VoteDataController.csAdd the VoteDataController.cs file

En el proyecto VotingData haga clic con el botón derecho en la carpeta Controladores y seleccione Agregar->Nuevo elemento->Clase.In the VotingData project, right-click on the Controllers folder, then select Add->New item->Class. Asigne al archivo el nombre VoteDataController.cs y haga clic en Agregar.Name the file VoteDataController.cs and click Add. Reemplace el contenido del archivo por el siguiente y guarde los cambios.Replace the file contents with the following, then save your changes.

namespace VotingData.Controllers
{
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.ServiceFabric.Data;
    using Microsoft.ServiceFabric.Data.Collections;

    [Route("api/[controller]")]
    public class VoteDataController : Controller
    {
        private readonly IReliableStateManager stateManager;

        public VoteDataController(IReliableStateManager stateManager)
        {
            this.stateManager = stateManager;
        }

        // GET api/VoteData
        [HttpGet]
        public async Task<IActionResult> Get()
        {
            CancellationToken ct = new CancellationToken();

            IReliableDictionary<string, int> votesDictionary = await this.stateManager.GetOrAddAsync<IReliableDictionary<string, int>>("counts");

            using (ITransaction tx = this.stateManager.CreateTransaction())
            {
                Microsoft.ServiceFabric.Data.IAsyncEnumerable<KeyValuePair<string, int>> list = await votesDictionary.CreateEnumerableAsync(tx);

                Microsoft.ServiceFabric.Data.IAsyncEnumerator<KeyValuePair<string, int>> enumerator = list.GetAsyncEnumerator();

                List<KeyValuePair<string, int>> result = new List<KeyValuePair<string, int>>();

                while (await enumerator.MoveNextAsync(ct))
                {
                    result.Add(enumerator.Current);
                }

                return this.Json(result);
            }
        }

        // PUT api/VoteData/name
        [HttpPut("{name}")]
        public async Task<IActionResult> Put(string name)
        {
            IReliableDictionary<string, int> votesDictionary = await this.stateManager.GetOrAddAsync<IReliableDictionary<string, int>>("counts");

            using (ITransaction tx = this.stateManager.CreateTransaction())
            {
                await votesDictionary.AddOrUpdateAsync(tx, name, 1, (key, oldvalue) => oldvalue + 1);
                await tx.CommitAsync();
            }

            return new OkResult();
        }

        // DELETE api/VoteData/name
        [HttpDelete("{name}")]
        public async Task<IActionResult> Delete(string name)
        {
            IReliableDictionary<string, int> votesDictionary = await this.stateManager.GetOrAddAsync<IReliableDictionary<string, int>>("counts");

            using (ITransaction tx = this.stateManager.CreateTransaction())
            {
                if (await votesDictionary.ContainsKeyAsync(tx, name))
                {
                    await votesDictionary.TryRemoveAsync(tx, name);
                    await tx.CommitAsync();
                    return new OkResult();
                }
                else
                {
                    return new NotFoundResult();
                }
            }
        }
    }
}

Conexión de los serviciosConnect the services

En este paso se conectan los dos servicios y se hace que la aplicación web de front-end obtenga y configure la información de votación del servicio back-end.In this next step, connect the two services and make the front-end Web application get and set voting information from the back-end service.

Service Fabric proporciona una flexibilidad completa en el modo de comunicación con los servicios confiables.Service Fabric provides complete flexibility in how you communicate with reliable services. Dentro de una única aplicación, es posible que tenga servicios que sean accesibles a través de TCP.Within a single application, you might have services that are accessible via TCP. Puede haber también otros servicios que podrían estar accesibles a través de una API de REST de HTTP e incluso otros servicios que pueden estar accesibles a través de sockets web.Other services that might be accessible via an HTTP REST API and still other services could be accessible via web sockets. Para más información sobre las opciones disponibles y sus inconvenientes, consulte Conexión y comunicación con servicios en Service Fabric.For background on the options available and the tradeoffs involved, see Communicating with services.

En este tutorial se usa la Web API de ASP.NET Core y el proxy inverso de Service Fabric para que el servicio web VotingWeb de front-end pueda comunicarse con el servicio VotingData de back-end.This tutorial uses ASP.NET Core Web API and the Service Fabric reverse proxy so the VotingWeb front-end web service can communicate with the back-end VotingData service. El proxy inverso está configurado de manera predeterminada para usar el puerto 19081 y debería funcionar para este tutorial.The reverse proxy is configured by default to use port 19081 and should work for this tutorial. El puerto del proxy inverso se establece en la plantilla de Azure Resource Manager utilizada para configurar el clúster.The reverse proxy port is set in the Azure Resource Manager template used to set up the cluster. Para ver qué puerto se utiliza, busque en la plantilla de clúster en el recurso Microsoft.ServiceFabric/clusters:To find which port is used, look in the cluster template in the Microsoft.ServiceFabric/clusters resource:

"nodeTypes": [
          {
            ...
            "httpGatewayEndpointPort": "[variables('nt0fabricHttpGatewayPort')]",
            "isPrimary": true,
            "vmInstanceCount": "[parameters('nt0InstanceCount')]",
            "reverseProxyEndpointPort": "[parameters('SFReverseProxyPort')]"
          }
        ],

Para encontrar el puerto del proxy inverso que se usa en el clúster de desarrollo local, consulte el elemento HttpApplicationGatewayEndpoint en el manifiesto del clúster de Service Fabric local:To find the reverse proxy port used in your local development cluster, view the HttpApplicationGatewayEndpoint element in the local Service Fabric cluster manifest:

  1. Abra un ventana del explorador y vaya a http://localhost:19080 para abrir la herramienta Service Fabric Explorer.Open a browser window and navigate to http://localhost:19080 to open the Service Fabric Explorer tool.
  2. Seleccione Cluster -> Manifest (Clúster -> Manifiesto).Select Cluster -> Manifest.
  3. Anote el puerto del elemento HttpApplicationGatewayEndpoint.Make a note of the HttpApplicationGatewayEndpoint element port. El valor predeterminado debería ser 19081.By default this should be 19081. Si no es 19081, deberá cambiar el puerto del método GetProxyAddress del siguiente código VotesController.cs.If it is not 19081, you will need to change the port in the GetProxyAddress method of the following VotesController.cs code.

Actualización del archivo VotesController.csUpdate the VotesController.cs file

En el proyecto VotingWeb, abra el archivo Controllers/VotesController.cs.In the VotingWeb project, open the Controllers/VotesController.cs file. Reemplace el contenido de la definición de clase VotesController por el siguiente y guarde los cambios.Replace the VotesController class definition contents with the following, then save your changes. Si el puerto de proxy inverso que detectó en el paso anterior no es 19081, cambie el puerto que se usa en el método GetProxyAddress de 19081 al puerto que detectó.If the reverse proxy port you discovered in the pervious step is not 19081, change the port used in the GetProxyAddress method from 19081 to the port that you discovered.

public class VotesController : Controller
{
    private readonly HttpClient httpClient;
    private readonly FabricClient fabricClient;
    private readonly StatelessServiceContext serviceContext;

    public VotesController(HttpClient httpClient, StatelessServiceContext context, FabricClient fabricClient)
    {
        this.fabricClient = fabricClient;
        this.httpClient = httpClient;
        this.serviceContext = context;
    }

    // GET: api/Votes
    [HttpGet("")]
    public async Task<IActionResult> Get()
    {
        Uri serviceName = VotingWeb.GetVotingDataServiceName(this.serviceContext);
        Uri proxyAddress = this.GetProxyAddress(serviceName);

        ServicePartitionList partitions = await this.fabricClient.QueryManager.GetPartitionListAsync(serviceName);

        List<KeyValuePair<string, int>> result = new List<KeyValuePair<string, int>>();

        foreach (Partition partition in partitions)
        {
            string proxyUrl =
                $"{proxyAddress}/api/VoteData?PartitionKey={((Int64RangePartitionInformation) partition.PartitionInformation).LowKey}&PartitionKind=Int64Range";

            using (HttpResponseMessage response = await this.httpClient.GetAsync(proxyUrl))
            {
                if (response.StatusCode != System.Net.HttpStatusCode.OK)
                {
                    continue;
                }

                result.AddRange(JsonConvert.DeserializeObject<List<KeyValuePair<string, int>>>(await response.Content.ReadAsStringAsync()));
            }
        }

        return this.Json(result);
    }

    // PUT: api/Votes/name
    [HttpPut("{name}")]
    public async Task<IActionResult> Put(string name)
    {
        Uri serviceName = VotingWeb.GetVotingDataServiceName(this.serviceContext);
        Uri proxyAddress = this.GetProxyAddress(serviceName);
        long partitionKey = this.GetPartitionKey(name);
        string proxyUrl = $"{proxyAddress}/api/VoteData/{name}?PartitionKey={partitionKey}&PartitionKind=Int64Range";

        StringContent putContent = new StringContent($"{{ 'name' : '{name}' }}", Encoding.UTF8, "application/json");
        putContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

        using (HttpResponseMessage response = await this.httpClient.PutAsync(proxyUrl, putContent))
        {
            return new ContentResult()
            {
                StatusCode = (int) response.StatusCode,
                Content = await response.Content.ReadAsStringAsync()
            };
        }
    }

    // DELETE: api/Votes/name
    [HttpDelete("{name}")]
    public async Task<IActionResult> Delete(string name)
    {
        Uri serviceName = VotingWeb.GetVotingDataServiceName(this.serviceContext);
        Uri proxyAddress = this.GetProxyAddress(serviceName);
        long partitionKey = this.GetPartitionKey(name);
        string proxyUrl = $"{proxyAddress}/api/VoteData/{name}?PartitionKey={partitionKey}&PartitionKind=Int64Range";

        using (HttpResponseMessage response = await this.httpClient.DeleteAsync(proxyUrl))
        {
            if (response.StatusCode != System.Net.HttpStatusCode.OK)
            {
                return this.StatusCode((int) response.StatusCode);
            }
        }

        return new OkResult();
    }


    /// <summary>
    /// Constructs a reverse proxy URL for a given service.
    /// Example: http://localhost:19081/VotingApplication/VotingData/
    /// </summary>
    /// <param name="serviceName"></param>
    /// <returns></returns>
    private Uri GetProxyAddress(Uri serviceName)
    {
        return new Uri($"http://localhost:19081{serviceName.AbsolutePath}");
    }

    /// <summary>
    /// Creates a partition key from the given name.
    /// Uses the zero-based numeric position in the alphabet of the first letter of the name (0-25).
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    private long GetPartitionKey(string name)
    {
        return Char.ToUpper(name.First()) - 'A';
    }
}

Tutorial de la aplicación de ejemplo de votaciónWalk through the voting sample application

La aplicación de votación consta de dos servicios:The voting application consists of two services:

  • Servicio front-end web (VotingWeb): front-end web de ASP.NET Core, que ofrece servicio a la página web y expone API web para comunicarse con el servicio back-end.Web front-end service (VotingWeb)- An ASP.NET Core web front-end service, which serves the web page and exposes web APIs to communicate with the backend service.
  • Servicio back-end (VotingData): servicio web de ASP.NET Core, que expone una API para almacenar los resultados de una votación en un diccionario de confianza que se conserva en el disco.Back-end service (VotingData)- An ASP.NET Core web service, which exposes an API to store the vote results in a reliable dictionary persisted on disk.

Diagrama de la aplicación

Al votar en la aplicación, se producen los eventos siguientes:When you vote in the application the following events occur:

  1. JavaScript envía la solicitud de votación a la API web del servicio front-end web como una solicitud HTTP PUT.A JavaScript sends the vote request to the web API in the web front-end service as an HTTP PUT request.

  2. El servicio front-end web usa un proxy para localizar y reenviar una solicitud HTTP PUT al servicio back-end.The web front-end service uses a proxy to locate and forward an HTTP PUT request to the back-end service.

  3. El servicio back-end recibe la solicitud entrante y almacena el resultado actualizado en un diccionario de confianza, que se replica en varios nodos del clúster y se conserva en el disco.The back-end service takes the incoming request, and stores the updated result in a reliable dictionary, which gets replicated to multiple nodes within the cluster and persisted on disk. Todos los datos de la aplicación se almacenan en el clúster, por lo que no se necesita una base de datos.All the application's data is stored in the cluster, so no database is needed.

Depurar en Visual StudioDebug in Visual Studio

Cuando se depura una aplicación en Visual Studio, se usa un clúster de desarrollo de Service Fabric local.When debugging application in Visual Studio, you are using a local Service Fabric development cluster. Tiene la opción de ajustar la experiencia de depuración a su escenario.You have the option to adjust your debugging experience to your scenario. En esta aplicación, los datos se almacenan en el servicio back-end mediante un diccionario de confianza.In this application, store data in the back-end service using a reliable dictionary. Visual Studio quita la aplicación de forma predeterminada cuando se detiene el depurador.Visual Studio removes the application per default when you stop the debugger. Cuando se quita la aplicación, los datos del servicio back-end también se quitan.Removing the application causes the data in the back-end service to also be removed. Para conservar los datos entre sesiones de depuración, puede cambiar el modo de depuración de la aplicación como una propiedad del proyecto Voting en Visual Studio.To persist the data between debugging sessions, you can change the Application Debug Mode as a property on the Voting project in Visual Studio.

Para ver lo que ocurre en el código, siga estos pasos:To look at what happens in the code, complete the following steps:

  1. Abra el archivo VotingWeb\VotesController.cs y establezca un punto de interrupción en el método Put (línea 72) de la API web.Open the VotingWeb\VotesController.cs file and set a breakpoint in the web API's Put method (line 72).

  2. Abra el archivo VotingData\VoteDataController.cs y establezca un punto de interrupción en el método Put (línea 54) de la API web.Open the VotingData\VoteDataController.cs file and set a breakpoint in this web API's Put method (line 54).

  3. Presione F5 para iniciar la aplicación en modo de depuración.Press F5 to start the application in debug mode.

  4. Vuelva al explorador y haga clic en una opción de votación o agregue una nueva opción de votación.Go back to the browser and click a voting option or add a new voting option. Alcanzará el primer punto de interrupción del controlador de API del front-end web.You hit the first breakpoint in the web front-end's api controller.

    1. Aquí es donde el código JavaScript del explorador envía una solicitud al controlador de API web del servicio front-end.This is where the JavaScript in the browser sends a request to the web API controller in the front-end service.

      Incorporación del servicio front-end de voto

    2. Primero, construya la dirección URL para el valor de ReverseProxy del servicio back-end (1) .First construct the URL to the ReverseProxy for the back-end service (1).

    3. A continuación, envíe la solicitud PUT de HTTP a ReverseProxy (2) .Then send the HTTP PUT Request to the ReverseProxy (2).

    4. Por último, se devuelve la respuesta del servicio back-end al cliente (3) .Finally the return the response from the back-end service to the client (3).

  5. Presione F5 para continuar.Press F5 to continue.

    1. Ya está en el punto de interrupción del servicio back-end.You are now at the break point in the back-end service.

      Incorporación del servicio back-end de voto

    2. En la primera línea del método (1) se usa StateManager para obtener o agregar un diccionario de confianza denominado counts.In the first line in the method (1) use the StateManager to get or add a reliable dictionary called counts.

    3. Todas las interacciones con valores de un diccionario de confianza requieren una transacción. Esta instrucción using (2) crea dicha transacción.All interactions with values in a reliable dictionary require a transaction, this using statement (2) creates that transaction.

    4. Después, en la transacción, se actualiza el valor de la tecla correspondiente para la opción de votación y se confirma la operación (3) .In the transaction, update the value of the relevant key for the voting option and commits the operation (3). Una vez que se devuelve el método Commit, los datos se actualizan en el diccionario y se replican en otros nodos del clúster.Once the commit method returns, the data is updated in the dictionary and replicated to other nodes in the cluster. Los datos ahora están almacenados de forma segura en el clúster y el servicio back-end puede conmutar por error a otros nodos, mientras sigue teniendo los datos disponibles.The data is now safely stored in the cluster, and the back-end service can fail over to other nodes, still having the data available.

  6. Presione F5 para continuar.Press F5 to continue.

Para detener la sesión de depuración, presione Mayús+F5.To stop the debugging session, press Shift+F5.

Pasos siguientesNext steps

En esta parte del tutorial, ha aprendido a:In this part of the tutorial, you learned how to:

  • Crear un servicio de ASP.NET Core Web API como un servicio de confianza con estadoCreate an ASP.NET Core Web API service as a stateful reliable service
  • Crear un servicio de ASP.NET Core Web Application como un servicio de confianza sin estadoCreate an ASP.NET Core Web Application service as a stateless web service
  • Usar el proxy inverso para comunicarse con el servicio con estadoUse the reverse proxy to communicate with the stateful service

Avance hasta el siguiente tutorial:Advance to the next tutorial: