Web Services

Gazel provides you convenience for building SOAP web service. You can create web services without making any XML configuration. In this section, you will find ways on how to make use of this feature.

A Quick Guide to Create a Web Service

This is a quick walkthrough of getting your first web service.

Each service in Gazel consists of following parts:

  • Request Message and Response Message
  • Service Implementation
  • Service Configuration
  • Service Host

This is core philosophy in Gazel.

Once you've created business services, you can expose them as web services using below instructions. You can read a detailed explanation of business services from here.

Follow the steps below to create a web service.

  1. Create your service interface.

    namespace CompanyName.Module.ProductName.Service { //Gazel scans interfaces under the `Service` namespace. public interface IHelloService { string GetHello(string request); } }
  2. Implement your service interface in corresponding class.

    public class HelloManager : IHelloService { private readonly IModuleContext context; protected HelloManager() { } public HelloManager(IModuleContext context) { this.context = context; } protected internal string GetHello(string request) { return request; } #region Web Service Mapping string IHelloService.GetHello(string request)=> GetHello(request); #endregion }
  3. Configure setup to tell Gazel where to find your web services.

    public class HelloService : ICodingStyleConfiguration { public void Configure(ConventionBasedCodingStyle codingStyle, IKernel kernel) { codingStyle.AddTypes( v => v.WebService("Hello", s => s .Methods.Add(o => o.Proxy<IHelloService>("GetHello").TargetBySingleton(kernel)) ) ); } }

After you complete this step, using a web service application host, you can expose your web services.

Let's look at the details now.

Request and Response Message

SOAP web service generated by Gazel expects payload as following on a request. This is just a sample for the GetHello method.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/" xmlns:mul="http://schemas.datacontract.org/2004/07/Gazel.WebService.Security"> <soapenv:Header/> <soapenv:Body> <tem:GetHello> <tem:appToken>6fdfe5b0efcc4870820fbae5ea07d068</tem:appToken> <tem:languageCode>en-US</tem:languageCode> <tem:request>Hello world</tem:request> </tem:GetHello> </soapenv:Body> </soapenv:Envelope>

As you can see, the payload of a SOAP request is an XML document that contains the parameter values of the method. This service method has string type as the recipient of this method call. If web service has a request DTO, naming convention for request object is [RequestDTOName]InputData. If authentication is enabled on AppHost, Gazel automatically adds the appToken parameter. You can read an explanation of authentication from here. It also contains a languageCode parameter generated by Gazel. It takes a culture name.

SOAP Web Service will respond with the following xml result, based on corresponding successfully invoked for GetHello method.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <GetHelloResponse xmlns="http://tempuri.org/"> <GetHelloResult xmlns:a="http://schemas.datacontract.org/2004/07/Gazel.WebService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <a:Result>Hello world</a:Result> <a:ResultCode>0</a:ResultCode> <a:ResultMessage/> </GetHelloResult> </GetHelloResponse> </s:Body> </s:Envelope>

As above shown, Gazel returns a new instance of structured response schema. It always contains three property.

  • ResultCode
  • ResultMessage
  • Result

Naming convention for response object is [ResponseDTOName]DataResult. Similarly, naming convention for Result parameter is [ResponseDTOName]Data. If service returns a successful response, ResultCode returns 0.

Error handling works naturally in Gazel where you can simply throw an exception, e.g:

... protected internal HelloInfo GetHello(Hello request) { if(request.Name.IsNullOrEmpty()) { throw new ArgumentException("Request is required."); } return new HelloInfo(request.Name); } ...

This will result in an error thrown on the client. It will automatically populate the ResultCode property with a structured ResultMessage.

Gazel uses the data transfer objects (DTO) design pattern for the request and response messages. You can read a detailed explanation the about DTOs from here.

Service Implementation

Gazel encourages you to think of SOAP web services as normal business service method calls. It provides published as a web service your business services in the business logic layer.

Service Interfaces

Service interface provides a service gateway for your web service methods. This interface should be under the Service namespace in your business logic project.

As you have seen, ICompanyService is defined in the Service namespace.

//Gazel scans interfaces under Service namespace. namespace CompanyName.Module.ProductName.Service { public interface ICompanyService { BranchInfo GetBranchById(int id); List<BranchInfo> GetBranchesByIds(List<int> ids); Branch CreateBranch(NewBranch request); } }

You can read a detailed explanation of Service Interface from here

Data Interfaces

Gazel web services let you return any kind of persistent class. To make, you need to implement a data interface in a persistent class. It holds the properties to be mapped with persistence class.

public interface IBranchDetail { int Id { get; } string Name { get; } string Address { get; } }
public class Branch : IBranchDetail { private readonly IRepository<Branch> repository; protected Branch() { } public Branch(IRepository<Branch> repository) { this.repository = repository; } public virtual int Id { get; protected set; } public virtual string Name { get; protected set; } public virtual string Address { get; protected set; } #region Web Service Mapping int IBranchDetail.Id => Id; string IBranchDetail.Name => Name; string IBranchDetail.Address => Address; #endregion }

Service Configuration

This step is to configure a setup to tell Gazel where to find your web services. To do that, you should add a configuration project to your application. You can read details about the configuration project from here.

Services are configured in a class that implements the ICodingStyleConfiguration marker interface. You can create multiple web service packages under the same configuration project.

public class CompanyService : ICodingStyleConfiguration { public void Configure(ConventionBasedCodingStyle codingStyle, IKernel kernel) { codingStyle.AddTypes( v => v.WebService("Company", s => s .Methods.Add(o => o.Proxy<ICompanyService>("GetBranchById").TargetBySingleton(kernel)) .Methods.Add(o => o.Proxy<ICompanyService>("GetBranchesByIds").TargetBySingleton(kernel)) .Methods.Add(o => o.Proxy<ICompanyService>("CreateBranch").TargetBySingleton(kernel)) ) ); } }

In the above example, we have created a web service package named CompanyService. Naming convention for endpoint address path is [ServiceName]Service. Service name can be specified in a configuration using the first parameter of WebService.

Test your first Web Service

To do that, you should add web service application host project to your application. It is starting point in your application. You can read details about the application host project from here.

If everything is configured correctly you can go to http://localhost:63048/wcf/CompanyService to see a list of your web services.

You can change endpoint address base path using wcfUrlBase parameter from Global.asax.

public class Global : WebServiceServiceClientApplication { protected override WebServiceConfiguration WebService(WebServiceConfigurer configure) { return configure.Wcf(wcfUrlBase: FromConfig("WebService.WcfUrlBase", http://localhost:63048/wcf)); } }

By default, Gazel generates data contract namespace named 'Gazel' in the WSDL. You can change the WSDL data contract namespace from Global.asax in your host project. This can easily be done using the resultObjectNamespace parameter.

public class Global : WebServiceServiceClientApplication { protected override WebServiceConfiguration WebService(WebServiceConfigurer configure) { return configure.Wcf(wcfUrlBase: FromConfig<string>("WebService.WcfUrlBase"), resultObjectNamespace: FromConfig<string>("WebService.ResultObjectNamespace")); } }