segunda-feira, 2 de dezembro de 2013

Disponibilizar Serviços WCF em JSON

Vimos, no artigo anterior, como expor os dados da nossa base de dados (BD) como coleções de instâncias através do protocolo OData, recorrendo a um projeto WCF Service Application e ao Entity Framework.

O OData baseia-se em protocolos standard para oferecer serviços de dados interoperáveis com aplicações que não usam a framework .Net. Como é baseado em URIs standard para aceder aos dados, podemos aceder e alterar dados usando a semantica REST (Representational State Transfer), como sejam os verbos GET, PUT, POST e DELETE. Isto possibilita a utilização destes serviços a partir de qualquer cliente que possa aceder a dados transmitidos sobre protocolo HTTP standard.

Um serviço OData pode representar dados no formato Atom, JSON (JavaScript Object Notation) e XML, sendo que Atom é o formato por omissão.
O formato dos dados é especificado no header do pedido HTTP.

É bastante fácil acrescentar serviços customizados a serviços de dados WCF, através de Service Operations.
Service Operations são métodos disponibilizados num servidor e que podem ser acedidos por URIs da mesma forma que os recursos de dados vistos anteriormente.
Service Operations podem também usar expressões de query para filtrar, ordenar e paginar os dados resultado da invocação de uma operação.

Vamos, neste artigo, ver como podemos disponibilizar Service Operations, em formato OData, SOAP e JSON.


Começamos por abrir o projeto criado no artigo anterior, e criar uma Domain Service Class:


Selecionamos as caixas de Enable client access que irá possibilitar o acesso a aplicações cliente, e de Expose OData endpoint, que irá criar um ponto de acesso OData para os Domain services.

Deve-nos aparecer a classe DBcontext (do Entity Framework) e selecionando-a aparecem as entidades do modelo de domínio. Selecionamos as entidades pretendidas e assinalamos a caixa relativa a enable editing, para que sejam gerados serviços de CRUD e não apenas de consulta.


 Se não aparecer a classe de contexto, então devemos proceder como se segue:
Para utilizar o modelo Entity Framework model com serviços WCF RIA Services, é preciso converte-lo para um modelo baseado em 'ObjectContext'. Isto pode ser feito através dos seguintes passos:
  1. Abrir o entity model no designer (diagrama)
  2. (Se necessário clicar no espaço em branco do diagrama para garantir que nenhum objeto dentro do modelo está selecionado)
  3. Na janela de propriedades do modelo de entidades (diagrama) alterar a "Code Generation Strategy" de "None" para "Default"
  4. Apagar os dois ficheiros ".tt" adjacentes ao modelo, assumindo que estes não foram modificados pelo utilizador, correspondendo às versões originalmente geradas. Se estes ficheiros foram modificados, então as alterações perder-se-ão.
  5. Fazer o "Rebuild" do projeto.

Após estes passos será possível selecionar a classe de Contexto do modelo de entidades do Entity Framework na lista de 'Available context classes'. O efeito co-lateral de estas alterações doi ter convertido o entity model de um modelo Entity Framework baseado em DbContext para um modelo baseado em ObjectContext.
(fonte: http://support.microsoft.com/kb/2745294)

A criação da Domain Service Class (RentACarDomainService), produz um ficheiro RentACarDomainService.cs:


...
    [EnableClientAccess()]
    public class RentACarDomainService : LinqToEntitiesDomainService<Rent_a_CarEntities>
    {

        // TODO:
        // Consider constraining the results of your query method.  If you need additional input you can
        // add parameters to this method or create additional query methods with different names.
        // To support paging you will need to add ordering to the 'Cars' query.
        [Query(IsDefault = true)]
        public IQueryable<Car> GetCars()
        {
            return this.ObjectContext.Cars;
        }

        public void InsertCar(Car car)
        {
            if ((car.EntityState != EntityState.Detached))
            {
                this.ObjectContext.ObjectStateManager.ChangeObjectState(car, EntityState.Added);
            }
            else
            {
                this.ObjectContext.Cars.AddObject(car);
            }
        }

...


São também criadas algumas linhas no ficheiro Web.config:

...
  <configSections>
    ...
    <sectionGroup name="system.serviceModel">
      <section name="domainServices" type="System.ServiceModel.DomainServices.Hosting.DomainServicesSection, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" allowDefinition="MachineToApplication" requirePermission="false" />
    </sectionGroup>
  </configSections>

...

<system.serviceModel>
    <domainServices>
      <endpoints>
        <add name="OData" type="System.ServiceModel.DomainServices.Hosting.ODataEndpointFactory, System.ServiceModel.DomainServices.Hosting.OData, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  </endpoints>
    </domainServices>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the value below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" />
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>

...

De notar o elemento sectionGroup, com a definição de uma secção domainServices  e do elemento domainServices, onde passou a estar definido um endpoint OData para os Domain Services criados.

Os domain services podem ser acedidos no endpoint OData através do endereço:

http://localhost:25408/WcfRentACarService/WcfRentACarService-RentACarDomainService.svc/OData/

onde, localhost:25408 correspondem ao nome do servidor e porta onde estão disponibilizados os serviços,
WcfRentACarService é o nome do projeto,
WcfRentACarService-RentACarDomainService.svc fornece o acesso aos serviços definidos na domain class WcfRentACarService.RentACarDomainService e por fim /OData/ é o endpoint.

O endereço http://localhost:25408/WcfRentACarService/WcfRentACarService-RentACarDomainService.svc/OData/CarSet
devolve-nos todos os carros (cars) da tabela respetiva na BD.

Na versão atual, o OData apenas dá acesso aos métodos de Query do domain service.

Para acedermos aos restantes métodos / serviços temos que aceder por SOAP ou JSON.
Para isso, precisamos de criar endpoints SOAP e JSON.

Precisamos de ter instalado o Package RIA Services SOAP and JSON Endpoints:



Ao instalar este package, o nosso projeto WCF Service Application passa a ter mais uma referência:


Para além disso, na secção endpoints do Web.config deverão ter aparecido mais dois endpoints, respetivamente SOAP e JSON:

<endpoints>
        <add name="OData" type="System.ServiceModel.DomainServices.Hosting.ODataEndpointFactory, System.ServiceModel.DomainServices.Hosting.OData, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        <add name="soap" type="Microsoft.ServiceModel.DomainServices.Hosting.SoapXmlEndpointFactory, Microsoft.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        <add name="json" type="Microsoft.ServiceModel.DomainServices.Hosting.JsonEndpointFactory, Microsoft.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</endpoints>

O acesso aos serviços por JSON faz-se por:

http://localhost:25408/WcfRentACarService/WcfRentACarService-RentACarDomainService.svc/json/GetCars

onde json é o endpoint e GetCars é o nome de um dos domain services disponibilizados.

No próximo artigo veremos como invocar estes serviços numa página HTML usando Javascript e/ou JQuery.

Referências:

Artigos relacionados:



Sem comentários:

Enviar um comentário