Skip to content
Demis Bellot edited this page May 5, 2016 · 27 revisions

If you want to support SOAP, you have to ensure you adhere to some additional constraints where each method needs to be defined with the Any() endpoint and each DTO needs to be decorated with [DataContract] and [DataMember] attributes so their metadata is generated in your Services XSD and WSDL metadata.

SOAP + REST

SOAP only supports a single POST request but REST services also make use of GET, PUT, DELETE, etc requests, which aren't used in SOAP. So if you want to support SOAP and REST, you need to create one service for each operation which is also the recommended API structure for creating message-based Services. The difference to be able to support SOAP is that Service implementations need to be defined with Any(), e.g:

//Request DTO - Add DataMember attribute for all properties.
[DataContract]
[Route("/customers", "GET")]
[Route("/customers/{Id}", "GET")]
public class GetCustomers {...}

[DataContract]
[Route("/customers", "POST")]
public class AddCustomer {...}

[DataContract]
[Route("/customers/{Id}", "PUT")]
public class UpdateCustomer {...}

[DataContract]
[Route("/customers/{Id}", "DELETE")]
public class DeleteCustomer {...}

//Service
public class CustomersService : Service 
{
   public object Any(GetCustomers request){...}
   public object Any(AddCustomer request){...}
   public object Any(UpdateCustomer request){...}
   public object Any(DeleteCustomer  request){...}
}

Using Any will allow the Service to be executed on each HTTP Verb which is required for SOAP since all SOAP Requests are made with a HTTP POST Request wrapped inside a SOAP message and sent to the fixed /soap11 or /soap12 endpoints. You also want to make sure that all DTO models have [DataContract] attribute (and [DataMember] attribute for all properties) otherwise the XSD-schema embedded within the WSDL will be partially incomplete.

REST-ful registration of multiple services

The Custom [Route] definitions are used to control how you want services exposed in REST APIs which all logically appear to exposed them under a single REST-ful resource, i.e:

GET /customers    - Get All Customers
GET /customers/1  - Get Customer #1
POST /customers   - Add New Customer
PUT /customers/1  - Update Customer #1 
DELETE /customers - Delete Customer #1

This Web Service now supports both REST and SOAP with REST API's using the above custom routes and SOAP requests posting WSDL Requests to their fixed /soap11 or /soap12 endpoints.

SOAP Limitations

SOAP expects that each request always returns the same response DTO. So you need to follow the Response DTO naming convention, otherwise ServiceStack won't be able to generate the WSDLs and the SOAP endpoint won't be able to work.

DTO Naming Conventions

Naming convention: {Request DTO Name} + Response

Example: Request DTO: DeleteCustomer --> Response DTO: DeleteCustomerResponse.

If you would leave the services as they are, the REST endpoint wouldn't exist. So you need to hook them all up on the same URL like that:

Single WSDL Namespace

If you happen to generate requests from the WSDLs with a tool like SoapUI you may end up with an incorrectly generated request like this:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" 
               xmlns:type="http://schemas.servicestack.net/types">
  <soap:Header/>
  <soap:Body>
    <type:Hello/>
  </soap:Body>
</soap:Envelope>

You can resolve this issue by adding the following line to your AssemblyInfo file

[assembly: ContractNamespace("http://schemas.servicestack.net/types", 
           ClrNamespace = "<YOUR NAMESPACE>")]

e.g:

[assembly: ContractNamespace("http://schemas.servicestack.net/types",
           ClrNamespace = "MyApp.ServiceModel")]
[assembly: ContractNamespace("http://schemas.servicestack.net/types",
           ClrNamespace = "MyApp.ServiceModel.Types")]

Rebuild and regenerate the request from the updated wsdl. You should get a correct request this time.

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" 
               xmlns:type="http://schemas.servicestack.net/types">
   <soap:Header/>
   <soap:Body>
      <type:Hello>
         <!--Optional:-->
         <type:Name>?</type:Name>
      </type:Hello>
   </soap:Body>
</soap:Envelope>

Changing the default namespace

A requirement with SOAP endpoints is for all DTO types to share the same single namespace which should match the Config.WsdlServiceNamespace if you want to change it from the default namespace: http://schemas.servicestack.net/types. E.g. You can change the default WSDL Namespace in your AppConfig with:

SetConfig(new HostConfig {
    WsdlServiceNamespace = "http://my.new.namespace.com/types",
});

This can easily be done by using the [assembly:ContractNamespace] attribute usually defined in the DTO project's AssemblyInfo.cs file, here is how this is done in the ServiceStack.Examples project:

[assembly: ContractNamespace("http://my.new.namespace.com/types",
           ClrNamespace = "ServiceStack.Examples.ServiceModel.Operations")]
[assembly: ContractNamespace("http://my.new.namespace.com/types",
           ClrNamespace = "ServiceStack.Examples.ServiceModel.Types")]

SOAP Exceptions

Exceptions in SOAP responses are returned with an 200 OK HTTP Status so they are deserialized as normal responses in code-generated SOAP clients. The original HTTP Status code is available in the X-Status HTTP Header or SOAP Response Header named X-Status. This is transparently converted into a typed WebServiceException when using ServiceStack's built-in Soap 1.1/1.2 generic Service Clients as seen in WebServicesTests.

To check if the response was an error in non ServiceStack SOAP clients, check the response.ResponseStatus.ErrorCode property for a non-null value.

Convert SOAP Exceptions to SOAP Faults

If preferred, you can also convert SOAP Exceptions into a SOAP Fault by adding a ServiceExceptionHandler, e.g:

ServiceExceptionHandlers.Add((req, request, ex) => {
    var requestMsg = req.GetItem("SoapMessage") as System.ServiceModel.Channels.Message;
    if (requestMsg != null)
    {
        var msgVersion = requestMsg.Version;
        using (var response = XmlWriter.Create(req.Response.OutputStream))
        {
            var message = System.ServiceModel.Channels.Message.CreateMessage(
                msgVersion, new FaultCode("Receiver"), ex.ToString(), null);
            message.WriteMessage(response);
        }
        req.Response.End();
    }
    return null;
});

Customize WSDL's and XSD's

There's finer-grain control available over which Operations and Types are exported in SOAP WSDL's and XSD's by overriding the new ExportSoapOperationTypes() and ExportSoapType() methods in your AppHost.

You can exclude specific Request DTO's from being emitted in WSDL's and XSD's with:

[Exclude(Feature.Soap)]
public class HiddenFromSoap { .. } 

Raw Access to WCF SOAP Message

IRequiresSoapMessage works similar to IRequiresRequestStream interface to tell ServiceStack to skip de-serialization of the request and instead pass the raw WCF Message to the Service instead for manual processing, e.g:

public class RawWcfMessage : IRequiresSoapMessage {
	public Message Message { get; set; }
}

public object Post(RawWcfMessage request) { 
	request.Message... //Raw WCF SOAP Message
}

Customize SOAP Response

You can override and customize how the SOAP Message Responses are written, here's a basic example:

public override WriteSoapMessage(Message message, Stream outputStream)
{
    using (var writer = XmlWriter.Create(outputStream, Config.XmlWriterSettings))
    {
        message.WriteMessage(writer);
    }
}

The default WriteSoapMessage implementation also raises a ServiceException and writes any returned response to a buffered Response Stream (if configured).

Visual Studios Add Service Reference

Since VS.NET's Add Service Reference is optimized for consuming .asmx or WCF RPC method calls it doesn't properly support multiple return values (e.g. when you also want a ResponseStatus property) where it will generate an ugly proxy API complete with out parameters.

If you want to ensure a pretty proxy is generated you should only have 1 first-level property which contains all the data you want to return.

Using XSD.exe

One way around it is to share your services DTO's and use any of the typed Generic Service Clients that are in-built into ServiceStack. Alternatively you can use the XSD.exe command-line utility to generate your types on the client and use those in the typed Service Clients.



  1. Getting Started

    1. Creating your first project
    2. Create Service from scratch
    3. Your first webservice explained
    4. Example Projects Overview
    5. Learning Resources
  2. Designing APIs

    1. ServiceStack API Design
    2. Designing a REST-ful service with ServiceStack
    3. Simple Customer REST Example
    4. How to design a Message-Based API
    5. Software complexity and role of DTOs
  3. Reference

    1. Order of Operations
    2. The IoC container
    3. Configuration and AppSettings
    4. Metadata page
    5. Rest, SOAP & default endpoints
    6. SOAP support
    7. Routing
    8. Service return types
    9. Customize HTTP Responses
    10. Customize JSON Responses
    11. Plugins
    12. Validation
    13. Error Handling
    14. Security
    15. Debugging
    16. JavaScript Client Library (ss-utils.js)
  4. Clients

    1. Overview
    2. C#/.NET client
      1. .NET Core Clients
    3. Add ServiceStack Reference
      1. C# Add Reference
      2. F# Add Reference
      3. VB.NET Add Reference
      4. Swift Add Reference
      5. Java Add Reference
    4. Silverlight client
    5. JavaScript client
      1. Add TypeScript Reference
    6. Dart Client
    7. MQ Clients
  5. Formats

    1. Overview
    2. JSON/JSV and XML
    3. HTML5 Report Format
    4. CSV Format
    5. MessagePack Format
    6. ProtoBuf Format
  6. View Engines 4. Razor & Markdown Razor

    1. Markdown Razor
  7. Hosts

    1. IIS
    2. Self-hosting
    3. Messaging
    4. Mono
  8. Security

    1. Authentication
    2. Sessions
    3. Restricting Services
    4. Encrypted Messaging
  9. Advanced

    1. Configuration options
    2. Access HTTP specific features in services
    3. Logging
    4. Serialization/deserialization
    5. Request/response filters
    6. Filter attributes
    7. Concurrency Model
    8. Built-in profiling
    9. Form Hijacking Prevention
    10. Auto-Mapping
    11. HTTP Utils
    12. Dump Utils
    13. Virtual File System
    14. Config API
    15. Physical Project Structure
    16. Modularizing Services
    17. MVC Integration
    18. ServiceStack Integration
    19. Embedded Native Desktop Apps
    20. Auto Batched Requests
    21. Versioning
    22. Multitenancy
  10. Caching

  11. Caching Providers

  12. HTTP Caching 1. CacheResponse Attribute 2. Cache Aware Clients

  13. Auto Query

  14. Overview

  15. Why Not OData

  16. AutoQuery RDBMS

  17. AutoQuery Data 1. AutoQuery Memory 2. AutoQuery Service 3. AutoQuery DynamoDB

  18. Server Events

    1. Overview
    2. JavaScript Client
    3. C# Server Events Client
    4. Redis Server Events
  19. Service Gateway

    1. Overview
    2. Service Discovery
  20. Encrypted Messaging

    1. Overview
    2. Encrypted Client
  21. Plugins

    1. Auto Query
    2. Server Sent Events
    3. Swagger API
    4. Postman
    5. Request logger
    6. Sitemaps
    7. Cancellable Requests
    8. CorsFeature
  22. Tests

    1. Testing
    2. HowTo write unit/integration tests
  23. ServiceStackVS

    1. Install ServiceStackVS
    2. Add ServiceStack Reference
    3. TypeScript React Template
    4. React, Redux Chat App
    5. AngularJS App Template
    6. React Desktop Apps
  24. Other Languages

    1. FSharp
      1. Add ServiceStack Reference
    2. VB.NET
      1. Add ServiceStack Reference
    3. Swift
    4. Swift Add Reference
    5. Java
      1. Add ServiceStack Reference
      2. Android Studio & IntelliJ
      3. Eclipse
  25. Amazon Web Services

  26. ServiceStack.Aws

  27. PocoDynamo

  28. AWS Live Demos

  29. Getting Started with AWS

  30. Deployment

    1. Deploy Multiple Sites to single AWS Instance
      1. Simple Deployments to AWS with WebDeploy
    2. Advanced Deployments with OctopusDeploy
  31. Install 3rd Party Products

    1. Redis on Windows
    2. RabbitMQ on Windows
  32. Use Cases

    1. Single Page Apps
    2. HTML, CSS and JS Minifiers
    3. Azure
    4. Connecting to Azure Redis via SSL
    5. Logging
    6. Bundling and Minification
    7. NHibernate
  33. Performance

    1. Real world performance
  34. Other Products

    1. ServiceStack.Redis
    2. ServiceStack.OrmLite
    3. ServiceStack.Text
  35. Future

    1. Roadmap
Clone this wiki locally