Expondo POCO como Web Services usando Spring Framework

Publicado por Ricardo Ushisima
23/12/2010
Categoria:
Tags: , , ,

Em implementações tradicionais de Web Services em .NET, os serviços são expostos por meio de arquivos asmx. Usando boas práticas de desenvolvimento, tais arquivos asmx não devem conter lógicas de negocio por se tratar de uma camada de “view”. Toda a lógica de negócio deve estar na camada abaixo, na camada de serviços. Usando essa abordagem, os arquivos asmx são meros intermediários (delegate), repassando as chamadas dos clientes web(service) para a camada de serviços.

Para eliminar esse intermediário e ainda possibilitar a adição de comportamentos extras em runtime, pode-ser expor os serviços que estão na camada de serviços, diretamente para a Web (na forma de Web Services) sem criar os arquivos asmx usando Spring Framework. Esse framework é mais conhecido por sua funcionalidade de IoC mas vou focar em outras partes do frameworks tais como AOP e Dynamic Proxies.

Nesse artigo vou me limitar a exposição do serviço sem o uso do arquivo asmx usando Spring mas em futuros artigos irei abordar a adição de comportamento aos serviços sem alterar a implementação dos serviços.

Para essa série de artigos, vou implementar um serviço de conversão de moedas exposto na web como um Web Service. Neste artigo tratarei exclusivamente da exposição do serviço na web.

Implementando o Serviço

A arquitetura básica do projeto de exemplo está representada no diagrama a seguir:

No exemplo, usarei uma Programação por Contrato, onde o contrato dos serviços fica no componente API e a implementação dos serviços fica no componente Core. O diagrama ainda mostra que o serviço esta sendo exposto como um Web Service.

No meu exemplo, o serviço de conversão de moedas teria a seguinte interface:

using System;

namespace SpringTutorial
{
    public interface ICurrencyService
    {
        decimal Convert(string from, string to, decimal value);
    }
}

A implementação do serviço seria a seguinte:

using System;
using System.Collections.Generic;

namespace SpringTutorial.Core
{
    public class CurrencyService : ICurrencyService
    {
        private readonly Dictionary<string, decimal> exchangeRates = new Dictionary<string, decimal>();

        public CurrencyService()
        {
            InitCurrencyExchangeRates();
        }

        private void InitCurrencyExchangeRates()
        {
            exchangeRates.Add("BRL", 1); //Base Rate
            exchangeRates.Add("USD", 1.68m);
            exchangeRates.Add("EUR", 2.24m);
        }

        public decimal Convert(string from, string to, decimal value)
        {
            decimal fromRate = exchangeRates[from];
            decimal toRate = exchangeRates[to];
            decimal baseValue = value * fromRate;
            return baseValue / toRate;
        }
    }
}

Note que a implementação do serviço não depende de qualquer classe de web service ou qualquer outra tecnologia de comunicação com o cliente. Essa implementação é um POCO (Plain Old CLR Object), que é um objeto simples sem referencias diretas a classes de frameworks tais como Web Services e WCF. Isso facilita, por exemplo, a aplicação de testes unitários usando NUnit, e a exposição desse objeto simples por meio de Web Services ou WCF. A implementação do serviço independe de como ele será exposto a seus clientes.

Expondo o Serviço

O serviço de conversão será exposto como Web Service usando Spring Framework. Para tal, iremos configurar um website com configurações do Spring.

A exposição de um POCO pelo Spring é feito por meio de um HTTP Handler customizado que irá processar a extensão asmx, ao invés de utilizar o processador padrão de asmx. Veja a seguir as configurações relevantes no arquivo Web.config.

<?xml version="1.0"?>
<configuration>
	<configSections>
		<!-- (...) -->
		<sectionGroup name="spring">
			<section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web"/>
		</sectionGroup>
	</configSections>
	<spring>
		<context>
			<resource uri="file://~/Spring.xml"/>
		</context>
	</spring>
	<appSettings/>
	<connectionStrings/>
	<system.web>
    <!-- (...) -->
		<httpHandlers>
      <remove verb="*" path="*.asmx"/>
      <add verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/>
      <!-- (...) -->
		</httpHandlers>
    <!-- (...) -->
	</system.web>
  <!-- (...) -->
</configuration>

No arquivo de configuração do Spring, iremos instanciar a instancia do serviço (currencyService) e expô-lo usando o WebServiceExporter do Spring Framework.

<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">
  <object id="currencyService" type="SpringTutorial.Core.CurrencyService, SpringTutorial.Core">
  </object>
  <object id="CurrencyServiceWS" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
    <property name="TargetName" value="currencyService"/>
    <property name="Namespace" value="http://zbra.com.br/springtutorial"/>
    <property name="Description" value="Currency Converter Web Service"/>
    <property name="MemberAttributes">
      <dictionary>
        <entry key="Convert">
          <object type="System.Web.Services.WebMethodAttribute, System.Web.Services">
            <property name="Description" value="Convert value from one currency to another"/>
          </object>
        </entry>
      </dictionary>
    </property>
  </object>
</objects>

O WebServiceHandlerFactory irá capturar as url’s terminadas em asmx e tentar achar um objeto no contexto do Spring com o nome especificado. Em nosso exemplo, se a url for CurrencyServiceWS.asmx, o WebServiceHandlerFactory irá buscar o objeto com identificador CurrencyServiceWS (sem a extensão)  e usá-lo como implementação do Web Service solicitado.

A implementação de serviços como POCO e expondo eles como Web Services usando o Spring Framework ajuda bastante aumentar a flexibilidade do sistema e aumentando a possibilidade de reuso desses serviços, já que sua implementação não depende da tecnologia de exposição do serviço, e ainda facilita a aplicação de frameworks de testes.

Referencias

Spring Framework

Design By Contract

NUnit





Desenvolvido por hacklab/ com WordPress